mirror of
https://github.com/xmonad/xmonad-contrib.git
synced 2025-05-19 03:20:21 -07:00
Compare commits
644 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
00832bf5e6 | ||
|
410b34f074 | ||
|
15dd45be0e | ||
|
f7451b9378 | ||
|
849208d1b8 | ||
|
4b86621051 | ||
|
18eb8aca94 | ||
|
a84f3e8540 | ||
|
bd81961a63 | ||
|
209839f3ca | ||
|
50e7dd4262 | ||
|
e2cdc0cc2c | ||
|
68da8c44ba | ||
|
0517c94960 | ||
|
b1bf33d6eb | ||
|
195a0ac3c0 | ||
|
b470de0d75 | ||
|
87585a6884 | ||
|
41f1d1434c | ||
|
ddcce31597 | ||
|
4496b4f2d5 | ||
|
5119626269 | ||
|
27c86d0dda | ||
|
58dbf59cab | ||
|
9d457a73ce | ||
|
f1f392cd01 | ||
|
6c1441d9db | ||
|
6a6d913dee | ||
|
4fc3642fa2 | ||
|
7614f94d92 | ||
|
c7061b0d73 | ||
|
6df1044265 | ||
|
619a347f3f | ||
|
2b11459496 | ||
|
beabe75dda | ||
|
0404372fd3 | ||
|
c0a5bc5f0f | ||
|
1f13bb2468 | ||
|
d4473946d4 | ||
|
55e1adde4c | ||
|
de01015af5 | ||
|
7f0f0ad498 | ||
|
195537e97e | ||
|
d9e54c1b96 | ||
|
b570ab1a74 | ||
|
0dc879698d | ||
|
fe826ca8db | ||
|
d19ea051d4 | ||
|
c5032a43fb | ||
|
61f8b4aa8e | ||
|
60fc830e2e | ||
|
f97ce867ac | ||
|
2f42d2e7b4 | ||
|
b454f1e0be | ||
|
5680205c72 | ||
|
1c5261d65a | ||
|
2c161ff670 | ||
|
2ec4bbc833 | ||
|
42340e0f76 | ||
|
4350936ba5 | ||
|
2973c283ae | ||
|
a96a2031f6 | ||
|
1e5fcb1216 | ||
|
6bc6bf8abd | ||
|
c98715623d | ||
|
b3c249434d | ||
|
d0d9d42761 | ||
|
e203096143 | ||
|
6811b9e296 | ||
|
f5f99c8abf | ||
|
d41f36fa5c | ||
|
e4e04aa017 | ||
|
ec5c751b35 | ||
|
eb7268451c | ||
|
0dcecb41c5 | ||
|
5d285ced1f | ||
|
91e59c3651 | ||
|
5f82296536 | ||
|
e8029cb51d | ||
|
1211a709dc | ||
|
66d334f4cd | ||
|
55f4c4ff1b | ||
|
bbbf5c3b44 | ||
|
b3ca4362af | ||
|
5325ca8902 | ||
|
0bc28ac473 | ||
|
077b4ff34b | ||
|
7109b0ce8f | ||
|
b57212cc18 | ||
|
da3e4bef33 | ||
|
d014d7ac84 | ||
|
a88b5aa58d | ||
|
58f956c29f | ||
|
7ac0f44db4 | ||
|
d6ea38e7be | ||
|
c8c5cc1838 | ||
|
7210251138 | ||
|
7dc39c7154 | ||
|
6fece17d3d | ||
|
8e34c2f745 | ||
|
02f124cf4b | ||
|
840ede1e9e | ||
|
8fb1973e05 | ||
|
abef527f73 | ||
|
cbdee7db6f | ||
|
0622ed11ed | ||
|
700507fcd0 | ||
|
ca5e70ffc4 | ||
|
67472aa307 | ||
|
c33efbbefd | ||
|
2b77997259 | ||
|
3839c8bce9 | ||
|
8efff53a06 | ||
|
cab938f07b | ||
|
f3d936ef97 | ||
|
aff212654d | ||
|
6e43da8598 | ||
|
fcd2f60226 | ||
|
51926854d9 | ||
|
0d2b68374c | ||
|
933cb57b90 | ||
|
ebe1b9b036 | ||
|
d691d25d1c | ||
|
8ac84079a2 | ||
|
7aa2ff6798 | ||
|
21a75bfeb4 | ||
|
78bad11578 | ||
|
51ee223ec3 | ||
|
2b079bf9fb | ||
|
94bccd3e16 | ||
|
35ded4259b | ||
|
e735339b75 | ||
|
de5ef6cabd | ||
|
da5566d59f | ||
|
82191700e6 | ||
|
ced5b7abfc | ||
|
ca8e9ce722 | ||
|
5ce04d6664 | ||
|
bfe2f5b3f9 | ||
|
c8dff5e2dc | ||
|
aec21860ba | ||
|
93ad0ef2ea | ||
|
ae5949657b | ||
|
dda929dfc5 | ||
|
a84cec9b2d | ||
|
1d8305d515 | ||
|
e963382d62 | ||
|
e6dae98c44 | ||
|
7843d4dd28 | ||
|
9217ac18e9 | ||
|
c65ea8d3ab | ||
|
d54d37e344 | ||
|
903e50a65c | ||
|
f3ee6289ec | ||
|
318ee92e61 | ||
|
a5fb7e021a | ||
|
09e37131ca | ||
|
39310a6695 | ||
|
14e80f2f74 | ||
|
2272691ada | ||
|
4bb6a2eedb | ||
|
7cb6b0f87b | ||
|
385d7e9579 | ||
|
99b24f314b | ||
|
9f51b2a1d6 | ||
|
82e4260084 | ||
|
5ceaed8126 | ||
|
cd32cac54b | ||
|
b1b3c4c469 | ||
|
c01cd3a33b | ||
|
4f2a5c7f43 | ||
|
19edf5a7a8 | ||
|
75d67cfb1d | ||
|
9609e0ef2e | ||
|
3613d4d543 | ||
|
d54a7e21dd | ||
|
0fe948feb3 | ||
|
3c329e0aad | ||
|
92a7c030b2 | ||
|
e75eb16a93 | ||
|
00993d46fa | ||
|
5c30cadaf6 | ||
|
22372abaab | ||
|
a34287dda6 | ||
|
34d55ede85 | ||
|
7ce7ddf9e6 | ||
|
7464b36afa | ||
|
45dd67433c | ||
|
ff8d4ebb94 | ||
|
f9d98e0936 | ||
|
ad28ae1daa | ||
|
192c0da831 | ||
|
e38e19edc1 | ||
|
8035db1bd0 | ||
|
da566d63e6 | ||
|
c176a752de | ||
|
532d64a0f9 | ||
|
487b6d1677 | ||
|
a9ba19fb24 | ||
|
105e529826 | ||
|
42179b8625 | ||
|
d668e4cb10 | ||
|
f654082c5b | ||
|
e1dc2a3750 | ||
|
105cbe0362 | ||
|
9c4325f3ef | ||
|
46a26487ba | ||
|
7680ebb93b | ||
|
c3d16bfa99 | ||
|
7599c898ef | ||
|
8ee129483a | ||
|
52a40f376c | ||
|
c8c81474a2 | ||
|
8c0ca8b9fe | ||
|
3cd1b066a2 | ||
|
ba5011b874 | ||
|
60867bdd2d | ||
|
8f8730b222 | ||
|
7854f7766e | ||
|
fde30fc073 | ||
|
431ba22e3c | ||
|
570bd17a51 | ||
|
4c0d3cac8d | ||
|
a379850f50 | ||
|
7bb1f0b887 | ||
|
4f6a9deaf8 | ||
|
32ff4db11c | ||
|
ac9265e774 | ||
|
c9a096ac1f | ||
|
51ba23b776 | ||
|
b358c90a73 | ||
|
000cd14799 | ||
|
0e2d3c159d | ||
|
63a758243a | ||
|
2df26cf9f8 | ||
|
6912eca829 | ||
|
348df92b89 | ||
|
1b7fd6d8c9 | ||
|
0ca3ce7a18 | ||
|
bf41a85d9c | ||
|
a0bfa8a447 | ||
|
7062b75ea9 | ||
|
34b544b7a1 | ||
|
5b5a51787c | ||
|
4d0f3ee944 | ||
|
e02400b1c7 | ||
|
4a60866ea4 | ||
|
4bdcab8bf6 | ||
|
f2ac181616 | ||
|
b6c0dd9cfc | ||
|
611317f398 | ||
|
ad48cdc2cb | ||
|
1486d076dc | ||
|
1243bbbded | ||
|
cf00d2d237 | ||
|
87ae269b82 | ||
|
673de11ca8 | ||
|
02bd9eb0c0 | ||
|
d86f9ade22 | ||
|
2dcc3a92e7 | ||
|
815a595b46 | ||
|
1d84db959c | ||
|
f127f71c91 | ||
|
fb63987ac8 | ||
|
635711e994 | ||
|
4929da0eac | ||
|
e60805bd45 | ||
|
49edaf37fd | ||
|
5324f53501 | ||
|
4a97716d59 | ||
|
6063855a3e | ||
|
1bcdbc9072 | ||
|
2dd3c614e2 | ||
|
6b19388139 | ||
|
a52e646cc1 | ||
|
29f0e03256 | ||
|
726e887239 | ||
|
6fba80168d | ||
|
90f4a96f93 | ||
|
33c1e24288 | ||
|
49904ec4d3 | ||
|
339dbbf2fd | ||
|
6caba97b34 | ||
|
0afbbf9129 | ||
|
df1fc2d334 | ||
|
7665b67ff4 | ||
|
a790c816d2 | ||
|
b9263ad17e | ||
|
8f0912a674 | ||
|
dd7855da3d | ||
|
e85f0151b2 | ||
|
840d740366 | ||
|
a5bd5d96ca | ||
|
4e8857ecee | ||
|
cf13f8f9a7 | ||
|
e2ffa533da | ||
|
6b20dbca42 | ||
|
6117a867d9 | ||
|
f7e9c0cf0d | ||
|
e0be851074 | ||
|
55855ccb5f | ||
|
4df2036187 | ||
|
4998e946cc | ||
|
04c0ddd3e0 | ||
|
b7afb0c0bc | ||
|
18eca4e8d6 | ||
|
797fd24112 | ||
|
cf0e3531f9 | ||
|
8cd09601b1 | ||
|
0bd43c072f | ||
|
10c1a93963 | ||
|
58e7f6d3c3 | ||
|
502f6a0e75 | ||
|
35e434ed85 | ||
|
b96bb908db | ||
|
5d0013ef53 | ||
|
0f69215fc8 | ||
|
57b5055858 | ||
|
af5fced79a | ||
|
d7499d1db0 | ||
|
233a05908d | ||
|
e406e27139 | ||
|
1c6ae39fc9 | ||
|
b627306772 | ||
|
8b4560dc1e | ||
|
608a8e4b88 | ||
|
6373dc41fa | ||
|
8cb789af39 | ||
|
cf975d082e | ||
|
e0d1f177ea | ||
|
bb9d6edad6 | ||
|
ab6299a488 | ||
|
226629977b | ||
|
f059829f03 | ||
|
49e9570f12 | ||
|
48a6d34f55 | ||
|
ca866229f6 | ||
|
d301affabb | ||
|
a97a1acf87 | ||
|
bdb13e2551 | ||
|
3d0502f7b6 | ||
|
6880417911 | ||
|
c4dca3592e | ||
|
4242fa02a0 | ||
|
f81b9fb725 | ||
|
b0b8e4fb2c | ||
|
7312d6f3f3 | ||
|
b75ed7295c | ||
|
c27a1f0791 | ||
|
eaab5bfbba | ||
|
fce5558b62 | ||
|
2ab520eeda | ||
|
cd95bf9c28 | ||
|
b9c8294045 | ||
|
2ffc5de6cf | ||
|
08071706ba | ||
|
f55502f723 | ||
|
7cee191516 | ||
|
dd26fcc3f1 | ||
|
571d017b82 | ||
|
570eb8ccb8 | ||
|
cca2ccfc71 | ||
|
41d6ac96d5 | ||
|
4c8edd3bfb | ||
|
3d65a6bf72 | ||
|
05c4c776af | ||
|
2b1a15c9e5 | ||
|
0bef428f8f | ||
|
4d7ae81f7a | ||
|
7d4f0aaece | ||
|
4734551c76 | ||
|
865c63a361 | ||
|
c3ea62c972 | ||
|
005951535b | ||
|
ecd052b7fd | ||
|
e15f2d17e5 | ||
|
9e55ae9184 | ||
|
b9794e6a13 | ||
|
1e3b49f064 | ||
|
f02b3a9869 | ||
|
3bf9d80c40 | ||
|
4a8bd762af | ||
|
d28c0a2425 | ||
|
6f49a9394f | ||
|
287b8bf95f | ||
|
d8a23d47bf | ||
|
16701c2df2 | ||
|
6ab4d9c0bc | ||
|
24f11362c7 | ||
|
a267fed24f | ||
|
a44df170f4 | ||
|
2ebbe57bc2 | ||
|
ea16598a78 | ||
|
4a3f8eb032 | ||
|
b0fc55499d | ||
|
ae652b40f4 | ||
|
8a6542f6b3 | ||
|
5378e93e15 | ||
|
cedb8d7c78 | ||
|
9809b2013f | ||
|
9b6bef7e9d | ||
|
fd5970fb34 | ||
|
b0ca330d08 | ||
|
edf8e2526d | ||
|
d0d222e974 | ||
|
c688975625 | ||
|
afd6824ce0 | ||
|
f5de0fc764 | ||
|
c701a75002 | ||
|
f77fb802eb | ||
|
063d97f8d3 | ||
|
c187dfde4c | ||
|
766667d8fa | ||
|
113dda4389 | ||
|
888a1e4f12 | ||
|
4d11a372c9 | ||
|
68b5a12f96 | ||
|
ae7f615b60 | ||
|
cfc6a52935 | ||
|
699d7d5002 | ||
|
996921528d | ||
|
2ae1c86ceb | ||
|
2c5ea5f94a | ||
|
64eb4e46ec | ||
|
a7ed269a01 | ||
|
84f928068f | ||
|
40171824cd | ||
|
7185b28e09 | ||
|
284463a172 | ||
|
df3d0aa057 | ||
|
ee97eec17d | ||
|
6b4675e3fa | ||
|
0934fe5cd7 | ||
|
8268cdde26 | ||
|
607fdc2a12 | ||
|
0a3fa0c3ed | ||
|
1aad21e866 | ||
|
8da08b8848 | ||
|
a45ecd3779 | ||
|
820a2f75de | ||
|
2a6ac58c71 | ||
|
ea97c3562f | ||
|
fc482b8771 | ||
|
5557944fb6 | ||
|
ebdb079a94 | ||
|
3e7b22fe9b | ||
|
839bc907ba | ||
|
7a33639aaa | ||
|
a4c73ad090 | ||
|
8ddc1f48e2 | ||
|
fe337dc6b0 | ||
|
87a36d7d31 | ||
|
0891575518 | ||
|
8b2594a526 | ||
|
6e1a1fe0df | ||
|
4287bab252 | ||
|
2643528945 | ||
|
accea5b1d8 | ||
|
0d5a952035 | ||
|
f998cc2d2e | ||
|
a88d6328fe | ||
|
b6be0dca40 | ||
|
e466d9b1dc | ||
|
473dc41afb | ||
|
2b48f3ff09 | ||
|
67243cbf7c | ||
|
41674386a6 | ||
|
616222c9f0 | ||
|
21e613e242 | ||
|
2291d3a009 | ||
|
6c787204aa | ||
|
4a982e54c9 | ||
|
6ab69e97d3 | ||
|
0e14e13845 | ||
|
a7fd31d233 | ||
|
3adb47235f | ||
|
154388aa20 | ||
|
716c634dc9 | ||
|
51907ad59e | ||
|
fa498ca728 | ||
|
b4b4d9b7c8 | ||
|
12cd1b9c6e | ||
|
0e106ccfbe | ||
|
b6d62fe9d3 | ||
|
965c4052fb | ||
|
3f590b86c6 | ||
|
80776d8969 | ||
|
923c5a2548 | ||
|
cf3a2be6d9 | ||
|
b7799c1e5b | ||
|
f43bd08728 | ||
|
0833c347e7 | ||
|
84caf89894 | ||
|
78dce3d85b | ||
|
737f8040cb | ||
|
e3ffbf63f6 | ||
|
533e17135e | ||
|
4c350b9a65 | ||
|
bc2aaf41af | ||
|
f6334b6af6 | ||
|
bf064710d0 | ||
|
3fc830aa09 | ||
|
0f788a9d92 | ||
|
fad1411995 | ||
|
8693654bdd | ||
|
9128342f25 | ||
|
8a28fbb29e | ||
|
dcd5918d3a | ||
|
974c10dca7 | ||
|
4ca46c2414 | ||
|
f1ea1e533d | ||
|
ca25b36aa0 | ||
|
fba22a7366 | ||
|
6a46ff449f | ||
|
eb8da2dcb2 | ||
|
2d33f18dec | ||
|
d2b174f269 | ||
|
253b952814 | ||
|
a6c048899c | ||
|
6a6e4bcce8 | ||
|
e98d666447 | ||
|
ee3ea2402d | ||
|
153d46fd90 | ||
|
7d91a1bf85 | ||
|
e6b50c5dd6 | ||
|
493b6adbc4 | ||
|
1b728ff96a | ||
|
68c967ec0c | ||
|
12c5518852 | ||
|
adced0a8c8 | ||
|
2e3254a908 | ||
|
7445390432 | ||
|
a49b664276 | ||
|
cf1d966ee6 | ||
|
86b816ec50 | ||
|
505577b755 | ||
|
54095f5420 | ||
|
d71856431a | ||
|
ae9b9c43a4 | ||
|
c4d718be9a | ||
|
a3012eaa27 | ||
|
741d90eb9b | ||
|
c36a888a59 | ||
|
56cc296754 | ||
|
4d24193baa | ||
|
de886d6182 | ||
|
201a14718e | ||
|
981166a2ab | ||
|
2b5d2c5ab8 | ||
|
50527a4613 | ||
|
20753c1bdb | ||
|
756d73c78c | ||
|
f90e76248f | ||
|
478393fb7d | ||
|
77ba202548 | ||
|
41ca79986b | ||
|
be1201cdeb | ||
|
aad8a3a865 | ||
|
0a6048c66d | ||
|
28d86f3a28 | ||
|
324fdd802a | ||
|
7bcd81a7e9 | ||
|
5cc2db4a9a | ||
|
be6530808f | ||
|
a17e2904dd | ||
|
3389241adb | ||
|
2e74c62be7 | ||
|
4f048fb563 | ||
|
0f8b3616b2 | ||
|
66fb59e934 | ||
|
f47ed83462 | ||
|
91f1a0de1e | ||
|
508586bf44 | ||
|
769e5f9c94 | ||
|
540635fe1c | ||
|
4d387bbfc9 | ||
|
125c945cc9 | ||
|
adeb27dc69 | ||
|
d65cf05369 | ||
|
2cf0a10f5b | ||
|
a67be39673 | ||
|
0010735aca | ||
|
520c51817a | ||
|
8abeb81fd0 | ||
|
b1532e666f | ||
|
8b3df5b268 | ||
|
061faf1748 | ||
|
42b392e06a | ||
|
905a4fec9c | ||
|
3569e7168b | ||
|
853264b113 | ||
|
afdd466bc6 | ||
|
ba43dc6c7e | ||
|
95b37a9ab2 | ||
|
a981832aaf | ||
|
28aa164abd | ||
|
243deb943f | ||
|
97beb3efc7 | ||
|
3d71669b0a | ||
|
b75d0d265e | ||
|
44fb597350 | ||
|
ed5d6f0d78 | ||
|
282afefddf | ||
|
2a02fb3753 | ||
|
ca5841f7fa | ||
|
5f3d660fde | ||
|
65408dc07a | ||
|
61c3aff33c | ||
|
90a96dee49 | ||
|
e82a8b8849 | ||
|
b6a8069e44 | ||
|
528b9d9fde | ||
|
6cb2796fc0 | ||
|
67a92edd2a | ||
|
3109e3966c | ||
|
7807eb1be0 | ||
|
b40b672288 | ||
|
99e1b30e86 | ||
|
42dec50c17 | ||
|
5c50387db0 | ||
|
feee11a0ba | ||
|
3bc06447d2 | ||
|
52998657ef | ||
|
d67df574cf | ||
|
4b4f7f2dd6 | ||
|
8623c29dd5 | ||
|
5aff766a4c | ||
|
e7f102bc9a | ||
|
8197cd9105 | ||
|
5f3a6210a2 | ||
|
b83f49d90b | ||
|
850a3c245e | ||
|
e252cc75b1 | ||
|
d8faed6ad2 | ||
|
d2f0a0586c | ||
|
c30e406cfd | ||
|
0973107b29 | ||
|
f6bcf094e1 | ||
|
eeb36e0d08 | ||
|
ab2ba347e5 | ||
|
edbff0dca1 | ||
|
2b98286ba3 | ||
|
f5f6ef41cb | ||
|
4a356cfc7c |
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
154
.github/workflows/haskell-ci-hackage.patch
vendored
154
.github/workflows/haskell-ci-hackage.patch
vendored
@ -2,14 +2,16 @@ Piggy-back on the haskell-ci workflow for automatic releases to Hackage.
|
|||||||
|
|
||||||
This extends the workflow with two additional triggers:
|
This extends the workflow with two additional triggers:
|
||||||
|
|
||||||
* When a release is created on GitHub, a candidate release is uploaded to
|
* When the Haskell-CI workflow is triggered manually with a non-empty version
|
||||||
Hackage and docs are submitted for it as Hackage can't build them itself
|
input (matching the version in the cabal file), a candidate release is
|
||||||
(https://github.com/haskell/hackage-server/issues/925).
|
uploaded to Hackage and docs are submitted for it as Hackage can't build
|
||||||
|
them itself (https://github.com/haskell/hackage-server/issues/925).
|
||||||
|
|
||||||
* To make a final release, the workflow can be triggered manually by entering
|
Note that promoting the candidate on Hackage discards the uploaded docs
|
||||||
the correct version number matching the version in the cabal file. This is
|
(https://github.com/haskell/hackage-server/issues/70). Don't do that.
|
||||||
here because promoting the candidate on Hackage discards the uploaded docs
|
|
||||||
(https://github.com/haskell/hackage-server/issues/70).
|
* When a release is created on GitHub, a final release is uploaded to Hackage
|
||||||
|
and docs are submitted for it.
|
||||||
|
|
||||||
The automation uses a special Hackage user: https://hackage.haskell.org/user/xmonad
|
The automation uses a special Hackage user: https://hackage.haskell.org/user/xmonad
|
||||||
and each repo (X11, xmonad, xmonad-contrib) has its own HACKAGE_API_KEY token
|
and each repo (X11, xmonad, xmonad-contrib) has its own HACKAGE_API_KEY token
|
||||||
@ -17,7 +19,7 @@ set in GitHub repository secrets.
|
|||||||
|
|
||||||
--- .github/workflows/haskell-ci.yml.orig
|
--- .github/workflows/haskell-ci.yml.orig
|
||||||
+++ .github/workflows/haskell-ci.yml
|
+++ .github/workflows/haskell-ci.yml
|
||||||
@@ -14,8 +14,17 @@
|
@@ -14,8 +14,15 @@
|
||||||
#
|
#
|
||||||
name: Haskell-CI
|
name: Haskell-CI
|
||||||
on:
|
on:
|
||||||
@ -31,85 +33,107 @@ set in GitHub repository secrets.
|
|||||||
+ workflow_dispatch:
|
+ workflow_dispatch:
|
||||||
+ inputs:
|
+ inputs:
|
||||||
+ version:
|
+ version:
|
||||||
+ # releases to Hackage are final and cannot be reverted, thus require
|
+ description: candidate version (must match version in cabal file)
|
||||||
+ # manual entry of version as a poor man's mistake avoidance
|
|
||||||
+ description: version (must match version in cabal file)
|
|
||||||
jobs:
|
jobs:
|
||||||
linux:
|
linux:
|
||||||
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
||||||
@@ -28,6 +37,7 @@
|
@@ -33,6 +40,7 @@
|
||||||
include:
|
compilerVersion: 9.8.4
|
||||||
- compiler: ghc-9.0.1
|
setup-method: ghcup
|
||||||
allow-failure: false
|
allow-failure: false
|
||||||
+ upload: true
|
+ upload: true
|
||||||
- compiler: ghc-8.10.4
|
- compiler: ghc-9.6.7
|
||||||
allow-failure: false
|
compilerKind: ghc
|
||||||
- compiler: ghc-8.8.4
|
compilerVersion: 9.6.7
|
||||||
@@ -171,8 +181,66 @@
|
@@ -257,6 +265,10 @@
|
||||||
${CABAL} -vnormal check
|
|
||||||
- name: haddock
|
- name: haddock
|
||||||
run: |
|
run: |
|
||||||
- $CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
|
$CABAL v2-haddock --disable-documentation $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
|
||||||
|
+ - name: haddock for hackage
|
||||||
|
+ if: matrix.upload
|
||||||
|
+ run: |
|
||||||
+ $CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH --haddock-for-hackage --builddir $GITHUB_WORKSPACE/haddock all
|
+ $CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH --haddock-for-hackage --builddir $GITHUB_WORKSPACE/haddock all
|
||||||
- name: unconstrained build
|
- name: unconstrained build
|
||||||
run: |
|
run: |
|
||||||
rm -f cabal.project.local
|
rm -f cabal.project.local
|
||||||
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
|
@@ -267,3 +279,80 @@
|
||||||
+ - name: upload artifacts (sdist)
|
with:
|
||||||
|
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
|
||||||
|
path: ~/.cabal/store
|
||||||
|
+ # must be separate artifacts because GitHub Actions are still broken:
|
||||||
|
+ # https://github.com/actions/upload-artifact/issues/441
|
||||||
|
+ # https://github.com/actions/upload-artifact/issues/457
|
||||||
|
+ - name: upload artifact (sdist)
|
||||||
+ if: matrix.upload
|
+ if: matrix.upload
|
||||||
+ uses: actions/upload-artifact@v2
|
+ uses: actions/upload-artifact@v4
|
||||||
+ with:
|
+ with:
|
||||||
|
+ name: sdist
|
||||||
+ path: ${{ github.workspace }}/sdist/*.tar.gz
|
+ path: ${{ github.workspace }}/sdist/*.tar.gz
|
||||||
+ - name: upload artifacts (haddock)
|
+ - name: upload artifact (haddock)
|
||||||
+ if: matrix.upload
|
+ if: matrix.upload
|
||||||
+ uses: actions/upload-artifact@v2
|
+ uses: actions/upload-artifact@v4
|
||||||
+ with:
|
+ with:
|
||||||
|
+ name: haddock
|
||||||
+ path: ${{ github.workspace }}/haddock/*-docs.tar.gz
|
+ path: ${{ github.workspace }}/haddock/*-docs.tar.gz
|
||||||
+ - name: hackage upload (candidate)
|
+ - name: hackage upload (candidate)
|
||||||
+ if: matrix.upload && github.event_name == 'release'
|
+ if: matrix.upload && github.event_name == 'workflow_dispatch' && github.event.inputs.version != ''
|
||||||
|
+ shell: bash
|
||||||
+ run: |
|
+ run: |
|
||||||
+ set -ex
|
+ set -ex
|
||||||
+ PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
+ PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||||
+ curl \
|
+ res=$(
|
||||||
+ --silent --show-error --fail \
|
+ curl \
|
||||||
+ --header "Accept: text/plain" \
|
+ --silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
+ --header "Accept: text/plain" \
|
||||||
+ --form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
+ https://hackage.haskell.org/packages/candidates/
|
+ --form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
||||||
+ curl \
|
+ https://hackage.haskell.org/packages/candidates/
|
||||||
+ --silent --show-error --fail \
|
+ )
|
||||||
+ -X PUT \
|
+ [[ $res == 2?? ]] # TODO: --fail-with-body once curl 7.76.0 is available
|
||||||
+ --header "Accept: text/plain" \
|
+ res=$(
|
||||||
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
+ curl \
|
||||||
+ --header "Content-Type: application/x-tar" \
|
+ --silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
+ --header "Content-Encoding: gzip" \
|
+ -X PUT \
|
||||||
+ --data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
+ --header "Accept: text/plain" \
|
||||||
+ https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/candidate/docs
|
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
+ env:
|
+ --header "Content-Type: application/x-tar" \
|
||||||
+ HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
+ --header "Content-Encoding: gzip" \
|
||||||
+ PACKAGE_NAME: ${{ github.event.repository.name }}
|
+ --data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
||||||
+ PACKAGE_VERSION: ${{ github.event.release.tag_name }}
|
+ https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/candidate/docs
|
||||||
+ - name: hackage upload (release)
|
+ )
|
||||||
+ if: matrix.upload && github.event_name == 'workflow_dispatch'
|
+ [[ $res == 2?? ]]
|
||||||
+ run: |
|
|
||||||
+ set -ex
|
|
||||||
+ PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
|
||||||
+ curl \
|
|
||||||
+ --silent --show-error --fail \
|
|
||||||
+ --header "Accept: text/plain" \
|
|
||||||
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
|
||||||
+ --form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
|
||||||
+ https://hackage.haskell.org/packages/
|
|
||||||
+ curl \
|
|
||||||
+ --silent --show-error --fail \
|
|
||||||
+ -X PUT \
|
|
||||||
+ --header "Accept: text/plain" \
|
|
||||||
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
|
||||||
+ --header "Content-Type: application/x-tar" \
|
|
||||||
+ --header "Content-Encoding: gzip" \
|
|
||||||
+ --data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
|
||||||
+ https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/docs
|
|
||||||
+ env:
|
+ env:
|
||||||
+ HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
+ HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||||
+ PACKAGE_NAME: ${{ github.event.repository.name }}
|
+ PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||||
+ PACKAGE_VERSION: ${{ github.event.inputs.version }}
|
+ PACKAGE_VERSION: ${{ github.event.inputs.version }}
|
||||||
|
+ - name: hackage upload (release)
|
||||||
|
+ if: matrix.upload && github.event_name == 'release'
|
||||||
|
+ shell: bash
|
||||||
|
+ run: |
|
||||||
|
+ set -ex
|
||||||
|
+ PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||||
|
+ res=$(
|
||||||
|
+ curl \
|
||||||
|
+ --silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
|
+ --header "Accept: text/plain" \
|
||||||
|
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
|
+ --form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
||||||
|
+ https://hackage.haskell.org/packages/
|
||||||
|
+ )
|
||||||
|
+ [[ $res == 2?? ]] # TODO: --fail-with-body once curl 7.76.0 is available
|
||||||
|
+ res=$(
|
||||||
|
+ curl \
|
||||||
|
+ --silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
|
+ -X PUT \
|
||||||
|
+ --header "Accept: text/plain" \
|
||||||
|
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
|
+ --header "Content-Type: application/x-tar" \
|
||||||
|
+ --header "Content-Encoding: gzip" \
|
||||||
|
+ --data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
||||||
|
+ https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/docs
|
||||||
|
+ )
|
||||||
|
+ [[ $res == 2?? ]]
|
||||||
|
+ env:
|
||||||
|
+ HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||||
|
+ PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||||
|
+ PACKAGE_VERSION: ${{ github.event.release.tag_name }}
|
||||||
|
272
.github/workflows/haskell-ci.yml
vendored
272
.github/workflows/haskell-ci.yml
vendored
@ -8,9 +8,9 @@
|
|||||||
#
|
#
|
||||||
# For more information, see https://github.com/haskell-CI/haskell-ci
|
# For more information, see https://github.com/haskell-CI/haskell-ci
|
||||||
#
|
#
|
||||||
# version: 0.12
|
# version: 0.19.20250506
|
||||||
#
|
#
|
||||||
# REGENDATA ("0.12",["github","cabal.project"])
|
# REGENDATA ("0.19.20250506",["github","cabal.project"])
|
||||||
#
|
#
|
||||||
name: Haskell-CI
|
name: Haskell-CI
|
||||||
on:
|
on:
|
||||||
@ -22,63 +22,111 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
version:
|
||||||
# releases to Hackage are final and cannot be reverted, thus require
|
description: candidate version (must match version in cabal file)
|
||||||
# manual entry of version as a poor man's mistake avoidance
|
|
||||||
description: version (must match version in cabal file)
|
|
||||||
jobs:
|
jobs:
|
||||||
linux:
|
linux:
|
||||||
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes:
|
||||||
|
60
|
||||||
container:
|
container:
|
||||||
image: buildpack-deps:bionic
|
image: buildpack-deps:jammy
|
||||||
continue-on-error: ${{ matrix.allow-failure }}
|
continue-on-error: ${{ matrix.allow-failure }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- compiler: ghc-9.0.1
|
- compiler: ghc-9.12.2
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 9.12.2
|
||||||
|
setup-method: ghcup
|
||||||
|
allow-failure: false
|
||||||
|
- compiler: ghc-9.10.2
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 9.10.2
|
||||||
|
setup-method: ghcup
|
||||||
|
allow-failure: false
|
||||||
|
- compiler: ghc-9.8.4
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 9.8.4
|
||||||
|
setup-method: ghcup
|
||||||
allow-failure: false
|
allow-failure: false
|
||||||
upload: true
|
upload: true
|
||||||
- compiler: ghc-8.10.4
|
- compiler: ghc-9.6.7
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 9.6.7
|
||||||
|
setup-method: ghcup
|
||||||
|
allow-failure: false
|
||||||
|
- compiler: ghc-9.4.8
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 9.4.8
|
||||||
|
setup-method: ghcup
|
||||||
|
allow-failure: false
|
||||||
|
- compiler: ghc-9.2.8
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 9.2.8
|
||||||
|
setup-method: ghcup
|
||||||
|
allow-failure: false
|
||||||
|
- compiler: ghc-9.0.2
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 9.0.2
|
||||||
|
setup-method: ghcup
|
||||||
|
allow-failure: false
|
||||||
|
- compiler: ghc-8.10.7
|
||||||
|
compilerKind: ghc
|
||||||
|
compilerVersion: 8.10.7
|
||||||
|
setup-method: ghcup
|
||||||
allow-failure: false
|
allow-failure: false
|
||||||
- compiler: ghc-8.8.4
|
- compiler: ghc-8.8.4
|
||||||
allow-failure: false
|
compilerKind: ghc
|
||||||
- compiler: ghc-8.6.5
|
compilerVersion: 8.8.4
|
||||||
allow-failure: false
|
setup-method: ghcup
|
||||||
- compiler: ghc-8.4.4
|
|
||||||
allow-failure: false
|
allow-failure: false
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- name: apt
|
- name: apt-get install
|
||||||
run: |
|
run: |
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common
|
apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5
|
||||||
apt-add-repository -y 'ppa:hvr/ghc'
|
apt-get install -y libx11-dev libxext-dev libxft-dev libxinerama-dev libxrandr-dev libxss-dev
|
||||||
apt-get update
|
- name: Install GHCup
|
||||||
apt-get install -y $CC cabal-install-3.4 libx11-dev libxext-dev libxft-dev libxinerama-dev libxrandr-dev libxss-dev
|
run: |
|
||||||
|
mkdir -p "$HOME/.ghcup/bin"
|
||||||
|
curl -sL https://downloads.haskell.org/ghcup/0.1.50.1/x86_64-linux-ghcup-0.1.50.1 > "$HOME/.ghcup/bin/ghcup"
|
||||||
|
chmod a+x "$HOME/.ghcup/bin/ghcup"
|
||||||
|
- name: Install cabal-install
|
||||||
|
run: |
|
||||||
|
"$HOME/.ghcup/bin/ghcup" install cabal 3.14.2.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
|
||||||
|
echo "CABAL=$HOME/.ghcup/bin/cabal-3.14.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
|
||||||
|
- name: Install GHC (GHCup)
|
||||||
|
if: matrix.setup-method == 'ghcup'
|
||||||
|
run: |
|
||||||
|
"$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false)
|
||||||
|
HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER")
|
||||||
|
HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#')
|
||||||
|
HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#')
|
||||||
|
echo "HC=$HC" >> "$GITHUB_ENV"
|
||||||
|
echo "HCPKG=$HCPKG" >> "$GITHUB_ENV"
|
||||||
|
echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV"
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.compiler }}
|
HCKIND: ${{ matrix.compilerKind }}
|
||||||
|
HCNAME: ${{ matrix.compiler }}
|
||||||
|
HCVER: ${{ matrix.compilerVersion }}
|
||||||
- name: Set PATH and environment variables
|
- name: Set PATH and environment variables
|
||||||
run: |
|
run: |
|
||||||
echo "$HOME/.cabal/bin" >> $GITHUB_PATH
|
echo "$HOME/.cabal/bin" >> $GITHUB_PATH
|
||||||
echo "LANG=C.UTF-8" >> $GITHUB_ENV
|
echo "LANG=C.UTF-8" >> "$GITHUB_ENV"
|
||||||
echo "CABAL_DIR=$HOME/.cabal" >> $GITHUB_ENV
|
echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV"
|
||||||
echo "CABAL_CONFIG=$HOME/.cabal/config" >> $GITHUB_ENV
|
echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV"
|
||||||
HCDIR=$(echo "/opt/$CC" | sed 's/-/\//')
|
|
||||||
HCNAME=ghc
|
|
||||||
HC=$HCDIR/bin/$HCNAME
|
|
||||||
echo "HC=$HC" >> $GITHUB_ENV
|
|
||||||
echo "HCPKG=$HCDIR/bin/$HCNAME-pkg" >> $GITHUB_ENV
|
|
||||||
echo "HADDOCK=$HCDIR/bin/haddock" >> $GITHUB_ENV
|
|
||||||
echo "CABAL=/opt/cabal/3.4/bin/cabal -vnormal+nowrap" >> $GITHUB_ENV
|
|
||||||
HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')
|
HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')
|
||||||
echo "HCNUMVER=$HCNUMVER" >> $GITHUB_ENV
|
echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV"
|
||||||
echo "ARG_TESTS=--enable-tests" >> $GITHUB_ENV
|
echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV"
|
||||||
echo "ARG_BENCH=--enable-benchmarks" >> $GITHUB_ENV
|
echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV"
|
||||||
echo "HEADHACKAGE=false" >> $GITHUB_ENV
|
echo "HEADHACKAGE=false" >> "$GITHUB_ENV"
|
||||||
echo "ARG_COMPILER=--$HCNAME --with-compiler=$HC" >> $GITHUB_ENV
|
echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV"
|
||||||
echo "GHCJSARITH=0" >> $GITHUB_ENV
|
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.compiler }}
|
HCKIND: ${{ matrix.compilerKind }}
|
||||||
|
HCNAME: ${{ matrix.compiler }}
|
||||||
|
HCVER: ${{ matrix.compilerVersion }}
|
||||||
- name: env
|
- name: env
|
||||||
run: |
|
run: |
|
||||||
env
|
env
|
||||||
@ -101,6 +149,10 @@ jobs:
|
|||||||
repository hackage.haskell.org
|
repository hackage.haskell.org
|
||||||
url: http://hackage.haskell.org/
|
url: http://hackage.haskell.org/
|
||||||
EOF
|
EOF
|
||||||
|
cat >> $CABAL_CONFIG <<EOF
|
||||||
|
program-default-options
|
||||||
|
ghc-options: $GHCJOBS +RTS -M3G -RTS
|
||||||
|
EOF
|
||||||
cat $CABAL_CONFIG
|
cat $CABAL_CONFIG
|
||||||
- name: versions
|
- name: versions
|
||||||
run: |
|
run: |
|
||||||
@ -110,28 +162,17 @@ jobs:
|
|||||||
- name: update cabal index
|
- name: update cabal index
|
||||||
run: |
|
run: |
|
||||||
$CABAL v2-update -v
|
$CABAL v2-update -v
|
||||||
- name: cache (tools)
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
key: ${{ runner.os }}-${{ matrix.compiler }}-tools-6c71d9d3
|
|
||||||
path: ~/.haskell-ci-tools
|
|
||||||
- name: install cabal-plan
|
- name: install cabal-plan
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/.cabal/bin
|
mkdir -p $HOME/.cabal/bin
|
||||||
curl -sL https://github.com/haskell-hvr/cabal-plan/releases/download/v0.6.2.0/cabal-plan-0.6.2.0-x86_64-linux.xz > cabal-plan.xz
|
curl -sL https://github.com/haskell-hvr/cabal-plan/releases/download/v0.7.3.0/cabal-plan-0.7.3.0-x86_64-linux.xz > cabal-plan.xz
|
||||||
echo 'de73600b1836d3f55e32d80385acc055fd97f60eaa0ab68a755302685f5d81bc cabal-plan.xz' | sha256sum -c -
|
echo 'f62ccb2971567a5f638f2005ad3173dba14693a45154c1508645c52289714cb2 cabal-plan.xz' | sha256sum -c -
|
||||||
xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan
|
xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan
|
||||||
rm -f cabal-plan.xz
|
rm -f cabal-plan.xz
|
||||||
chmod a+x $HOME/.cabal/bin/cabal-plan
|
chmod a+x $HOME/.cabal/bin/cabal-plan
|
||||||
cabal-plan --version
|
cabal-plan --version
|
||||||
- name: install hlint
|
|
||||||
run: |
|
|
||||||
if [ $((HCNUMVER >= 90000)) -ne 0 ] ; then HLINTVER=$(cd /tmp && (${CABAL} v2-install -v $ARG_COMPILER --dry-run hlint --constraint='hlint >=3.2 && <3.3' | perl -ne 'if (/\bhlint-(\d+(\.\d+)*)\b/) { print "$1"; last; }')); echo "HLint version $HLINTVER" ; fi
|
|
||||||
if [ $((HCNUMVER >= 90000)) -ne 0 ] ; then if [ ! -e $HOME/.haskell-ci-tools/hlint-$HLINTVER/hlint ]; then echo "Downloading HLint version $HLINTVER"; mkdir -p $HOME/.haskell-ci-tools; curl --write-out 'Status Code: %{http_code} Redirects: %{num_redirects} Total time: %{time_total} Total Dsize: %{size_download}\n' --silent --location --output $HOME/.haskell-ci-tools/hlint-$HLINTVER.tar.gz "https://github.com/ndmitchell/hlint/releases/download/v$HLINTVER/hlint-$HLINTVER-x86_64-linux.tar.gz"; tar -xzv -f $HOME/.haskell-ci-tools/hlint-$HLINTVER.tar.gz -C $HOME/.haskell-ci-tools; fi ; fi
|
|
||||||
if [ $((HCNUMVER >= 90000)) -ne 0 ] ; then mkdir -p $CABAL_DIR/bin && ln -sf "$HOME/.haskell-ci-tools/hlint-$HLINTVER/hlint" $CABAL_DIR/bin/hlint ; fi
|
|
||||||
if [ $((HCNUMVER >= 90000)) -ne 0 ] ; then hlint --version ; fi
|
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: source
|
path: source
|
||||||
- name: initial cabal.project for sdist
|
- name: initial cabal.project for sdist
|
||||||
@ -150,7 +191,8 @@ jobs:
|
|||||||
- name: generate cabal.project
|
- name: generate cabal.project
|
||||||
run: |
|
run: |
|
||||||
PKGDIR_xmonad_contrib="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/xmonad-contrib-[0-9.]*')"
|
PKGDIR_xmonad_contrib="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/xmonad-contrib-[0-9.]*')"
|
||||||
echo "PKGDIR_xmonad_contrib=${PKGDIR_xmonad_contrib}" >> $GITHUB_ENV
|
echo "PKGDIR_xmonad_contrib=${PKGDIR_xmonad_contrib}" >> "$GITHUB_ENV"
|
||||||
|
rm -f cabal.project cabal.project.local
|
||||||
touch cabal.project
|
touch cabal.project
|
||||||
touch cabal.project.local
|
touch cabal.project.local
|
||||||
echo "packages: ${PKGDIR_xmonad_contrib}" >> cabal.project
|
echo "packages: ${PKGDIR_xmonad_contrib}" >> cabal.project
|
||||||
@ -168,15 +210,15 @@ jobs:
|
|||||||
flags: +pedantic
|
flags: +pedantic
|
||||||
ghc-options: -j
|
ghc-options: -j
|
||||||
EOF
|
EOF
|
||||||
$HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: $_ installed\n" unless /^(xmonad-contrib)$/; }' >> cabal.project.local
|
$HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: any.$_ installed\n" unless /^(xmonad-contrib)$/; }' >> cabal.project.local
|
||||||
cat cabal.project
|
cat cabal.project
|
||||||
cat cabal.project.local
|
cat cabal.project.local
|
||||||
- name: dump install plan
|
- name: dump install plan
|
||||||
run: |
|
run: |
|
||||||
$CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all
|
$CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all
|
||||||
cabal-plan
|
cabal-plan
|
||||||
- name: cache
|
- name: restore cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache/restore@v4
|
||||||
with:
|
with:
|
||||||
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
|
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
|
||||||
path: ~/.cabal/store
|
path: ~/.cabal/store
|
||||||
@ -194,75 +236,101 @@ jobs:
|
|||||||
- name: tests
|
- name: tests
|
||||||
run: |
|
run: |
|
||||||
$CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct
|
$CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct
|
||||||
- name: hlint
|
|
||||||
run: |
|
|
||||||
if [ $((HCNUMVER >= 90000)) -ne 0 ] ; then (cd ${PKGDIR_xmonad_contrib} && hlint -h ${GITHUB_WORKSPACE}/source/.hlint.yaml .) ; fi
|
|
||||||
- name: cabal check
|
- name: cabal check
|
||||||
run: |
|
run: |
|
||||||
cd ${PKGDIR_xmonad_contrib} || false
|
cd ${PKGDIR_xmonad_contrib} || false
|
||||||
${CABAL} -vnormal check
|
${CABAL} -vnormal check
|
||||||
- name: haddock
|
- name: haddock
|
||||||
|
run: |
|
||||||
|
$CABAL v2-haddock --disable-documentation $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
|
||||||
|
- name: haddock for hackage
|
||||||
|
if: matrix.upload
|
||||||
run: |
|
run: |
|
||||||
$CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH --haddock-for-hackage --builddir $GITHUB_WORKSPACE/haddock all
|
$CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH --haddock-for-hackage --builddir $GITHUB_WORKSPACE/haddock all
|
||||||
- name: unconstrained build
|
- name: unconstrained build
|
||||||
run: |
|
run: |
|
||||||
rm -f cabal.project.local
|
rm -f cabal.project.local
|
||||||
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
|
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
|
||||||
- name: upload artifacts (sdist)
|
- name: save cache
|
||||||
if: matrix.upload
|
if: always()
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/cache/save@v4
|
||||||
with:
|
with:
|
||||||
|
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
|
||||||
|
path: ~/.cabal/store
|
||||||
|
# must be separate artifacts because GitHub Actions are still broken:
|
||||||
|
# https://github.com/actions/upload-artifact/issues/441
|
||||||
|
# https://github.com/actions/upload-artifact/issues/457
|
||||||
|
- name: upload artifact (sdist)
|
||||||
|
if: matrix.upload
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sdist
|
||||||
path: ${{ github.workspace }}/sdist/*.tar.gz
|
path: ${{ github.workspace }}/sdist/*.tar.gz
|
||||||
- name: upload artifacts (haddock)
|
- name: upload artifact (haddock)
|
||||||
if: matrix.upload
|
if: matrix.upload
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
name: haddock
|
||||||
path: ${{ github.workspace }}/haddock/*-docs.tar.gz
|
path: ${{ github.workspace }}/haddock/*-docs.tar.gz
|
||||||
- name: hackage upload (candidate)
|
- name: hackage upload (candidate)
|
||||||
if: matrix.upload && github.event_name == 'release'
|
if: matrix.upload && github.event_name == 'workflow_dispatch' && github.event.inputs.version != ''
|
||||||
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -ex
|
set -ex
|
||||||
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||||
curl \
|
res=$(
|
||||||
--silent --show-error --fail \
|
curl \
|
||||||
--header "Accept: text/plain" \
|
--silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
--header "Accept: text/plain" \
|
||||||
--form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
https://hackage.haskell.org/packages/candidates/
|
--form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
||||||
curl \
|
https://hackage.haskell.org/packages/candidates/
|
||||||
--silent --show-error --fail \
|
)
|
||||||
-X PUT \
|
[[ $res == 2?? ]] # TODO: --fail-with-body once curl 7.76.0 is available
|
||||||
--header "Accept: text/plain" \
|
res=$(
|
||||||
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
curl \
|
||||||
--header "Content-Type: application/x-tar" \
|
--silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
--header "Content-Encoding: gzip" \
|
-X PUT \
|
||||||
--data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
--header "Accept: text/plain" \
|
||||||
https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/candidate/docs
|
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
env:
|
--header "Content-Type: application/x-tar" \
|
||||||
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
--header "Content-Encoding: gzip" \
|
||||||
PACKAGE_NAME: ${{ github.event.repository.name }}
|
--data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
||||||
PACKAGE_VERSION: ${{ github.event.release.tag_name }}
|
https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/candidate/docs
|
||||||
- name: hackage upload (release)
|
)
|
||||||
if: matrix.upload && github.event_name == 'workflow_dispatch'
|
[[ $res == 2?? ]]
|
||||||
run: |
|
|
||||||
set -ex
|
|
||||||
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
|
||||||
curl \
|
|
||||||
--silent --show-error --fail \
|
|
||||||
--header "Accept: text/plain" \
|
|
||||||
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
|
||||||
--form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
|
||||||
https://hackage.haskell.org/packages/
|
|
||||||
curl \
|
|
||||||
--silent --show-error --fail \
|
|
||||||
-X PUT \
|
|
||||||
--header "Accept: text/plain" \
|
|
||||||
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
|
||||||
--header "Content-Type: application/x-tar" \
|
|
||||||
--header "Content-Encoding: gzip" \
|
|
||||||
--data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
|
||||||
https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/docs
|
|
||||||
env:
|
env:
|
||||||
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||||
PACKAGE_NAME: ${{ github.event.repository.name }}
|
PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||||
PACKAGE_VERSION: ${{ github.event.inputs.version }}
|
PACKAGE_VERSION: ${{ github.event.inputs.version }}
|
||||||
|
- name: hackage upload (release)
|
||||||
|
if: matrix.upload && github.event_name == 'release'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -ex
|
||||||
|
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||||
|
res=$(
|
||||||
|
curl \
|
||||||
|
--silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
|
--header "Accept: text/plain" \
|
||||||
|
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
|
--form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
||||||
|
https://hackage.haskell.org/packages/
|
||||||
|
)
|
||||||
|
[[ $res == 2?? ]] # TODO: --fail-with-body once curl 7.76.0 is available
|
||||||
|
res=$(
|
||||||
|
curl \
|
||||||
|
--silent --show-error --output /dev/stderr --write-out '%{http_code}' \
|
||||||
|
-X PUT \
|
||||||
|
--header "Accept: text/plain" \
|
||||||
|
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||||
|
--header "Content-Type: application/x-tar" \
|
||||||
|
--header "Content-Encoding: gzip" \
|
||||||
|
--data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
||||||
|
https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/docs
|
||||||
|
)
|
||||||
|
[[ $res == 2?? ]]
|
||||||
|
env:
|
||||||
|
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||||
|
PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||||
|
PACKAGE_VERSION: ${{ github.event.release.tag_name }}
|
||||||
|
22
.github/workflows/hlint.yaml
vendored
Normal file
22
.github/workflows/hlint.yaml
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
name: hlint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
hlint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: 'Set up HLint'
|
||||||
|
uses: haskell-actions/hlint-setup@v2
|
||||||
|
with:
|
||||||
|
version: '3.5'
|
||||||
|
|
||||||
|
- name: 'Run HLint'
|
||||||
|
uses: haskell-actions/hlint-run@v2
|
||||||
|
with:
|
||||||
|
path: '["XMonad/", "tests/", "scripts/"]'
|
||||||
|
fail-on: status
|
14
.github/workflows/nix.yml
vendored
14
.github/workflows/nix.yml
vendored
@ -12,17 +12,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@v13
|
uses: cachix/install-nix-action@v31
|
||||||
with:
|
with:
|
||||||
install_url: https://nixos-nix-install-tests.cachix.org/serve/i6laym9jw3wg9mw6ncyrk6gjx4l34vvx/install
|
github_access_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
install_options: '--tarball-url-prefix https://nixos-nix-install-tests.cachix.org/serve'
|
|
||||||
extra_nix_config: |
|
|
||||||
experimental-features = nix-command flakes
|
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Clone project
|
- name: Clone project
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
- name: Build
|
- name: Build
|
||||||
# "nix build" builds with full optimization and includes a profiling
|
# "nix build" builds with full optimization and includes a profiling
|
||||||
# build, so just the build of xmonad-contrib itself takes 3 minutes.
|
# build, so just the build of xmonad-contrib itself takes 3 minutes.
|
||||||
# As a workaround, we invoke cabal manually here.
|
# As a workaround, we invoke cabal manually here.
|
||||||
run: nix develop -c cabal v2-build -O0 -j
|
run: |
|
||||||
|
nix develop -c cabal v2-update -O0 -j
|
||||||
|
nix develop -c cabal v2-build -O0 -j
|
||||||
|
14
.github/workflows/packdeps.yml
vendored
14
.github/workflows/packdeps.yml
vendored
@ -13,16 +13,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone project
|
- name: Clone project
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
- name: Setup Haskell
|
- name: Setup Haskell
|
||||||
uses: haskell/actions/setup@v1
|
uses: haskell-actions/setup@v2
|
||||||
with:
|
with:
|
||||||
# packdeps doesn't build with newer as of 2021-10
|
# packdeps doesn't build with newer as of 2021-10
|
||||||
ghc-version: '8.8'
|
ghc-version: '8.8'
|
||||||
- name: Install packdeps
|
- name: Install packdeps
|
||||||
run: |
|
run: |
|
||||||
set -ex
|
set -ex
|
||||||
echo "$HOME/.cabal/bin" >> $GITHUB_PATH
|
cd # go somewhere without a cabal.project
|
||||||
cabal install packdeps
|
cabal install packdeps
|
||||||
- name: Check package bounds (all)
|
- name: Check package bounds (all)
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
@ -40,3 +40,11 @@ jobs:
|
|||||||
--exclude X11 \
|
--exclude X11 \
|
||||||
--exclude xmonad \
|
--exclude xmonad \
|
||||||
*.cabal
|
*.cabal
|
||||||
|
|
||||||
|
workflow-keepalive:
|
||||||
|
if: github.event_name == 'schedule'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: write
|
||||||
|
steps:
|
||||||
|
- uses: liskin/gh-workflow-keepalive@v1
|
||||||
|
60
.github/workflows/stack.yml
vendored
60
.github/workflows/stack.yml
vendored
@ -12,42 +12,33 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# XXX: temporarily disabled until xmonad 0.17 release
|
- resolver: lts-16 # GHC 8.8
|
||||||
# - resolver: lts-12
|
yaml: stack.yaml
|
||||||
# ghc: 8.4.4
|
- resolver: lts-16 # GHC 8.8
|
||||||
# yaml: stack.yaml
|
|
||||||
- resolver: lts-12
|
|
||||||
ghc: 8.4.4
|
|
||||||
yaml: stack-master.yaml
|
yaml: stack-master.yaml
|
||||||
- resolver: lts-14
|
- resolver: lts-18 # GHC 8.10
|
||||||
ghc: 8.6.5
|
|
||||||
yaml: stack-master.yaml
|
yaml: stack-master.yaml
|
||||||
- resolver: lts-16
|
- resolver: lts-19 # GHC 9.0
|
||||||
ghc: 8.8.4
|
|
||||||
yaml: stack-master.yaml
|
yaml: stack-master.yaml
|
||||||
# - resolver: lts-17
|
- resolver: lts-20 # GHC 9.2
|
||||||
# ghc: 8.10.4
|
|
||||||
# yaml: stack.yaml
|
|
||||||
- resolver: lts-17
|
|
||||||
ghc: 8.10.4
|
|
||||||
yaml: stack-master.yaml
|
yaml: stack-master.yaml
|
||||||
- resolver: lts-18
|
- resolver: lts-21 # GHC 9.4
|
||||||
ghc: 8.10.7
|
yaml: stack-master.yaml
|
||||||
|
- resolver: lts-22 # GHC 9.6
|
||||||
|
yaml: stack-master.yaml
|
||||||
|
- resolver: lts-23 # GHC 9.8
|
||||||
|
yaml: stack.yaml
|
||||||
|
- resolver: lts-23 # GHC 9.8
|
||||||
yaml: stack-master.yaml
|
yaml: stack-master.yaml
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone project
|
- name: Clone project
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Prepare apt sources
|
|
||||||
run: |
|
|
||||||
set -ex
|
|
||||||
sudo add-apt-repository -y ppa:hvr/ghc
|
|
||||||
sudo apt update -y
|
|
||||||
|
|
||||||
- name: Install C dependencies
|
- name: Install C dependencies
|
||||||
run: |
|
run: |
|
||||||
set -ex
|
set -ex
|
||||||
|
sudo apt update -y
|
||||||
sudo apt install -y \
|
sudo apt install -y \
|
||||||
libx11-dev \
|
libx11-dev \
|
||||||
libxext-dev \
|
libxext-dev \
|
||||||
@ -57,31 +48,23 @@ jobs:
|
|||||||
libxss-dev \
|
libxss-dev \
|
||||||
#
|
#
|
||||||
|
|
||||||
- name: Install GHC
|
|
||||||
# use system ghc (if available) in stack, don't waste GH Actions cache space
|
|
||||||
continue-on-error: true
|
|
||||||
run: |
|
|
||||||
set -ex
|
|
||||||
sudo apt install -y ghc-${{ matrix.ghc }}
|
|
||||||
echo /opt/ghc/${{ matrix.ghc }}/bin >> $GITHUB_PATH
|
|
||||||
|
|
||||||
- name: Refresh caches once a month
|
- name: Refresh caches once a month
|
||||||
id: cache-date
|
id: cache-date
|
||||||
# GHA writes caches on the first miss and then never updates them again;
|
# GHA writes caches on the first miss and then never updates them again;
|
||||||
# force updating the cache at least once a month
|
# force updating the cache at least once a month. Additionally, the
|
||||||
|
# date is prefixed with an epoch number to let us manually refresh the
|
||||||
|
# cache when needed. This is a workaround for https://github.com/actions/cache/issues/2
|
||||||
run: |
|
run: |
|
||||||
echo "::set-output name=date::$(date +%Y-%m)"
|
date +date=1-%Y-%m >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Cache Haskell package metadata
|
- name: Cache Haskell package metadata
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.stack/pantry
|
path: ~/.stack/pantry
|
||||||
key: stack-pantry-${{ runner.os }}-${{ steps.cache-date.outputs.date }}
|
key: stack-pantry-${{ runner.os }}-${{ steps.cache-date.outputs.date }}
|
||||||
restore-keys: |
|
|
||||||
stack-pantry-${{ runner.os }}-
|
|
||||||
|
|
||||||
- name: Cache Haskell dependencies
|
- name: Cache Haskell dependencies
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.stack/*
|
~/.stack/*
|
||||||
@ -91,7 +74,6 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
stack-${{ runner.os }}-${{ matrix.resolver }}-${{ steps.cache-date.outputs.date }}-${{ hashFiles(matrix.yaml) }}-
|
stack-${{ runner.os }}-${{ matrix.resolver }}-${{ steps.cache-date.outputs.date }}-${{ hashFiles(matrix.yaml) }}-
|
||||||
stack-${{ runner.os }}-${{ matrix.resolver }}-${{ steps.cache-date.outputs.date }}-
|
stack-${{ runner.os }}-${{ matrix.resolver }}-${{ steps.cache-date.outputs.date }}-
|
||||||
stack-${{ runner.os }}-${{ matrix.resolver }}-
|
|
||||||
|
|
||||||
- name: Update hackage index
|
- name: Update hackage index
|
||||||
# always update index to prevent the shared ~/.stack/pantry cache from being empty
|
# always update index to prevent the shared ~/.stack/pantry cache from being empty
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -27,3 +27,6 @@ tags
|
|||||||
|
|
||||||
stack.yaml.lock
|
stack.yaml.lock
|
||||||
|
|
||||||
|
# nix artifacts
|
||||||
|
result
|
||||||
|
flake.lock
|
||||||
|
5
.mailmap
5
.mailmap
@ -103,6 +103,7 @@ hexago.nl <xmonad-contrib@hexago.nl>
|
|||||||
lithis <xmonad@selg.hethrael.org>
|
lithis <xmonad@selg.hethrael.org>
|
||||||
lithis <xmonad@selg.hethrael.org> <xmonad@s001.hethrael.com>
|
lithis <xmonad@selg.hethrael.org> <xmonad@s001.hethrael.com>
|
||||||
sam-barr <mail@samf.bar> <samfbarr@outlook.com>
|
sam-barr <mail@samf.bar> <samfbarr@outlook.com>
|
||||||
slotThe <soliditsallgood@mailbox.org> <50166980+slotThe@users.noreply.github.com>
|
Tony Zorman <soliditsallgood@mailbox.org> <50166980+slotThe@users.noreply.github.com>
|
||||||
slotThe <soliditsallgood@mailbox.org> <soliditsallgood@tuta.io>
|
Tony Zorman <soliditsallgood@mailbox.org> <soliditsallgood@tuta.io>
|
||||||
|
Tony Zorman <soliditsallgood@mailbox.org>
|
||||||
spoonm <spoonm@spoonm.org>
|
spoonm <spoonm@spoonm.org>
|
||||||
|
731
CHANGES.md
731
CHANGES.md
@ -1,5 +1,722 @@
|
|||||||
# Change Log / Release Notes
|
# Change Log / Release Notes
|
||||||
|
|
||||||
|
## _unreleased_
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
* Drop support for GHC 8.6
|
||||||
|
|
||||||
|
### Bug Fixes and Minor Changes
|
||||||
|
|
||||||
|
* `XMonad.Util.EZConfig`
|
||||||
|
|
||||||
|
- Added `XF86WLAN` and `Menu` to the list of supported special keys.
|
||||||
|
|
||||||
|
* `XMonad.Actions.DynamicProjects`
|
||||||
|
|
||||||
|
- No longer autodelete projects when `switchProject` is called from
|
||||||
|
an empty workspace. This also fixes a bug where static workspaces
|
||||||
|
would be deleted when switching to a dynamic project.
|
||||||
|
- Improved documentation on how to close a project.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.Rescreen`
|
||||||
|
|
||||||
|
- Allow overriding the `rescreen` operation itself. Additionally, the
|
||||||
|
`XMonad.Actions.PhysicalScreens` module now provides an alternative
|
||||||
|
implementation of `rescreen` that avoids reshuffling the workspaces if
|
||||||
|
the number of screens doesn't change and only their locations do (which
|
||||||
|
is especially common if one uses `xrandr --setmonitor` to split an
|
||||||
|
ultra-wide display in two).
|
||||||
|
|
||||||
|
- Added an optional delay when waiting for events to settle. This may be
|
||||||
|
used to avoid flicker and unnecessary workspace reshuffling if multiple
|
||||||
|
`xrandr` commands are used to reconfigure the display layout.
|
||||||
|
|
||||||
|
* `XMonad.Layout.NoBorders`
|
||||||
|
|
||||||
|
- It's no longer necessary to use `borderEventHook` to garbage collect
|
||||||
|
`alwaysHidden`/`neverHidden` lists. The layout listens to
|
||||||
|
`DestroyWindowEvent` messages instead, which are broadcast to layouts
|
||||||
|
since xmonad v0.17.0.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.EwmhDesktops`
|
||||||
|
|
||||||
|
- Added a customization option for the action that gets executed when
|
||||||
|
a client sends a **_NET_CURRENT_DESKTOP** request. It is now possible
|
||||||
|
to change it using the `setEwmhSwitchDesktopHook`.
|
||||||
|
- Added a customization option for mapping hidden workspaces to screens
|
||||||
|
when setting the **_NET_DESKTOP_VIEWPORT**. This can be done using
|
||||||
|
the `setEwmhHiddenWorkspaceToScreenMapping`.
|
||||||
|
|
||||||
|
* `XMonad.Layout.IndependentScreens`
|
||||||
|
|
||||||
|
- Added `focusWorkspace` for focusing workspaces on the screen that they
|
||||||
|
belong to.
|
||||||
|
- Added `doFocus'` hook as an alternative for `doFocus` when using
|
||||||
|
IndependentScreens.
|
||||||
|
- Added `screenOnMonitor` for getting the active screen for a monitor.
|
||||||
|
|
||||||
|
* `XMonad.Util.NamedScratchPad`
|
||||||
|
|
||||||
|
- Fix unintended window hiding in `nsSingleScratchpadPerWorkspace`.
|
||||||
|
Only hide the previously active scratchpad.
|
||||||
|
|
||||||
|
## 0.18.1 (August 20, 2024)
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
* `XMonad.Hooks.StatusBars`
|
||||||
|
|
||||||
|
- Move status bar functions from the `IO` to the `X` monad to
|
||||||
|
allow them to look up information from `X`, like the screen
|
||||||
|
width. Existing configurations may need to use `io` from
|
||||||
|
`XMonad.Core` or `liftIO` from `Control.Monad.IO.Class` in
|
||||||
|
order to lift any existing `IO StatusBarConfig` values into
|
||||||
|
`X StatusBarConfig` values.
|
||||||
|
|
||||||
|
* `XMonad.Prompt`
|
||||||
|
|
||||||
|
- Added an additional `XPConfig` argument to `historyCompletion` and
|
||||||
|
`historyCompletionP`. Calls along the lines of `historyCompletionP
|
||||||
|
myFunc` should be changed to `historyCompletionP myConf myFunc`.
|
||||||
|
If not `myConf` is lying around, `def` can be used instead.
|
||||||
|
|
||||||
|
* `XMonad.Actions.GridSelect`
|
||||||
|
|
||||||
|
- Added the `gs_cancelOnEmptyClick` field to `GSConfig`, which makes
|
||||||
|
mouse clicks into "empty space" cancel the current grid-select.
|
||||||
|
Users explicitly defining their own `GSConfig` record will have to
|
||||||
|
add this to their definitions. Additionally, the field defaults to
|
||||||
|
`True`—to retain the old behaviour, set it to `False`.
|
||||||
|
|
||||||
|
### New Modules
|
||||||
|
|
||||||
|
* `XMonad.Actions.Profiles`
|
||||||
|
|
||||||
|
- Group workspaces by similarity. Useful when one has lots
|
||||||
|
of workspaces and uses only a couple per unit of work.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.FloatConfigureReq`
|
||||||
|
|
||||||
|
- Customize handling of floating windows' move/resize/restack requests
|
||||||
|
(ConfigureRequest). Useful as a workaround for some misbehaving client
|
||||||
|
applications (Steam, rxvt-unicode, anything that tries to restore
|
||||||
|
absolute position of floats).
|
||||||
|
|
||||||
|
* `XMonad.Layout.Columns`
|
||||||
|
|
||||||
|
- Organize windows in columns. This layout allows to move/resize windows in
|
||||||
|
every directions.
|
||||||
|
|
||||||
|
* `XMonad.Prompt.WindowBringer`
|
||||||
|
|
||||||
|
- Added `copyMenu`, a convenient way to copy a window to the current workspace.
|
||||||
|
|
||||||
|
### Bug Fixes and Minor Changes
|
||||||
|
|
||||||
|
* Fix build-with-cabal.sh when XDG_CONFIG_HOME is defined.
|
||||||
|
|
||||||
|
* `XMonad.Util.EZConfig`
|
||||||
|
|
||||||
|
- Fixed `checkKeymap` warning that all keybindings are duplicates.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.ManageHelpers`
|
||||||
|
|
||||||
|
- Added `isNotification` predicate to check for windows with
|
||||||
|
`_NET_WM_WINDOW_TYPE` property of `_NET_WM_WINDOW_TYPE_NOTIFICATION`.
|
||||||
|
|
||||||
|
* `XMonad.Prompt.OrgMode`
|
||||||
|
|
||||||
|
- Added `HH:MM-HH:MM` and `HH:MM+HH` syntax to specify time spans.
|
||||||
|
|
||||||
|
* `XMonad.Prompt`
|
||||||
|
|
||||||
|
- The history file is not extraneously read and written anymore if
|
||||||
|
the `historySize` is set to 0.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.EwmhDesktops`
|
||||||
|
|
||||||
|
- Requests for unmanaged windows no longer cause a refresh. This avoids
|
||||||
|
flicker and also fixes disappearing menus in the Steam client and
|
||||||
|
possibly a few other client applications.
|
||||||
|
|
||||||
|
(See also `XMonad.Hooks.FloatConfigureReq` and/or `XMonad.Util.Hacks`
|
||||||
|
for additional Steam client workarounds.)
|
||||||
|
|
||||||
|
* `XMonad.Actions.Submap`
|
||||||
|
|
||||||
|
- Added `visualSubmapSorted` to enable sorting of the keymap
|
||||||
|
descriptions.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.ScreenCorners`
|
||||||
|
|
||||||
|
- Added screen edge support with `SCTop`, `SCBottom`, `SCLeft` and
|
||||||
|
`SCRight`. Now both corners and edges are supported.
|
||||||
|
|
||||||
|
* `XMonad.Actions.WindowNavigation`
|
||||||
|
|
||||||
|
- Improve navigation in presence of floating windows.
|
||||||
|
- Handle window switching when in `Full` layout.
|
||||||
|
|
||||||
|
### Other changes
|
||||||
|
|
||||||
|
## 0.18.0 (February 3, 2024)
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
* Deprecated `XMonad.Layout.Cross` due to bitrot; refer to
|
||||||
|
`XMonad.Layout.Circle` and `XMonad.Layout.ThreeColumns` for
|
||||||
|
alternatives.
|
||||||
|
|
||||||
|
* Deprecated the `XMonad.Layout.StateFull` module and
|
||||||
|
`XMonad.Layout.TrackFloating.(t|T)rackFloating` in favour of
|
||||||
|
`XMonad.Layout.FocusTracking`.
|
||||||
|
|
||||||
|
* Dropped support for GHC 8.4.
|
||||||
|
|
||||||
|
* `XMonad.Util.ExclusiveScratchpads`
|
||||||
|
|
||||||
|
- Deprecated the module in favour of the (new) exclusive scratchpad
|
||||||
|
functionality of `XMonad.Util.NamedScratchpad`.
|
||||||
|
|
||||||
|
* `XMonad.Actions.CycleWorkspaceByScreen`
|
||||||
|
|
||||||
|
- The type of `repeatableAction` has changed, and it's deprecated in
|
||||||
|
favour of `X.A.Repeatable.repeatable`.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.DynamicProperty`
|
||||||
|
|
||||||
|
- Deprecated the module in favour of the more aptly named
|
||||||
|
`XMonad.Hooks.OnPropertyChange`.
|
||||||
|
|
||||||
|
* `XMonad.Util.Scratchpad`:
|
||||||
|
|
||||||
|
- Deprecated the module; use `XMonad.Util.NamedScratchpad` instead.
|
||||||
|
|
||||||
|
* `XMonad.Actions.Navigation2D`
|
||||||
|
|
||||||
|
- Removed deprecated function `hybridNavigation`.
|
||||||
|
|
||||||
|
* `XMonad.Layout.Spacing`
|
||||||
|
|
||||||
|
- Removed deprecated functions `SpacingWithEdge`, `SmartSpacing`,
|
||||||
|
`SmartSpacingWithEdge`, `ModifySpacing`, `setSpacing`, and
|
||||||
|
`incSpacing`.
|
||||||
|
|
||||||
|
* `XMonad.Actions.MessageFeedback`
|
||||||
|
|
||||||
|
- Removed deprecated functions `send`, `sendSM`, `sendSM_`,
|
||||||
|
`tryInOrder`, `tryInOrder_`, `tryMessage`, and `tryMessage_`.
|
||||||
|
|
||||||
|
* `XMonad.Prompt.Window`
|
||||||
|
|
||||||
|
- Removed deprecated functions `windowPromptGoto`,
|
||||||
|
`windowPromptBring`, and `windowPromptBringCopy`.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.ICCCMFocus`
|
||||||
|
|
||||||
|
- Removed deprecated module. This was merged into xmonad.
|
||||||
|
|
||||||
|
* `XMonad.Layout.LayoutBuilderP`
|
||||||
|
|
||||||
|
- Removed deprecated module; use `XMonad.Layout.LayoutBuilder`
|
||||||
|
instead.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.RestoreMinimized`
|
||||||
|
|
||||||
|
- Removed deprecated module; use `XMonad.Hooks.Minimize` instead.
|
||||||
|
|
||||||
|
* `XMonad.Layout.Named`
|
||||||
|
|
||||||
|
- Deprecated the entire module, use `XMonad.Layout.Renamed` (which newly
|
||||||
|
provides `named` for convenience) instead.
|
||||||
|
|
||||||
|
* `XMonad.Actions.SinkAll`
|
||||||
|
|
||||||
|
- Deprecated the entire module, use `XMonad.Actions.WithAll`
|
||||||
|
instead.
|
||||||
|
|
||||||
|
* `XMonad.Layout.Circle`:
|
||||||
|
|
||||||
|
- Deprecated the entire module, use the `circle` function from
|
||||||
|
`XMonad.Layout.CircleEx` instead.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.EwmhDesktops`
|
||||||
|
|
||||||
|
- `_NET_CLIENT_LIST_STACKING` puts windows in the current workspace at the
|
||||||
|
top in bottom-to-top order, followed by visible workspaces, followed by
|
||||||
|
invisible workspaces. Within visible and invisible groups, workspaces are
|
||||||
|
ordered lexicographically, as before. Currently focused window will
|
||||||
|
always be the topmost, meaning the last in the list.
|
||||||
|
|
||||||
|
* `XMonad.Util.NamedScratchpad`
|
||||||
|
|
||||||
|
- Added `nsSingleScratchpadPerWorkspace`—a logHook to allow only one
|
||||||
|
active scratchpad per workspace.
|
||||||
|
|
||||||
|
* `XMonad.Util.EZConfig`
|
||||||
|
|
||||||
|
- The function `readKeySequence` now returns a non-empty list if it
|
||||||
|
succeeded.
|
||||||
|
|
||||||
|
* Deprecate `XMonad.Util.Ungrab`; it was moved to `XMonad.Operations`
|
||||||
|
in core.
|
||||||
|
|
||||||
|
### New Modules
|
||||||
|
|
||||||
|
* `XMonad.Layout.CenterMainFluid`
|
||||||
|
- A three column layout with main column in the center and two stack
|
||||||
|
column surrounding it. Master window will be on center column and
|
||||||
|
spaces on the sides are reserved.
|
||||||
|
|
||||||
|
* `XMonad.Layout.FocusTracking`.
|
||||||
|
|
||||||
|
- Replaces `X.L.StateFull` and half of `X.L.TrackFloating`.
|
||||||
|
|
||||||
|
* `XMonad.Actions.MostRecentlyUsed`
|
||||||
|
|
||||||
|
- Tab through windows by recency of use. Based on the Alt+Tab behaviour
|
||||||
|
common outside of xmonad.
|
||||||
|
|
||||||
|
* `XMonad.Util.History`
|
||||||
|
|
||||||
|
- Track history in *O(log n)* time. Provides `History`, a variation on a
|
||||||
|
LIFO stack with a uniqueness property. In order to achieve the desired
|
||||||
|
asymptotics, the data type is implemented as an ordered Map.
|
||||||
|
|
||||||
|
* `XMonad.Actions.Repeatable`
|
||||||
|
|
||||||
|
- Actions you'd like to repeat. Factors out the shared logic of
|
||||||
|
`X.A.CycleRecentWS`, `X.A.CycleWorkspaceByScreen` and `X.A.CycleWindows`.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.OnPropertyChange`:
|
||||||
|
|
||||||
|
- A new module replicating the functionality of
|
||||||
|
`XMonad.Hooks.DynamicProperty`, but with more discoverable names.
|
||||||
|
|
||||||
|
* `XMonad.Actions.ToggleFullFloat`:
|
||||||
|
|
||||||
|
- Fullscreen (float) a window while remembering its original state.
|
||||||
|
There's both an action to be bound to a key, and hooks that plug into
|
||||||
|
`XMonad.Hooks.EwmhDesktops`.
|
||||||
|
|
||||||
|
* `XMonad.Layout.CircleEx`:
|
||||||
|
|
||||||
|
- A new window layout, similar to X.L.Circle, but with more
|
||||||
|
possibilities for customisation.
|
||||||
|
|
||||||
|
* `XMonad.Layout.DecorationEx`:
|
||||||
|
|
||||||
|
- A new, more extensible, mechanism for window decorations, and some
|
||||||
|
standard types of decorations, including usual bar on top of window,
|
||||||
|
tabbed decorations and dwm-like decorations.
|
||||||
|
|
||||||
|
### Bug Fixes and Minor Changes
|
||||||
|
|
||||||
|
* `XMonad.Layout.Magnifier`
|
||||||
|
|
||||||
|
- Added `magnifyxy` to allow for different magnification in the
|
||||||
|
horizontal and vertical directions. Added `magnifierxy`,
|
||||||
|
`magnifierxy'`, `magnifierxyOff`, and `magnifierxyOff'` as
|
||||||
|
particular combinators.
|
||||||
|
|
||||||
|
* `XMonad.Util.Loggers`
|
||||||
|
|
||||||
|
- Added `logClassname`, `logClassnames`, `logClassnames'`,
|
||||||
|
`logClassnameOnScreen`, `logClassnamesOnScreen`, `logClassnamesOnScreen'`,
|
||||||
|
and `ClassnamesFormat`. These are all equivalents of their `Title`
|
||||||
|
counterparts, allowing logging the window classname instead.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.StatusBar.PP`
|
||||||
|
|
||||||
|
- `dynamicLogString` now forces its result and produces an error string if
|
||||||
|
it throws an exception. Use `dynamicLogString'` if for some reason you
|
||||||
|
need the old behavior.
|
||||||
|
|
||||||
|
* `XMonad.Util.EZConfig`
|
||||||
|
|
||||||
|
- Added `remapKeysP`, which remaps keybindings from one binding to
|
||||||
|
another.
|
||||||
|
|
||||||
|
- Made `additionalKeys{,P}`, `removeKeys{,P}`, `remapKeysP`, and
|
||||||
|
`{additional,remove}MouseBindings` `infixl 4` so they can more easily
|
||||||
|
be concatenated with `(++)`.
|
||||||
|
|
||||||
|
* `XMonad.Util.NamedScratchpad`
|
||||||
|
|
||||||
|
- Added `addExclusives`, `resetFocusedNSP`, `setNoexclusive`,
|
||||||
|
`resizeNoexclusive`, and `floatMoveNoexclusive` in order to augment
|
||||||
|
named scratchpads with the exclusive scratchpad functionality of
|
||||||
|
`XMonad.Util.ExclusiveScratchpads`.
|
||||||
|
|
||||||
|
* `XMonad.Layout.BorderResize`
|
||||||
|
|
||||||
|
- Added `borderResizeNear` as a variant of `borderResize` that can
|
||||||
|
control how many pixels near a border resizing still works.
|
||||||
|
|
||||||
|
* `XMonad.Util.Run`
|
||||||
|
|
||||||
|
- It is now ensured that all arguments of `execute` and `eval` are
|
||||||
|
quoted. Likewise, `executeNoQuote` is added as a version of
|
||||||
|
`execute` that does not do that.
|
||||||
|
|
||||||
|
- Added `findFile` as a shorthand to call `find-file`.
|
||||||
|
|
||||||
|
- Added `list` and `saveExcursion` to the list of Emacs commands.
|
||||||
|
|
||||||
|
- Added `toList` to easily lift a `String` to an `X Input`.
|
||||||
|
|
||||||
|
- Added `>&&>` and `>||>` to glue together different inputs.
|
||||||
|
|
||||||
|
* `XMonad.Util.Parser`
|
||||||
|
|
||||||
|
- Added the `gather`, `count`, `between`, `option`, `optionally`,
|
||||||
|
`skipMany`, `skipMany1`, `chainr`, `chainr1`, `chainl`, `chainl1`,
|
||||||
|
and `manyTill` functions, in order to achieve feature parity with
|
||||||
|
`Text.ParserCombinators.ReadP`.
|
||||||
|
|
||||||
|
* `XMonad.Actions.FloatKeys`
|
||||||
|
|
||||||
|
- Added `directionMoveWindow` and `directionMoveWindow` as more
|
||||||
|
alternatives to the existing functions.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.InsertPosition`
|
||||||
|
|
||||||
|
- Added `setupInsertPosition` as a combinator alternative to
|
||||||
|
`insertPosition`.
|
||||||
|
|
||||||
|
* `XMonad.Actions.Navigation2D`
|
||||||
|
|
||||||
|
- Added `sideNavigation` as a fallback to the default tiling strategy,
|
||||||
|
in case `lineNavigation` can't find a window. This benefits
|
||||||
|
especially users who use `XMonad.Layout.Spacing`.
|
||||||
|
|
||||||
|
* `XMonad.Prompt.OrgMode`
|
||||||
|
|
||||||
|
- Added `orgPromptRefile` and `orgPromptRefileTo` for interactive
|
||||||
|
and targeted refiling of the entered note into some existing tree
|
||||||
|
of headings, respectively.
|
||||||
|
|
||||||
|
- Allowed the time specification in `HHMM` format.
|
||||||
|
|
||||||
|
* `XMonad.Actions.Search`
|
||||||
|
|
||||||
|
- Added `aur`, `flora`, `ncatlab`, `protondb`, `rosettacode`, `sourcehut`,
|
||||||
|
`steam`, `voidpgks_x86_64`, `voidpgks_x86_64_musl`, `arXiv`,
|
||||||
|
`clojureDocs`, `cratesIo`, `rustStd`, `noogle`, `nixos`, `homeManager`,
|
||||||
|
and `zbmath` search engines.
|
||||||
|
|
||||||
|
* `XMonad.Layout.ResizableThreeColumns`
|
||||||
|
|
||||||
|
- Fixed an issue where the bottom right window would not respond to
|
||||||
|
`MirrorShrink` and `MirrorExpand` messages.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.EwmhDesktops`
|
||||||
|
|
||||||
|
- Added `disableEwmhManageDesktopViewport` to avoid setting the
|
||||||
|
`_NET_DESKTOP_VIEWPORT` property, as it can lead to issues with
|
||||||
|
some status bars (see this
|
||||||
|
[polybar issue](https://github.com/polybar/polybar/issues/2603)).
|
||||||
|
|
||||||
|
- Added `setEwmhFullscreenHooks` to override the default fullfloat/sink
|
||||||
|
behaviour of `_NET_WM_STATE_FULLSCREEN` requests. See also
|
||||||
|
`XMonad.Actions.ToggleFullFloat` for a float-restoring implementation of
|
||||||
|
fullscreening.
|
||||||
|
|
||||||
|
- Added `ewmhDesktops(Maybe)ManageHook` that places windows in their
|
||||||
|
preferred workspaces. This is useful when restoring a browser session
|
||||||
|
after a restart.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.StatusBar`
|
||||||
|
|
||||||
|
- Added `startAllStatusBars` to start the configured status bars.
|
||||||
|
|
||||||
|
* `XMonad.Util.NamedActions`
|
||||||
|
|
||||||
|
- Changed `addDescrKeys` and `addDescrKeys'` to not discard the
|
||||||
|
keybindings in the current config.
|
||||||
|
|
||||||
|
* `XMonad.Prompt`
|
||||||
|
|
||||||
|
- The `emacsLikeXPKeymap` and `vimLikeXPKeymap` keymaps now treat
|
||||||
|
`C-m` the same as `Return`.
|
||||||
|
|
||||||
|
- Added `prevCompletionKey` to `XPConfig`, facilitating the ability
|
||||||
|
to cycle through the completions backwards. This is bound to
|
||||||
|
`S-<TAB>` by default.
|
||||||
|
|
||||||
|
- The `vimLikeXPKeymap` now accepts the prompt upon pressing enter
|
||||||
|
in normal mode.
|
||||||
|
|
||||||
|
* `XMonad.Actions.Prefix`
|
||||||
|
|
||||||
|
- Added `orIfPrefixed`, a combinator to decide upon an action based
|
||||||
|
on whether any prefix argument was given.
|
||||||
|
|
||||||
|
* `XMonad.Actions.WorkspaceNames`
|
||||||
|
|
||||||
|
- Enabled prompt completion (from history) in `renameWorkspace`.
|
||||||
|
|
||||||
|
* `XMonad.Prompt.Pass`
|
||||||
|
|
||||||
|
- Added `passOTPTypePrompt` to type out one-time-passwords via
|
||||||
|
`xdotool`.
|
||||||
|
|
||||||
|
* `XMonad.Util.Stack`
|
||||||
|
|
||||||
|
- Added `zipperFocusedAtFirstOf` to differentiate two lists into a
|
||||||
|
zipper.
|
||||||
|
|
||||||
|
## 0.17.1 (September 3, 2022)
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
* `XMonad.Util.EZConfig`
|
||||||
|
|
||||||
|
- The functions `parseKey`, `parseKeyCombo`, and `parseKeySequence`
|
||||||
|
now return a `Parser` (from `XMonad.Util.Parser`) instead of a
|
||||||
|
`ReadP`.
|
||||||
|
|
||||||
|
* `XMonad.Config.{Arossato,Dmwit,Droundy,Monad,Prime,Saegesser,Sjanssen}`
|
||||||
|
|
||||||
|
- Deprecated all of these modules. The user-specific configuration
|
||||||
|
modules may still be found [on the
|
||||||
|
website](https://xmonad.org/configurations.html)
|
||||||
|
|
||||||
|
* `XMonad.Util.NamedScratchpad`
|
||||||
|
|
||||||
|
- Scratchpads are now only based on the argument given to
|
||||||
|
`namedScratchpadManageHook`; all other scratchpad arguments are,
|
||||||
|
while still present, ignored. Users passing all of their
|
||||||
|
scratchpads to functions like `namedScratchpadAction` (as is shown
|
||||||
|
in the module's documentation) should _not_ notice any difference
|
||||||
|
in behaviour.
|
||||||
|
|
||||||
|
* `XMonad.Util.DynamicScratchpads`
|
||||||
|
|
||||||
|
- Deprecated the module; use the new dynamic scratchpad
|
||||||
|
functionality of `XMonad.Util.NamedScratchpad` instead.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.UrgencyHook`
|
||||||
|
|
||||||
|
- Deprecated `urgencyConfig`; use `def` from the new `Default`
|
||||||
|
instance of `UrgencyConfig` instead.
|
||||||
|
|
||||||
|
### New Modules
|
||||||
|
|
||||||
|
* `XMonad.Actions.PerLayoutKeys`
|
||||||
|
|
||||||
|
Customizes a keybinding on a per-layout basis. Based on PerWorkspaceKeys.
|
||||||
|
|
||||||
|
* `XMonad.Layout.CenteredIfSingle`
|
||||||
|
|
||||||
|
Layout modifier that, if only a single window is on screen, places that window
|
||||||
|
in the middle of the screen.
|
||||||
|
|
||||||
|
* `XMonad.Util.ActionQueue`
|
||||||
|
|
||||||
|
Put XMonad actions in the queue to be executed every time the
|
||||||
|
`logHook` (or, alternatively, a hook of your choice) runs.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.BorderPerWindow`
|
||||||
|
|
||||||
|
While XMonad provides config to set all window borders at the same
|
||||||
|
width, this extension lets user set border width for a specific window
|
||||||
|
using a ManageHook.
|
||||||
|
|
||||||
|
* `XMonad.Util.Parser`
|
||||||
|
|
||||||
|
A wrapper around the 'ReadP' parser combinator, providing behaviour
|
||||||
|
that's closer to the more popular parser combinator libraries.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.StatusBar.WorkspaceScreen`
|
||||||
|
|
||||||
|
In multi-head setup, it might be useful to have screen information of the
|
||||||
|
visible workspaces combined with the workspace name, for example in a status
|
||||||
|
bar. This module provides utility functions to do just that.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.ShowWName`
|
||||||
|
|
||||||
|
Flashes the name of the current workspace when switching to it.
|
||||||
|
Like `XMonad.Layout.ShowWName`, but as a logHook.
|
||||||
|
|
||||||
|
* `XMonad.Actions.RepeatAction`
|
||||||
|
|
||||||
|
A module for adding a keybinding to repeat the last action, similar
|
||||||
|
to Vim's `.` or Emacs's `dot-mode`.
|
||||||
|
|
||||||
|
* `XMonad.Util.Grab`
|
||||||
|
|
||||||
|
Utilities for making grabbing and ungrabbing keys more convenient.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.Modal`
|
||||||
|
|
||||||
|
This module implements modal keybindings for xmonad.
|
||||||
|
|
||||||
|
* `XMonad.Layout.SideBorderDecoration`
|
||||||
|
|
||||||
|
This module allows for having a configurable border position around
|
||||||
|
windows; i.e., it can move the border to either cardinal direction.
|
||||||
|
|
||||||
|
### Bug Fixes and Minor Changes
|
||||||
|
|
||||||
|
* `XMonad.Prompt.Pass`
|
||||||
|
|
||||||
|
- Added new versions of the `pass` functions that allow user-specified
|
||||||
|
prompts.
|
||||||
|
|
||||||
|
* `XMonad.Prompt.AppendFile`
|
||||||
|
|
||||||
|
- Use `XMonad.Prelude.mkAbsolutePath` to force names to be relative to the
|
||||||
|
home directory and support `~/` prefixes.
|
||||||
|
|
||||||
|
* `XMonad.Prompt.OrgMode`
|
||||||
|
|
||||||
|
- Fixed the date parsing issue such that entries with a format of
|
||||||
|
`todo +d 12 02 2024` work.
|
||||||
|
|
||||||
|
- Added the ability to specify alphabetic (`#A`, `#B`, and `#C`)
|
||||||
|
[priorities](https://orgmode.org/manual/Priorities.html) at the end of
|
||||||
|
the input note.
|
||||||
|
|
||||||
|
* `XMonad.Prompt.Unicode`
|
||||||
|
|
||||||
|
- Fixed the display of non-ASCII characters in the description of Unicode
|
||||||
|
characters
|
||||||
|
|
||||||
|
* `XMonad.Prompt`
|
||||||
|
|
||||||
|
- Added `transposeChars` to interchange the characters around the
|
||||||
|
point and bound it to `C-t` in the Emacs XPKeymaps.
|
||||||
|
|
||||||
|
- Added xft-based font fallback support. This may be used by
|
||||||
|
appending other fonts to the given string:
|
||||||
|
`xft:iosevka-11,FontAwesome-9`. Note that this requires
|
||||||
|
`xmonad-contrib` to be compiled with `X11-xft` version 0.3.4 or
|
||||||
|
higher.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.WindowSwallowing`
|
||||||
|
|
||||||
|
- Fixed windows getting lost when used in conjunction with
|
||||||
|
`smartBorders` and a single window.
|
||||||
|
|
||||||
|
- No longer needs `pstree` to detect child/parent relationships.
|
||||||
|
|
||||||
|
- Fixed some false positives in child/parent relationship detection.
|
||||||
|
|
||||||
|
* `XMonad.Actions.SpawnOn`
|
||||||
|
|
||||||
|
- Fixed parsing of `/proc/*/stat` to correctly handle complex process names.
|
||||||
|
|
||||||
|
* `XMonad.Util.EZConfig`
|
||||||
|
|
||||||
|
- Added support for Modifier Keys `KeySym`s for Emacs-like `additionalKeysP`.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.ManageHelpers`
|
||||||
|
|
||||||
|
- Flipped how `(^?)`, `(~?)`, and `($?)` work to more accurately
|
||||||
|
reflect how one uses these operators.
|
||||||
|
|
||||||
|
- Added `isMinimized`
|
||||||
|
|
||||||
|
* `XMonad.Actions.WindowNavigation`
|
||||||
|
|
||||||
|
- Fixed navigation getting "stuck" in certain situations for
|
||||||
|
widescreen resolutions.
|
||||||
|
|
||||||
|
* `XMonad.Layout.BinarySpacePartition`
|
||||||
|
|
||||||
|
- Hidden windows are now ignored by the layout so that hidden windows in
|
||||||
|
the stack don't offset position calculations in the layout.
|
||||||
|
|
||||||
|
* `XMonad.Layout.MagicFocus`
|
||||||
|
|
||||||
|
- The focused window will always be at the master area in the stack being
|
||||||
|
passed onto the modified layout, even when focus leaves the workspace
|
||||||
|
using the modified layout.
|
||||||
|
|
||||||
|
* `XMonad.Actions.TreeSelect`
|
||||||
|
|
||||||
|
- Added xft-based font fallback support. This may be used by
|
||||||
|
appending other fonts to the given string:
|
||||||
|
`xft:iosevka-11,FontAwesome-9`. Note that this requires
|
||||||
|
`xmonad-contrib` to be compiled with `X11-xft` version 0.3.4 or
|
||||||
|
higher.
|
||||||
|
|
||||||
|
* `XMonad.Actions.FloatKeys`
|
||||||
|
|
||||||
|
- Changed type signature of `keysMoveWindow` from `D -> Window -> X ()`
|
||||||
|
to `ChangeDim -> Window -> X ()` to allow negative numbers without compiler warnings.
|
||||||
|
|
||||||
|
* `XMonad.Util.Hacks`
|
||||||
|
|
||||||
|
- Added `trayerPaddingXmobarEventHook` (plus generic variants for other
|
||||||
|
trays/panels) to communicate trayer resize events to XMobar so that
|
||||||
|
padding space may be reserved on xmobar for the tray. Requires `xmobar`
|
||||||
|
version 0.40 or higher.
|
||||||
|
|
||||||
|
* `XMonad.Layout.VoidBorders`
|
||||||
|
|
||||||
|
- Added new layout modifier `normalBorders` which can be used for
|
||||||
|
resetting borders back in layouts where you want borders after calling
|
||||||
|
`voidBorders`.
|
||||||
|
|
||||||
|
* `XMonad.Prelude`
|
||||||
|
|
||||||
|
- Added `keymaskToString` and `keyToString` to show a key mask and a
|
||||||
|
key in the style of `XMonad.Util.EZConfig`.
|
||||||
|
|
||||||
|
- Added `WindowScreen`, which is a type synonym for the specialized `Screen`
|
||||||
|
type, that results from the `WindowSet` definition in `XMonad.Core`.
|
||||||
|
|
||||||
|
- Modified `mkAbsolutePath` to support a leading environment variable, so
|
||||||
|
things like `$HOME/NOTES` work. If you want more general environment
|
||||||
|
variable support, comment on [this
|
||||||
|
PR](https://github.com/xmonad/xmonad-contrib/pull/744)
|
||||||
|
|
||||||
|
* `XMonad.Util.XUtils`
|
||||||
|
|
||||||
|
- Added `withSimpleWindow`, `showSimpleWindow`, `WindowConfig`, and
|
||||||
|
`WindowRect` in order to simplify the handling of simple popup
|
||||||
|
windows.
|
||||||
|
|
||||||
|
* `XMonad.Actions.Submap`
|
||||||
|
|
||||||
|
- Added `visualSubmap` to visualise the available keys and their
|
||||||
|
actions when inside a submap.
|
||||||
|
|
||||||
|
* `XMonad.Prompt`, `XMonad.Actions.TreeSelect`, `XMonad.Actions.GridSelect`
|
||||||
|
|
||||||
|
- Key bindings now behave similarly to xmonad core:
|
||||||
|
State of mouse buttons and XKB layout groups is ignored.
|
||||||
|
Translation of key codes to symbols ignores modifiers, so `Shift-Tab` is
|
||||||
|
now just `(shiftMap, xK_Tab)` instead of `(shiftMap, xK_ISO_Left_Tab)`.
|
||||||
|
|
||||||
|
* `XMonad.Util.NamedScratchpad`
|
||||||
|
|
||||||
|
- Added support for dynamic scratchpads in the form of
|
||||||
|
`dynamicNSPAction` and `toggleDynamicNSP`.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.EwmhDesktops`
|
||||||
|
|
||||||
|
- Added support for `_NET_DESKTOP_VIEWPORT`, which is required by
|
||||||
|
some status bars.
|
||||||
|
|
||||||
|
* `XMonad.Util.Run`
|
||||||
|
|
||||||
|
- Added an EDSL—particularly geared towards programs like terminals
|
||||||
|
or Emacs—to spawn processes from XMonad in a compositional way.
|
||||||
|
|
||||||
|
* `XMonad.Hooks.UrgencyHook`
|
||||||
|
|
||||||
|
- Added a `Default` instance for `UrgencyConfig` and `DzenUrgencyHook`.
|
||||||
|
|
||||||
|
### Other changes
|
||||||
|
|
||||||
|
* Migrated the sample build scripts from the deprecated `xmonad-testing` repo to
|
||||||
|
`scripts/build`. This will be followed by a documentation update in the `xmonad`
|
||||||
|
repo.
|
||||||
|
|
||||||
## 0.17.0 (October 27, 2021)
|
## 0.17.0 (October 27, 2021)
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
@ -549,6 +1266,11 @@
|
|||||||
ones) on the focused workspace, as well as `logTitlesOnScreen` as
|
ones) on the focused workspace, as well as `logTitlesOnScreen` as
|
||||||
a screen-specific variant thereof.
|
a screen-specific variant thereof.
|
||||||
|
|
||||||
|
- Added `logTitles'` and `logTitleOnScreen'`. These act like
|
||||||
|
`logTitles` and `logTitlesOnScreen` but use a record as an input
|
||||||
|
to enable logging for more window types. For example, currently
|
||||||
|
urgent windows are additionally supported.
|
||||||
|
|
||||||
* `XMonad.Layout.Minimize`
|
* `XMonad.Layout.Minimize`
|
||||||
|
|
||||||
- Export `Minimize` type constructor.
|
- Export `Minimize` type constructor.
|
||||||
@ -772,6 +1494,9 @@
|
|||||||
- Fixed a system freeze when using `X.A.CopyWindow.copy` in
|
- Fixed a system freeze when using `X.A.CopyWindow.copy` in
|
||||||
combination with `removeWorkspace`.
|
combination with `removeWorkspace`.
|
||||||
|
|
||||||
|
- `withWorkspace` now honors the users `searchPredicate`, for
|
||||||
|
example `fuzzyMatch` from `Prompt.FuzzyMatch`.
|
||||||
|
|
||||||
## 0.16
|
## 0.16
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
@ -1511,8 +2236,8 @@
|
|||||||
|
|
||||||
* `XMonad.Prompt.Pass`
|
* `XMonad.Prompt.Pass`
|
||||||
|
|
||||||
This module provides 3 `XMonad.Prompt`s to ease passwords
|
This module provides 3 `XMonad.Prompt`s to ease passwords manipulation
|
||||||
manipulation (generate, read, remove) via [pass][].
|
(generate, read, remove) via [pass](http://www.passwordstore.org/).
|
||||||
|
|
||||||
* `XMonad.Util.RemoteWindows`
|
* `XMonad.Util.RemoteWindows`
|
||||||
|
|
||||||
@ -1588,5 +2313,3 @@
|
|||||||
## See Also
|
## See Also
|
||||||
|
|
||||||
<https://wiki.haskell.org/Xmonad/Notable_changes_since_0.8>
|
<https://wiki.haskell.org/Xmonad/Notable_changes_since_0.8>
|
||||||
|
|
||||||
[pass]: http://www.passwordstore.org/
|
|
||||||
|
106
NIX.md
Normal file
106
NIX.md
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# `nix` integration for XMonad
|
||||||
|
|
||||||
|
## Customizing the `nix-shell`
|
||||||
|
|
||||||
|
It's possible to use a file `develop.nix` to customize the `devShell`
|
||||||
|
provided by the flake. This is useful if e.g. you want to have the
|
||||||
|
`haskell-language-server` or other developer tools in the shell properly
|
||||||
|
configured (correct GHC versions, and the like).
|
||||||
|
|
||||||
|
Here is an example `develop.nix` for `haskell-language-server`:
|
||||||
|
|
||||||
|
``` nix
|
||||||
|
pkgs: devInputs: devInputs // {
|
||||||
|
nativeBuildInputs = with pkgs.haskellPackages;
|
||||||
|
[ cabal-install hlint ghcid ormolu implicit-hie haskell-language-server ];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Selecting a Compiler
|
||||||
|
|
||||||
|
A `comp.nix` file can be used to set the compiler used for `nix build` etc. E.g.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ compiler = "ghc924"; }
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that you must `git add comp.nix`, or it will be invisible to the flake.
|
||||||
|
|
||||||
|
There is also a `prefix` option (see documentation below) but it doesn't really
|
||||||
|
work in this context, since the xmonad flakes don't see the effects of your
|
||||||
|
system overlays. Instead try the `--override-input` flag, e.g.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ nix develop . --override-input nixpkgs 'github:NixOS/nixpkgs/nixos-unstable'
|
||||||
|
```
|
||||||
|
|
||||||
|
## NixOS Modules
|
||||||
|
|
||||||
|
The core and contrib flakes provide NixOS configuration modules.
|
||||||
|
You can bring them into your system flake like so:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = github:NixOS/nixpkgs/nixos-<version>;
|
||||||
|
# The xmonad-contrib flake depends upon and re-exports from the xmonad
|
||||||
|
# flake. As such, you don't need to use the latter directly. If you wish to
|
||||||
|
# use /only/ the xmonad flake, you should beware that the version of
|
||||||
|
# contrib you get from nixpkgs might not build against it.
|
||||||
|
xmonad-contrib.url = github:xmonad/xmonad-contrib;
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, xmonad-contrib }: {
|
||||||
|
nixosConfigurations.<hostname> = nixpkgs.lib.nixosSystem rec {
|
||||||
|
system = <system>;
|
||||||
|
# NixOS module composition is /not/ commutative; order matters.
|
||||||
|
# To avoid issues, add `xmonad-contrib.nixosModules` after your standard
|
||||||
|
# configuration, but before `modernise` or any module overlaying in a
|
||||||
|
# "prefix".
|
||||||
|
modules = [
|
||||||
|
./configuration.nix
|
||||||
|
./hardware-configuration.nix
|
||||||
|
<myMiscConfigModule>
|
||||||
|
] ++ xmonad-contrib.nixosModules ++ [
|
||||||
|
# `modernise` replaces the standard xmonad module and wrapper script
|
||||||
|
# with those from unstable. This is currently a necessary workaround to
|
||||||
|
# make Mod-q recompilation work.
|
||||||
|
xmonad-contrib.modernise.${system}
|
||||||
|
<myPrefixModule>
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that `<thing>` should be replaced with a user-supplied `thing`.
|
||||||
|
`<version>`, `<hostname>` and `<system>` are necessary, while
|
||||||
|
` <myMiscConfigModule>` and `<myPrefixModule>` are entirely optional.
|
||||||
|
|
||||||
|
Having brought in `xmonad-contrib.nixosModules`, you can then set the provided
|
||||||
|
options in your `configuration.nix` under `flake`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
services.xserver.windowManager.xmonad = {
|
||||||
|
enable = true;
|
||||||
|
enableContribAndExtras = true;
|
||||||
|
flake = {
|
||||||
|
enable = true;
|
||||||
|
# prefix = "unstable";
|
||||||
|
compiler = "ghc924";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
This will use core and contrib from git for your system xmonad, building your
|
||||||
|
config with the compiler of your choice.
|
||||||
|
|
||||||
|
With the flake enabled, the `xmonad.haskellPackages` option is not used
|
||||||
|
directly, and is instead set by the `flake.compiler` option. When `compiler` is
|
||||||
|
unset, the default `pkgs.haskellPackages` is used.
|
||||||
|
|
||||||
|
The `prefix` option is used if you wish to select your haskell packages from
|
||||||
|
within, e.g., unstable overlaid into `pkgs` as `pkgs.unstable`.
|
||||||
|
|
||||||
|
See the flakes themselves and nix flake documentation for full detail.
|
||||||
|
Additionally, a semi-walkthrough is available [here](https://tony-zorman.com/posts/xmonad-on-nixos.html).
|
39
README.md
39
README.md
@ -1,35 +1,20 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://xmonad.org/">
|
<a href="https://xmonad.org/"><img alt="XMonad logo" src="https://xmonad.org/images/logo-wrapped.svg" height=150></a>
|
||||||
<img alt="XMonad logo" src="https://xmonad.org/images/logo-wrapped.svg" height=150>
|
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://hackage.haskell.org/package/xmonad-contrib">
|
<a href="https://hackage.haskell.org/package/xmonad-contrib"><img alt="Hackage" src="https://img.shields.io/hackage/v/xmonad-contrib?logo=haskell"></a>
|
||||||
<img alt="Hackage" src="https://img.shields.io/hackage/v/xmonad-contrib?logo=haskell">
|
<a href="https://github.com/xmonad/xmonad-contrib/blob/readme/LICENSE"><img alt="License" src="https://img.shields.io/github/license/xmonad/xmonad-contrib"></a>
|
||||||
</a>
|
<a href="https://haskell.org/"><img alt="Made in Haskell" src="https://img.shields.io/badge/Made%20in-Haskell-%235e5086?logo=haskell"></a>
|
||||||
<a href="https://github.com/xmonad/xmonad-contrib/blob/readme/LICENSE">
|
|
||||||
<img alt="License" src="https://img.shields.io/github/license/xmonad/xmonad-contrib">
|
|
||||||
</a>
|
|
||||||
<a href="https://haskell.org/">
|
|
||||||
<img alt="Made in Haskell" src="https://img.shields.io/badge/Made%20in-Haskell-%235e5086?logo=haskell">
|
|
||||||
</a>
|
|
||||||
<br>
|
<br>
|
||||||
<a href="https://github.com/xmonad/xmonad-contrib/actions/workflows/stack.yml">
|
<a href="https://github.com/xmonad/xmonad-contrib/actions/workflows/stack.yml"><img alt="Stack" src="https://img.shields.io/github/actions/workflow/status/xmonad/xmonad-contrib/stack.yml?label=Stack&logo=githubactions&logoColor=white"></a>
|
||||||
<img alt="Stack" src="https://img.shields.io/github/workflow/status/xmonad/xmonad-contrib/Stack?label=Stack&logo=githubactions&logoColor=white">
|
<a href="https://github.com/xmonad/xmonad-contrib/actions/workflows/haskell-ci.yml"><img alt="Cabal" src="https://img.shields.io/github/actions/workflow/status/xmonad/xmonad-contrib/haskell-ci.yml?label=Cabal&logo=githubactions&logoColor=white"></a>
|
||||||
</a>
|
<a href="https://github.com/xmonad/xmonad-contrib/actions/workflows/nix.yml"><img alt="Nix" src="https://img.shields.io/github/actions/workflow/status/xmonad/xmonad-contrib/nix.yml?label=Nix&logo=githubactions&logoColor=white"></a>
|
||||||
<a href="https://github.com/xmonad/xmonad-contrib/actions/workflows/haskell-ci.yml">
|
|
||||||
<img alt="Cabal" src="https://img.shields.io/github/workflow/status/xmonad/xmonad-contrib/Haskell-CI?label=Cabal&logo=githubactions&logoColor=white">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/xmonad/xmonad-contrib/actions/workflows/nix.yml">
|
|
||||||
<img alt="Nix" src="https://img.shields.io/github/workflow/status/xmonad/xmonad-contrib/Nix?label=Nix&logo=githubactions&logoColor=white">
|
|
||||||
</a>
|
|
||||||
<br>
|
<br>
|
||||||
<a href="https://github.com/sponsors/xmonad">
|
<a href="https://github.com/sponsors/xmonad"><img alt="GitHub Sponsors" src="https://img.shields.io/github/sponsors/xmonad?label=GitHub%20Sponsors&logo=githubsponsors"></a>
|
||||||
<img alt="GitHub Sponsors" src="https://img.shields.io/github/sponsors/xmonad?label=GitHub%20Sponsors&logo=githubsponsors">
|
<a href="https://opencollective.com/xmonad"><img alt="Open Collective" src="https://img.shields.io/opencollective/all/xmonad?label=Open%20Collective&logo=opencollective"></a>
|
||||||
</a>
|
<br>
|
||||||
<a href="https://opencollective.com/xmonad">
|
<a href="https://web.libera.chat/#xmonad"><img alt="Chat on #xmonad@irc.libera.chat" src="https://img.shields.io/badge/%23%20chat-on%20libera-brightgreen"></a>
|
||||||
<img alt="Open Collective" src="https://img.shields.io/opencollective/all/xmonad?label=Open%20Collective&logo=opencollective">
|
<a href="https://matrix.to/#/#xmonad:matrix.org"><img alt="Chat on #xmonad:matrix.org" src="https://img.shields.io/matrix/xmonad:matrix.org?logo=matrix"></a>
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# xmonad-contrib
|
# xmonad-contrib
|
||||||
|
@ -24,7 +24,7 @@ import XMonad
|
|||||||
import Data.Time (NominalDiffTime, diffUTCTime, getCurrentTime)
|
import Data.Time (NominalDiffTime, diffUTCTime, getCurrentTime)
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.AfterDrag
|
-- > import XMonad.Actions.AfterDrag
|
||||||
--
|
--
|
||||||
|
@ -29,7 +29,7 @@ import System.Exit
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Hooks.ServerMode
|
-- > import XMonad.Hooks.ServerMode
|
||||||
-- > import XMonad.Actions.BluetileCommands
|
-- > import XMonad.Actions.BluetileCommands
|
||||||
|
@ -37,7 +37,7 @@ import XMonad.Prelude
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.Commands
|
-- > import XMonad.Actions.Commands
|
||||||
--
|
--
|
||||||
@ -57,7 +57,7 @@ import XMonad.Prelude
|
|||||||
-- bindings!)
|
-- bindings!)
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | Create a 'Data.Map.Map' from @String@s to xmonad actions from a
|
-- | Create a 'Data.Map.Map' from @String@s to xmonad actions from a
|
||||||
-- list of pairs.
|
-- list of pairs.
|
||||||
|
@ -26,7 +26,7 @@ import XMonad
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import qualified XMonad.Actions.ConstrainedResize as Sqr
|
-- > import qualified XMonad.Actions.ConstrainedResize as Sqr
|
||||||
--
|
--
|
||||||
@ -44,8 +44,8 @@ import XMonad
|
|||||||
|
|
||||||
-- | Resize (floating) window with optional aspect ratio constraints.
|
-- | Resize (floating) window with optional aspect ratio constraints.
|
||||||
mouseResizeWindow :: Window -> Bool -> X ()
|
mouseResizeWindow :: Window -> Bool -> X ()
|
||||||
mouseResizeWindow w c = whenX (isClient w) $ withDisplay $ \d -> do
|
mouseResizeWindow w c = whenX (isClient w) $ withDisplay $ \d ->
|
||||||
wa <- io $ getWindowAttributes d w
|
withWindowAttributes d w $ \wa -> do
|
||||||
sh <- io $ getWMNormalHints d w
|
sh <- io $ getWMNormalHints d w
|
||||||
io $ warpPointer d none w 0 0 0 0 (fromIntegral (wa_width wa)) (fromIntegral (wa_height wa))
|
io $ warpPointer d none w 0 0 0 0 (fromIntegral (wa_width wa)) (fromIntegral (wa_height wa))
|
||||||
mouseDrag (\ex ey -> do
|
mouseDrag (\ex ey -> do
|
||||||
|
@ -37,7 +37,7 @@ import qualified XMonad.StackSet as W
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
|
-- You can use this module with the following in your @xmonad.hs@ file:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.CopyWindow
|
-- > import XMonad.Actions.CopyWindow
|
||||||
--
|
--
|
||||||
@ -77,7 +77,7 @@ import qualified XMonad.StackSet as W
|
|||||||
-- > , ((modm .|. shiftMask, xK_v ), killAllOtherCopies) -- @@ Toggle window state back
|
-- > , ((modm .|. shiftMask, xK_v ), killAllOtherCopies) -- @@ Toggle window state back
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- $logHook
|
-- $logHook
|
||||||
--
|
--
|
||||||
@ -114,10 +114,10 @@ copyWindow w n = copy'
|
|||||||
where copy' s = if n `W.tagMember` s
|
where copy' s = if n `W.tagMember` s
|
||||||
then W.view (W.currentTag s) $ insertUp' w $ W.view n s
|
then W.view (W.currentTag s) $ insertUp' w $ W.view n s
|
||||||
else s
|
else s
|
||||||
insertUp' a s = W.modify (Just $ W.Stack a [] [])
|
insertUp' a = W.modify (Just $ W.Stack a [] [])
|
||||||
(\(W.Stack t l r) -> if a `elem` t:l++r
|
(\(W.Stack t l r) -> if a `elem` t:l++r
|
||||||
then Just $ W.Stack t l r
|
then Just $ W.Stack t l r
|
||||||
else Just $ W.Stack a (L.delete a l) (L.delete a (t:r))) s
|
else Just $ W.Stack a (L.delete a l) (L.delete a (t:r)))
|
||||||
|
|
||||||
|
|
||||||
-- | runOrCopy will run the provided shell command unless it can
|
-- | runOrCopy will run the provided shell command unless it can
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
{-# LANGUAGE PatternGuards #-}
|
{-# LANGUAGE PatternGuards #-}
|
||||||
|
{-# LANGUAGE MultiWayIf #-}
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.CycleRecentWS
|
-- Module : XMonad.Actions.CycleRecentWS
|
||||||
@ -35,21 +36,25 @@ module XMonad.Actions.CycleRecentWS (
|
|||||||
#endif
|
#endif
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import XMonad.Actions.Repeatable (repeatableSt)
|
||||||
|
|
||||||
import XMonad hiding (workspaces)
|
import XMonad hiding (workspaces)
|
||||||
import XMonad.StackSet hiding (filter)
|
import XMonad.Prelude (void, when)
|
||||||
|
import XMonad.StackSet hiding (filter, modify)
|
||||||
|
|
||||||
import Control.Arrow ((&&&))
|
import Control.Arrow ((&&&))
|
||||||
import Data.Function (on)
|
import Data.Function (on)
|
||||||
|
import Control.Monad.State (lift)
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
|
-- You can use this module with the following in your @xmonad.hs@ file:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.CycleRecentWS
|
-- > import XMonad.Actions.CycleRecentWS
|
||||||
-- >
|
-- >
|
||||||
-- > , ((modm, xK_Tab), cycleRecentWS [xK_Alt_L] xK_Tab xK_grave)
|
-- > , ((modm, xK_Tab), cycleRecentWS [xK_Alt_L] xK_Tab xK_grave)
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | Cycle through most recent workspaces with repeated presses of a key, while
|
-- | Cycle through most recent workspaces with repeated presses of a key, while
|
||||||
-- a modifier key is held down. The recency of workspaces previewed while browsing
|
-- a modifier key is held down. The recency of workspaces previewed while browsing
|
||||||
@ -111,25 +116,15 @@ cycleWindowSets :: (WindowSet -> [WorkspaceId]) -- ^ A function used to create a
|
|||||||
-> X ()
|
-> X ()
|
||||||
cycleWindowSets genOptions mods keyNext keyPrev = do
|
cycleWindowSets genOptions mods keyNext keyPrev = do
|
||||||
(options, unView') <- gets $ (genOptions &&& unView) . windowset
|
(options, unView') <- gets $ (genOptions &&& unView) . windowset
|
||||||
XConf {theRoot = root, display = d} <- ask
|
let
|
||||||
let event = allocaXEvent $ \p -> do
|
preview = do
|
||||||
maskEvent d (keyPressMask .|. keyReleaseMask) p
|
i <- get
|
||||||
KeyEvent {ev_event_type = t, ev_keycode = c} <- getEvent p
|
lift $ windows (view (options !! (i `mod` n)) . unView')
|
||||||
s <- keycodeToKeysym d c 0
|
where n = length options
|
||||||
return (t, s)
|
void . repeatableSt (-1) mods keyNext $ \t s -> when (t == keyPress) $ if
|
||||||
let setOption n = do windows $ view (options `cycref` n) . unView'
|
| s == keyNext -> modify succ >> preview
|
||||||
(t, s) <- io event
|
| s == keyPrev -> modify pred >> preview
|
||||||
case () of
|
| otherwise -> pure ()
|
||||||
() | t == keyPress && s == keyNext -> setOption (n+1)
|
|
||||||
| t == keyPress && s == keyPrev -> setOption (n-1)
|
|
||||||
| t == keyRelease && s `elem` mods -> return ()
|
|
||||||
| otherwise -> setOption n
|
|
||||||
io $ grabKeyboard d root False grabModeAsync grabModeAsync currentTime
|
|
||||||
setOption 0
|
|
||||||
io $ ungrabKeyboard d currentTime
|
|
||||||
where
|
|
||||||
cycref :: [a] -> Int -> a
|
|
||||||
cycref l i = l !! (i `mod` length l)
|
|
||||||
|
|
||||||
-- | Given an old and a new 'WindowSet', which is __exactly__ one
|
-- | Given an old and a new 'WindowSet', which is __exactly__ one
|
||||||
-- 'view' away from the old one, restore the workspace order of the
|
-- 'view' away from the old one, restore the workspace order of the
|
||||||
|
@ -23,7 +23,7 @@ import XMonad.Prelude (elemIndex, fromMaybe)
|
|||||||
import qualified XMonad.StackSet as S
|
import qualified XMonad.StackSet as S
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad
|
-- > import XMonad
|
||||||
-- > import XMonad.Actions.CycleSelectedLayouts
|
-- > import XMonad.Actions.CycleSelectedLayouts
|
||||||
@ -39,8 +39,9 @@ cycleToNext lst a = do
|
|||||||
-- | If the current layout is in the list, cycle to the next layout. Otherwise,
|
-- | If the current layout is in the list, cycle to the next layout. Otherwise,
|
||||||
-- apply the first layout from list.
|
-- apply the first layout from list.
|
||||||
cycleThroughLayouts :: [String] -> X ()
|
cycleThroughLayouts :: [String] -> X ()
|
||||||
cycleThroughLayouts lst = do
|
cycleThroughLayouts [] = pure ()
|
||||||
|
cycleThroughLayouts lst@(x: _) = do
|
||||||
winset <- gets windowset
|
winset <- gets windowset
|
||||||
let ld = description . S.layout . S.workspace . S.current $ winset
|
let ld = description . S.layout . S.workspace . S.current $ winset
|
||||||
let newld = fromMaybe (head lst) (cycleToNext lst ld)
|
let newld = fromMaybe x (cycleToNext lst ld)
|
||||||
sendMessage $ JumpToLayout newld
|
sendMessage $ JumpToLayout newld
|
||||||
|
@ -92,7 +92,7 @@ import XMonad.Util.Types
|
|||||||
import XMonad.Util.WorkspaceCompare
|
import XMonad.Util.WorkspaceCompare
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
|
-- You can use this module with the following in your @xmonad.hs@ file:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.CycleWS
|
-- > import XMonad.Actions.CycleWS
|
||||||
-- >
|
-- >
|
||||||
@ -122,7 +122,7 @@ import XMonad.Util.WorkspaceCompare
|
|||||||
-- > windows . view $ t )
|
-- > windows . view $ t )
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
--
|
--
|
||||||
-- When using the toggle functions, in order to ensure that the workspace
|
-- When using the toggle functions, in order to ensure that the workspace
|
||||||
-- to which you switch is the previously viewed workspace, use the
|
-- to which you switch is the previously viewed workspace, use the
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE ViewPatterns, MultiWayIf #-}
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.CycleWindows
|
-- Module : XMonad.Actions.CycleWindows
|
||||||
@ -48,18 +50,21 @@ module XMonad.Actions.CycleWindows (
|
|||||||
-- $pointer
|
-- $pointer
|
||||||
|
|
||||||
-- * Generic list rotations
|
-- * Generic list rotations
|
||||||
-- $generic
|
|
||||||
rotUp, rotDown
|
rotUp, rotDown
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import XMonad
|
import XMonad
|
||||||
|
import XMonad.Prelude
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
import XMonad.Actions.RotSlaves
|
import XMonad.Actions.RotSlaves
|
||||||
|
import XMonad.Actions.Repeatable (repeatableSt)
|
||||||
|
|
||||||
import Control.Arrow (second)
|
import Control.Arrow (second)
|
||||||
|
import Control.Monad.Trans (lift)
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
|
-- You can use this module with the following in your @xmonad.hs@ file:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.CycleWindows
|
-- > import XMonad.Actions.CycleWindows
|
||||||
-- > -- config
|
-- > -- config
|
||||||
@ -75,7 +80,7 @@ import Control.Arrow (second)
|
|||||||
--
|
--
|
||||||
-- Also, if you use focus follows mouse, you will want to read the section
|
-- Also, if you use focus follows mouse, you will want to read the section
|
||||||
-- on updating the mouse pointer below. For detailed instructions on
|
-- on updating the mouse pointer below. For detailed instructions on
|
||||||
-- editing your key bindings, see "XMonad.Doc.Extending#Editing_key_bindings".
|
-- editing your key bindings, see <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
{- $pointer
|
{- $pointer
|
||||||
With FocusFollowsMouse == True, the focus is updated after binding
|
With FocusFollowsMouse == True, the focus is updated after binding
|
||||||
actions, possibly focusing a window you didn't intend to focus. Most
|
actions, possibly focusing a window you didn't intend to focus. Most
|
||||||
@ -135,27 +140,19 @@ cycleStacks' :: (W.Stack Window -> [W.Stack Window]) -- ^ A function to a finite
|
|||||||
-> KeySym -- ^ Key used to select a \"previous\" stack.
|
-> KeySym -- ^ Key used to select a \"previous\" stack.
|
||||||
-> X ()
|
-> X ()
|
||||||
cycleStacks' filteredPerms mods keyNext keyPrev = do
|
cycleStacks' filteredPerms mods keyNext keyPrev = do
|
||||||
XConf {theRoot = root, display = d} <- ask
|
stacks <- gets $ maybe [] filteredPerms
|
||||||
stacks <- gets $ maybe [] filteredPerms . W.stack . W.workspace . W.current . windowset
|
. W.stack . W.workspace . W.current . windowset
|
||||||
|
let
|
||||||
let evt = allocaXEvent $
|
preview = do
|
||||||
\p -> do maskEvent d (keyPressMask .|. keyReleaseMask) p
|
i <- get
|
||||||
KeyEvent {ev_event_type = t, ev_keycode = c} <- getEvent p
|
lift . windows . W.modify' . const $ stacks !! (i `mod` n)
|
||||||
s <- keycodeToKeysym d c 0
|
where n = length stacks
|
||||||
return (t, s)
|
void $ repeatableSt 0 mods keyNext $ \t s -> if
|
||||||
choose n (t, s)
|
| t == keyPress && s == keyNext -> modify succ
|
||||||
| t == keyPress && s == keyNext = io evt >>= choose (n+1)
|
| t == keyPress && s == keyPrev -> modify pred
|
||||||
| t == keyPress && s == keyPrev = io evt >>= choose (n-1)
|
| t == keyPress && s `elem` [xK_0..xK_9] -> put (numKeyToN s)
|
||||||
| t == keyPress && s `elem` [xK_0..xK_9] = io evt >>= choose (numKeyToN s)
|
| otherwise -> preview
|
||||||
| t == keyRelease && s `elem` mods = return ()
|
where numKeyToN = subtract 48 . read . show
|
||||||
| otherwise = doStack n >> io evt >>= choose n
|
|
||||||
doStack n = windows . W.modify' . const $ stacks `cycref` n
|
|
||||||
|
|
||||||
io $ grabKeyboard d root False grabModeAsync grabModeAsync currentTime
|
|
||||||
io evt >>= choose 1
|
|
||||||
io $ ungrabKeyboard d currentTime
|
|
||||||
where cycref l i = l !! (i `mod` length l) -- modify' ensures l is never [], but must also be finite
|
|
||||||
numKeyToN = subtract 48 . read . show
|
|
||||||
|
|
||||||
-- | Given a stack element and a stack, shift or insert the element (window)
|
-- | Given a stack element and a stack, shift or insert the element (window)
|
||||||
-- at the currently focused position.
|
-- at the currently focused position.
|
||||||
@ -179,7 +176,7 @@ rotOpposite' :: W.Stack a -> W.Stack a
|
|||||||
rotOpposite' (W.Stack t l r) = W.Stack t' l' r'
|
rotOpposite' (W.Stack t l r) = W.Stack t' l' r'
|
||||||
where rrvl = r ++ reverse l
|
where rrvl = r ++ reverse l
|
||||||
part = (length rrvl + 1) `div` 2
|
part = (length rrvl + 1) `div` 2
|
||||||
(l',t':r') = second reverse . splitAt (length l) $
|
(l', notEmpty -> t' :| r') = second reverse . splitAt (length l) $
|
||||||
reverse (take part rrvl ++ t : drop part rrvl)
|
reverse (take part rrvl ++ t : drop part rrvl)
|
||||||
|
|
||||||
|
|
||||||
@ -205,7 +202,7 @@ rotFocusedDown = windows . W.modify' $ rotFocused' rotDown
|
|||||||
rotFocused' :: ([a] -> [a]) -> W.Stack a -> W.Stack a
|
rotFocused' :: ([a] -> [a]) -> W.Stack a -> W.Stack a
|
||||||
rotFocused' _ s@(W.Stack _ [] []) = s
|
rotFocused' _ s@(W.Stack _ [] []) = s
|
||||||
rotFocused' f (W.Stack t [] (r:rs)) = W.Stack t' [] (r:rs') -- Master has focus
|
rotFocused' f (W.Stack t [] (r:rs)) = W.Stack t' [] (r:rs') -- Master has focus
|
||||||
where (t':rs') = f (t:rs)
|
where (notEmpty -> t' :| rs') = f (t:rs)
|
||||||
rotFocused' f s@W.Stack{} = rotSlaves' f s -- otherwise
|
rotFocused' f s@W.Stack{} = rotSlaves' f s -- otherwise
|
||||||
|
|
||||||
|
|
||||||
@ -223,14 +220,5 @@ rotUnfocused' :: ([a] -> [a]) -> W.Stack a -> W.Stack a
|
|||||||
rotUnfocused' _ s@(W.Stack _ [] []) = s
|
rotUnfocused' _ s@(W.Stack _ [] []) = s
|
||||||
rotUnfocused' f s@(W.Stack _ [] _ ) = rotSlaves' f s -- Master has focus
|
rotUnfocused' f s@(W.Stack _ [] _ ) = rotSlaves' f s -- Master has focus
|
||||||
rotUnfocused' f (W.Stack t ls rs) = W.Stack t (reverse revls') rs' -- otherwise
|
rotUnfocused' f (W.Stack t ls rs) = W.Stack t (reverse revls') rs' -- otherwise
|
||||||
where (master:revls) = reverse ls
|
where (master :| revls) = NE.reverse (let l:ll = ls in l :| ll)
|
||||||
(revls',rs') = splitAt (length ls) (f $ master:revls ++ rs)
|
(revls',rs') = splitAt (length ls) (f $ master:revls ++ rs)
|
||||||
|
|
||||||
-- $generic
|
|
||||||
-- Generic list rotations such that @rotUp [1..4]@ is equivalent to
|
|
||||||
-- @[2,3,4,1]@ and @rotDown [1..4]@ to @[4,1,2,3]@. They both are
|
|
||||||
-- @id@ for null or singleton lists.
|
|
||||||
rotUp :: [a] -> [a]
|
|
||||||
rotUp l = drop 1 l ++ take 1 l
|
|
||||||
rotDown :: [a] -> [a]
|
|
||||||
rotDown = reverse . rotUp . reverse
|
|
||||||
|
@ -25,35 +25,36 @@ module XMonad.Actions.CycleWorkspaceByScreen (
|
|||||||
|
|
||||||
import Data.IORef
|
import Data.IORef
|
||||||
|
|
||||||
import Graphics.X11.Xlib.Extras
|
|
||||||
|
|
||||||
import XMonad
|
import XMonad
|
||||||
import XMonad.Prelude
|
import XMonad.Prelude
|
||||||
import XMonad.Hooks.WorkspaceHistory
|
import XMonad.Hooks.WorkspaceHistory
|
||||||
|
import XMonad.Actions.Repeatable (repeatable)
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- This module must be used in conjuction with XMonad.Hooks.WorkspaceHistory
|
|
||||||
--
|
--
|
||||||
-- To use, add something like the following to your keybindings
|
-- To use this module, first import it as well as
|
||||||
-- , ((mod4Mask, xK_slash), cycleWorkspaceOnCurrentScreen [xK_Super_L] xK_slash xK_p)
|
-- "XMonad.Hooks.WorkspaceHistory":
|
||||||
|
--
|
||||||
|
-- > import XMonad.Hooks.WorkspaceHistory (workspaceHistoryHook)
|
||||||
|
-- > import XMonad.Actions.CycleWorkspaceByScreen
|
||||||
|
--
|
||||||
|
-- Then add 'workspaceHistoryHook' to your @logHook@ like this:
|
||||||
|
--
|
||||||
|
-- > main :: IO ()
|
||||||
|
-- > main = xmonad $ def
|
||||||
|
-- > { ...
|
||||||
|
-- > , logHook = workspaceHistoryHook >> ...
|
||||||
|
-- > }
|
||||||
|
--
|
||||||
|
-- Finally, define a new keybinding for cycling (seen) workspaces per
|
||||||
|
-- screen:
|
||||||
|
--
|
||||||
|
-- > , ((mod4Mask, xK_slash), cycleWorkspaceOnCurrentScreen [xK_Super_L] xK_slash xK_p)
|
||||||
|
|
||||||
repeatableAction :: [KeySym] -> (EventType -> KeySym -> X ()) -> X ()
|
{-# DEPRECATED repeatableAction "Use XMonad.Actions.Repeatable.repeatable" #-}
|
||||||
repeatableAction mods pressHandler = do
|
repeatableAction :: [KeySym] -> KeySym -> (EventType -> KeySym -> X ()) -> X ()
|
||||||
XConf {theRoot = root, display = d} <- ask
|
repeatableAction = repeatable
|
||||||
let getNextEvent = io $ allocaXEvent $ \p ->
|
|
||||||
do
|
|
||||||
maskEvent d (keyPressMask .|. keyReleaseMask) p
|
|
||||||
KeyEvent {ev_event_type = t, ev_keycode = c} <- getEvent p
|
|
||||||
s <- io $ keycodeToKeysym d c 0
|
|
||||||
return (t, s)
|
|
||||||
handleEvent (t, s)
|
|
||||||
| t == keyRelease && s `elem` mods = return ()
|
|
||||||
| otherwise = pressHandler t s >> getNextEvent >>= handleEvent
|
|
||||||
|
|
||||||
io $ grabKeyboard d root False grabModeAsync grabModeAsync currentTime
|
|
||||||
getNextEvent >>= handleEvent
|
|
||||||
io $ ungrabKeyboard d currentTime
|
|
||||||
|
|
||||||
handleKeyEvent :: EventType
|
handleKeyEvent :: EventType
|
||||||
-> KeySym
|
-> KeySym
|
||||||
@ -72,7 +73,16 @@ runFirst :: [EventType -> KeySym -> Maybe (X ())] -> EventType -> KeySym -> X ()
|
|||||||
runFirst matchers eventType key =
|
runFirst matchers eventType key =
|
||||||
fromMaybe (return ()) $ join $ find isJust $ map (\fn -> fn eventType key) matchers
|
fromMaybe (return ()) $ join $ find isJust $ map (\fn -> fn eventType key) matchers
|
||||||
|
|
||||||
cycleWorkspaceOnScreen :: ScreenId -> [KeySym] -> KeySym -> KeySym -> X ()
|
-- | Like 'XMonad.Actions.CycleRecentWS.cycleRecentWS', but only cycle
|
||||||
|
-- through the most recent workspaces on the given screen.
|
||||||
|
cycleWorkspaceOnScreen
|
||||||
|
:: ScreenId -- ^ The screen to cycle on.
|
||||||
|
-> [KeySym] -- ^ A list of modifier keys used when invoking this
|
||||||
|
-- action; as soon as one of them is released, the final
|
||||||
|
-- switch is made.
|
||||||
|
-> KeySym -- ^ Key used to switch to next workspace.
|
||||||
|
-> KeySym -- ^ Key used to switch to previous workspace.
|
||||||
|
-> X ()
|
||||||
cycleWorkspaceOnScreen screenId mods nextKey prevKey = workspaceHistoryTransaction $ do
|
cycleWorkspaceOnScreen screenId mods nextKey prevKey = workspaceHistoryTransaction $ do
|
||||||
startingHistory <- workspaceHistoryByScreen
|
startingHistory <- workspaceHistoryByScreen
|
||||||
currentWSIndex <- io $ newIORef 1
|
currentWSIndex <- io $ newIORef 1
|
||||||
@ -85,14 +95,15 @@ cycleWorkspaceOnScreen screenId mods nextKey prevKey = workspaceHistoryTransacti
|
|||||||
return $ cycleWorkspaces !! current
|
return $ cycleWorkspaces !! current
|
||||||
focusIncrement i = io (getAndIncrementWS i) >>= (windows . W.greedyView)
|
focusIncrement i = io (getAndIncrementWS i) >>= (windows . W.greedyView)
|
||||||
|
|
||||||
focusIncrement 1 -- Do the first workspace cycle
|
repeatable mods nextKey $
|
||||||
repeatableAction mods $
|
|
||||||
runFirst
|
runFirst
|
||||||
[ handleKeyEvent keyPress nextKey $ focusIncrement 1
|
[ handleKeyEvent keyPress nextKey $ focusIncrement 1
|
||||||
, handleKeyEvent keyPress prevKey $ focusIncrement (-1)
|
, handleKeyEvent keyPress prevKey $ focusIncrement (-1)
|
||||||
]
|
]
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
|
-- | Like 'cycleWorkspaceOnScreen', but supply the currently focused
|
||||||
|
-- screen as the @screenId@.
|
||||||
cycleWorkspaceOnCurrentScreen
|
cycleWorkspaceOnCurrentScreen
|
||||||
:: [KeySym] -> KeySym -> KeySym -> X ()
|
:: [KeySym] -> KeySym -> KeySym -> X ()
|
||||||
cycleWorkspaceOnCurrentScreen mods n p =
|
cycleWorkspaceOnCurrentScreen mods n p =
|
||||||
|
@ -39,7 +39,7 @@ import qualified XMonad.StackSet as W
|
|||||||
import XMonad
|
import XMonad
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- To use demanage, add this import to your @~\/.xmonad\/xmonad.hs@:
|
-- To use demanage, add this import to your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.DeManage
|
-- > import XMonad.Actions.DeManage
|
||||||
--
|
--
|
||||||
@ -48,7 +48,7 @@ import XMonad
|
|||||||
-- > , ((modm, xK_d ), withFocused demanage)
|
-- > , ((modm, xK_d ), withFocused demanage)
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | Stop managing the currently focused window.
|
-- | Stop managing the currently focused window.
|
||||||
demanage :: Window -> X ()
|
demanage :: Window -> X ()
|
||||||
|
@ -25,10 +25,13 @@ module XMonad.Actions.DwmPromote (
|
|||||||
|
|
||||||
import XMonad
|
import XMonad
|
||||||
import XMonad.StackSet
|
import XMonad.StackSet
|
||||||
|
import XMonad.Prelude
|
||||||
|
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.DwmPromote
|
-- > import XMonad.Actions.DwmPromote
|
||||||
--
|
--
|
||||||
@ -37,7 +40,7 @@ import XMonad.StackSet
|
|||||||
-- > , ((modm, xK_Return), dwmpromote)
|
-- > , ((modm, xK_Return), dwmpromote)
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | Swap the focused window with the master window. If focus is in
|
-- | Swap the focused window with the master window. If focus is in
|
||||||
-- the master, swap it with the next window in the stack. Focus
|
-- the master, swap it with the next window in the stack. Focus
|
||||||
@ -45,6 +48,6 @@ import XMonad.StackSet
|
|||||||
dwmpromote :: X ()
|
dwmpromote :: X ()
|
||||||
dwmpromote = windows $ modify' $
|
dwmpromote = windows $ modify' $
|
||||||
\c -> case c of
|
\c -> case c of
|
||||||
Stack _ [] [] -> c
|
Stack _ [] [] -> c
|
||||||
Stack t [] (x:rs) -> Stack x [] (t:rs)
|
Stack t [] (r:rs) -> Stack r [] (t:rs)
|
||||||
Stack t ls rs -> Stack t [] (ys ++ x : rs) where (x:ys) = reverse ls
|
Stack t (l:ls) rs -> Stack t [] (ys ++ y : rs) where (y :| ys) = NE.reverse (l :| ls)
|
||||||
|
@ -69,7 +69,9 @@ import qualified XMonad.Util.ExtensibleState as XS
|
|||||||
-- the working directory to the one configured for the matching
|
-- the working directory to the one configured for the matching
|
||||||
-- project. If the workspace doesn't have any windows, the project's
|
-- project. If the workspace doesn't have any windows, the project's
|
||||||
-- start-up hook is executed. This allows you to launch applications
|
-- start-up hook is executed. This allows you to launch applications
|
||||||
-- or further configure the workspace/project.
|
-- or further configure the workspace/project. To close a project,
|
||||||
|
-- you can use the functions provided by "XMonad.Actions.DynamicWorkspaces",
|
||||||
|
-- such as @removeWorkspace@ or @removeWorkspaceByTag@.
|
||||||
--
|
--
|
||||||
-- When using the @switchProjectPrompt@ function, workspaces are
|
-- When using the @switchProjectPrompt@ function, workspaces are
|
||||||
-- created as needed. This means you can create new project spaces
|
-- created as needed. This means you can create new project spaces
|
||||||
@ -109,11 +111,11 @@ import qualified XMonad.Util.ExtensibleState as XS
|
|||||||
--
|
--
|
||||||
-- And finally, configure some optional key bindings:
|
-- And finally, configure some optional key bindings:
|
||||||
--
|
--
|
||||||
-- > , ((modm, xK_space), switchProjectPrompt)
|
-- > , ((modm, xK_space), switchProjectPrompt def)
|
||||||
-- > , ((modm, xK_slash), shiftToProjectPrompt)
|
-- > , ((modm, xK_slash), shiftToProjectPrompt def)
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
type ProjectName = String
|
type ProjectName = String
|
||||||
@ -230,7 +232,9 @@ lookupProject name = Map.lookup name <$> XS.gets projects
|
|||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- | Fetch the current project (the one being used for the currently
|
-- | Fetch the current project (the one being used for the currently
|
||||||
-- active workspace).
|
-- active workspace). If the workspace doesn't have a project, a
|
||||||
|
-- default project is returned, using the workspace name as the
|
||||||
|
-- project name.
|
||||||
currentProject :: X Project
|
currentProject :: X Project
|
||||||
currentProject = do
|
currentProject = do
|
||||||
name <- gets (W.tag . W.workspace . W.current . windowset)
|
name <- gets (W.tag . W.workspace . W.current . windowset)
|
||||||
@ -255,20 +259,7 @@ modifyProject f = do
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- | Switch to the given project.
|
-- | Switch to the given project.
|
||||||
switchProject :: Project -> X ()
|
switchProject :: Project -> X ()
|
||||||
switchProject p = do
|
switchProject p = appendWorkspace (projectName p)
|
||||||
oldws <- gets (W.workspace . W.current . windowset)
|
|
||||||
oldp <- currentProject
|
|
||||||
|
|
||||||
let name = W.tag oldws
|
|
||||||
ws = W.integrate' (W.stack oldws)
|
|
||||||
|
|
||||||
-- If the project we are switching away from has no windows, and
|
|
||||||
-- it's a dynamic project, remove it from the configuration.
|
|
||||||
when (null ws && isNothing (projectStartHook oldp)) $ do
|
|
||||||
removeWorkspaceByTag name -- also remove the old workspace
|
|
||||||
XS.modify (\s -> s {projects = Map.delete name $ projects s})
|
|
||||||
|
|
||||||
appendWorkspace (projectName p)
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- | Prompt for a project name and then switch to it. Automatically
|
-- | Prompt for a project name and then switch to it. Automatically
|
||||||
|
@ -51,7 +51,7 @@ import qualified XMonad.Util.ExtensibleState as XS
|
|||||||
import XMonad.Actions.TopicSpace
|
import XMonad.Actions.TopicSpace
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module by importing it into your ~\/.xmonad\/xmonad.hs file:
|
-- You can use this module by importing it into your @xmonad.hs@ file:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.DynamicWorkspaceGroups
|
-- > import XMonad.Actions.DynamicWorkspaceGroups
|
||||||
--
|
--
|
||||||
|
@ -49,7 +49,7 @@ import XMonad.Prelude (fromJust, fromMaybe)
|
|||||||
import Data.Ord (comparing)
|
import Data.Ord (comparing)
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module by importing it into your ~\/.xmonad\/xmonad.hs file:
|
-- You can use this module by importing it into your @xmonad.hs@ file:
|
||||||
--
|
--
|
||||||
-- > import qualified XMonad.Actions.DynamicWorkspaceOrder as DO
|
-- > import qualified XMonad.Actions.DynamicWorkspaceOrder as DO
|
||||||
--
|
--
|
||||||
@ -152,7 +152,8 @@ swapOrder :: WorkspaceId -> WorkspaceId -> X ()
|
|||||||
swapOrder w1 w2 = do
|
swapOrder w1 w2 = do
|
||||||
io $ print (w1,w2)
|
io $ print (w1,w2)
|
||||||
WSO (Just m) <- XS.get
|
WSO (Just m) <- XS.get
|
||||||
let [i1,i2] = map (fromJust . flip M.lookup m) [w1,w2]
|
let i1 = fromJust (w1 `M.lookup` m)
|
||||||
|
let i2 = fromJust (w2 `M.lookup` m)
|
||||||
XS.modify (withWSO (M.insert w1 i2 . M.insert w2 i1))
|
XS.modify (withWSO (M.insert w1 i2 . M.insert w2 i1))
|
||||||
windows id -- force a status bar update
|
windows id -- force a status bar update
|
||||||
|
|
||||||
|
@ -38,13 +38,13 @@ import XMonad.Prelude (find, isNothing, nub, when)
|
|||||||
import XMonad hiding (workspaces)
|
import XMonad hiding (workspaces)
|
||||||
import XMonad.StackSet hiding (filter, modify, delete)
|
import XMonad.StackSet hiding (filter, modify, delete)
|
||||||
import XMonad.Prompt.Workspace ( Wor(Wor), workspacePrompt )
|
import XMonad.Prompt.Workspace ( Wor(Wor), workspacePrompt )
|
||||||
import XMonad.Prompt ( XPConfig, mkXPrompt )
|
import XMonad.Prompt ( XPConfig, mkComplFunFromList', mkXPrompt )
|
||||||
import XMonad.Util.WorkspaceCompare ( getSortByIndex )
|
import XMonad.Util.WorkspaceCompare ( getSortByIndex )
|
||||||
import qualified Data.Map.Strict as Map
|
import qualified Data.Map.Strict as Map
|
||||||
import qualified XMonad.Util.ExtensibleState as XS
|
import qualified XMonad.Util.ExtensibleState as XS
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
|
-- You can use this module with the following in your @xmonad.hs@ file:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.DynamicWorkspaces
|
-- > import XMonad.Actions.DynamicWorkspaces
|
||||||
-- > import XMonad.Actions.CopyWindow(copy)
|
-- > import XMonad.Actions.CopyWindow(copy)
|
||||||
@ -75,7 +75,7 @@ import qualified XMonad.Util.ExtensibleState as XS
|
|||||||
-- > zip (zip (repeat (modm .|. controlMask)) [xK_1..xK_9]) (map (setWorkspaceIndex) [1..])
|
-- > zip (zip (repeat (modm .|. controlMask)) [xK_1..xK_9]) (map (setWorkspaceIndex) [1..])
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings". See also the documentation for
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>. See also the documentation for
|
||||||
-- "XMonad.Actions.CopyWindow", 'windows', 'shift', and 'XPConfig'.
|
-- "XMonad.Actions.CopyWindow", 'windows', 'shift', and 'XPConfig'.
|
||||||
|
|
||||||
type WorkspaceTag = String
|
type WorkspaceTag = String
|
||||||
@ -107,17 +107,13 @@ withWorkspaceIndex job widx = do
|
|||||||
ilookup :: WorkspaceIndex -> X (Maybe WorkspaceTag)
|
ilookup :: WorkspaceIndex -> X (Maybe WorkspaceTag)
|
||||||
ilookup idx = Map.lookup idx <$> XS.gets workspaceIndexMap
|
ilookup idx = Map.lookup idx <$> XS.gets workspaceIndexMap
|
||||||
|
|
||||||
|
|
||||||
mkCompl :: [String] -> String -> IO [String]
|
|
||||||
mkCompl l s = return $ filter (\x -> take (length s) x == s) l
|
|
||||||
|
|
||||||
withWorkspace :: XPConfig -> (String -> X ()) -> X ()
|
withWorkspace :: XPConfig -> (String -> X ()) -> X ()
|
||||||
withWorkspace c job = do ws <- gets (workspaces . windowset)
|
withWorkspace c job = do ws <- gets (workspaces . windowset)
|
||||||
sort <- getSortByIndex
|
sort <- getSortByIndex
|
||||||
let ts = map tag $ sort ws
|
let ts = map tag $ sort ws
|
||||||
job' t | t `elem` ts = job t
|
job' t | t `elem` ts = job t
|
||||||
| otherwise = addHiddenWorkspace t >> job t
|
| otherwise = addHiddenWorkspace t >> job t
|
||||||
mkXPrompt (Wor "") c (mkCompl ts) job'
|
mkXPrompt (Wor "") c (mkComplFunFromList' c ts) job'
|
||||||
|
|
||||||
renameWorkspace :: XPConfig -> X ()
|
renameWorkspace :: XPConfig -> X ()
|
||||||
renameWorkspace conf = workspacePrompt conf renameWorkspaceByName
|
renameWorkspace conf = workspacePrompt conf renameWorkspaceByName
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
{-# LANGUAGE MultiWayIf #-}
|
{-# LANGUAGE MultiWayIf #-}
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
@ -50,7 +51,7 @@ import qualified Data.Map.Strict as M (Map, elems, map, mapWithKey)
|
|||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module's basic functionality with the following in your
|
-- You can use this module's basic functionality with the following in your
|
||||||
-- @~\/.xmonad\/xmonad.hs@:
|
-- @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.EasyMotion (selectWindow)
|
-- > import XMonad.Actions.EasyMotion (selectWindow)
|
||||||
--
|
--
|
||||||
@ -263,19 +264,19 @@ handleSelectWindow c = do
|
|||||||
visibleWindows :: [Window]
|
visibleWindows :: [Window]
|
||||||
visibleWindows = toList mappedWins
|
visibleWindows = toList mappedWins
|
||||||
sortedOverlayWindows :: X [OverlayWindow]
|
sortedOverlayWindows :: X [OverlayWindow]
|
||||||
sortedOverlayWindows = sortOverlayWindows <$> buildOverlayWindows dpy th visibleWindows
|
sortedOverlayWindows = sortOverlayWindows <$> buildOverlayWindows th visibleWindows
|
||||||
PerScreenKeys m ->
|
PerScreenKeys m ->
|
||||||
fmap concat
|
fmap concat
|
||||||
$ sequence
|
$ sequence
|
||||||
$ M.elems
|
$ M.elems
|
||||||
$ M.mapWithKey (\sid ks -> buildOverlays ks <$> sortedOverlayWindows sid) m
|
$ M.mapWithKey (\sid ks -> buildOverlays ks <$> sortedOverlayWindows sid) m
|
||||||
where
|
where
|
||||||
screenById :: ScreenId -> Maybe (W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail)
|
screenById :: ScreenId -> Maybe WindowScreen
|
||||||
screenById sid = find ((== sid) . W.screen) (W.screens ws)
|
screenById sid = find ((== sid) . W.screen) (W.screens ws)
|
||||||
visibleWindowsOnScreen :: ScreenId -> [Window]
|
visibleWindowsOnScreen :: ScreenId -> [Window]
|
||||||
visibleWindowsOnScreen sid = filter (`elem` toList mappedWins) $ W.integrate' $ screenById sid >>= W.stack . W.workspace
|
visibleWindowsOnScreen sid = filter (`elem` toList mappedWins) $ W.integrate' $ screenById sid >>= W.stack . W.workspace
|
||||||
sortedOverlayWindows :: ScreenId -> X [OverlayWindow]
|
sortedOverlayWindows :: ScreenId -> X [OverlayWindow]
|
||||||
sortedOverlayWindows sid = sortOverlayWindows <$> buildOverlayWindows dpy th (visibleWindowsOnScreen sid)
|
sortedOverlayWindows sid = sortOverlayWindows <$> buildOverlayWindows th (visibleWindowsOnScreen sid)
|
||||||
status <- io $ grabKeyboard dpy rw True grabModeAsync grabModeAsync currentTime
|
status <- io $ grabKeyboard dpy rw True grabModeAsync grabModeAsync currentTime
|
||||||
if status == grabSuccess
|
if status == grabSuccess
|
||||||
then do
|
then do
|
||||||
@ -296,10 +297,11 @@ handleSelectWindow c = do
|
|||||||
allKeys (PerScreenKeys m) = concat $ M.elems m
|
allKeys (PerScreenKeys m) = concat $ M.elems m
|
||||||
|
|
||||||
buildOverlays :: [KeySym] -> [OverlayWindow] -> [Overlay]
|
buildOverlays :: [KeySym] -> [OverlayWindow] -> [Overlay]
|
||||||
buildOverlays ks = appendChords (maxChordLen c) ks
|
buildOverlays = appendChords (maxChordLen c)
|
||||||
|
|
||||||
buildOverlayWindows :: Display -> Position -> [Window] -> X [OverlayWindow]
|
buildOverlayWindows :: Position -> [Window] -> X [OverlayWindow]
|
||||||
buildOverlayWindows dpy th ws = sequence $ buildOverlayWin dpy th <$> ws
|
buildOverlayWindows th = fmap (fromMaybe [] . sequenceA)
|
||||||
|
. traverse (buildOverlayWin th)
|
||||||
|
|
||||||
sortOverlayWindows :: [OverlayWindow] -> [OverlayWindow]
|
sortOverlayWindows :: [OverlayWindow] -> [OverlayWindow]
|
||||||
sortOverlayWindows = sortOn ((wa_x &&& wa_y) . attrs)
|
sortOverlayWindows = sortOn ((wa_x &&& wa_y) . attrs)
|
||||||
@ -307,12 +309,13 @@ handleSelectWindow c = do
|
|||||||
makeRect :: WindowAttributes -> Rectangle
|
makeRect :: WindowAttributes -> Rectangle
|
||||||
makeRect wa = Rectangle (fi (wa_x wa)) (fi (wa_y wa)) (fi (wa_width wa)) (fi (wa_height wa))
|
makeRect wa = Rectangle (fi (wa_x wa)) (fi (wa_y wa)) (fi (wa_width wa)) (fi (wa_height wa))
|
||||||
|
|
||||||
buildOverlayWin :: Display -> Position -> Window -> X OverlayWindow
|
buildOverlayWin :: Position -> Window -> X (Maybe OverlayWindow)
|
||||||
buildOverlayWin dpy th w = do
|
buildOverlayWin th w = safeGetWindowAttributes w >>= \case
|
||||||
wAttrs <- io $ getWindowAttributes dpy w
|
Nothing -> pure Nothing
|
||||||
let r = overlayF c th $ makeRect wAttrs
|
Just wAttrs -> do
|
||||||
o <- createNewWindow r Nothing "" True
|
let r = overlayF c th $ makeRect wAttrs
|
||||||
return OverlayWindow { rect=r, overlay=o, win=w, attrs=wAttrs }
|
o <- createNewWindow r Nothing "" True
|
||||||
|
return . Just $ OverlayWindow { rect=r, overlay=o, win=w, attrs=wAttrs }
|
||||||
|
|
||||||
-- | Display an overlay with the provided formatting
|
-- | Display an overlay with the provided formatting
|
||||||
displayOverlay :: XMonadFont -> Overlay -> X ()
|
displayOverlay :: XMonadFont -> Overlay -> X ()
|
||||||
@ -384,5 +387,5 @@ handleKeyboard dpy drawFn cancel selected deselected = do
|
|||||||
_ -> handleKeyboard dpy drawFn cancel (trim fg) (clear bg) >>= retryBackspace
|
_ -> handleKeyboard dpy drawFn cancel (trim fg) (clear bg) >>= retryBackspace
|
||||||
where
|
where
|
||||||
(fg, bg) = partition ((== Just keySym) . listToMaybe . chord) selected
|
(fg, bg) = partition ((== Just keySym) . listToMaybe . chord) selected
|
||||||
trim = map (\o -> o { chord = tail $ chord o })
|
trim = map (\o -> o { chord = drop 1 $ chord o })
|
||||||
clear = map (\o -> o { chord = [] })
|
clear = map (\o -> o { chord = [] })
|
||||||
|
@ -25,7 +25,7 @@ import XMonad.StackSet
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- To use, import this module into your @~\/.xmonad\/xmonad.hs@:
|
-- To use, import this module into your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.FindEmptyWorkspace
|
-- > import XMonad.Actions.FindEmptyWorkspace
|
||||||
--
|
--
|
||||||
@ -38,7 +38,7 @@ import XMonad.StackSet
|
|||||||
-- will tag the current window to an empty workspace and view it.
|
-- will tag the current window to an empty workspace and view it.
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | Find the first hidden empty workspace in a StackSet. Returns
|
-- | Find the first hidden empty workspace in a StackSet. Returns
|
||||||
-- Nothing if all workspaces are in use. Function searches currently
|
-- Nothing if all workspaces are in use. Function searches currently
|
||||||
|
@ -24,12 +24,12 @@ module XMonad.Actions.FlexibleManipulate (
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import XMonad
|
import XMonad
|
||||||
import XMonad.Prelude ((<&>))
|
import XMonad.Prelude ((<&>), fi)
|
||||||
import qualified Prelude as P
|
import qualified Prelude as P
|
||||||
import Prelude (Double, Integer, Ord (..), const, fromIntegral, fst, id, map, otherwise, round, snd, uncurry, ($), (.))
|
import Prelude (Double, Integer, Ord (..), const, fromIntegral, fst, id, otherwise, round, snd, uncurry, ($))
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- First, add this import to your @~\/.xmonad\/xmonad.hs@:
|
-- First, add this import to your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import qualified XMonad.Actions.FlexibleManipulate as Flex
|
-- > import qualified XMonad.Actions.FlexibleManipulate as Flex
|
||||||
--
|
--
|
||||||
@ -80,8 +80,10 @@ position = const 0.5
|
|||||||
-- | Given an interpolation function, implement an appropriate window
|
-- | Given an interpolation function, implement an appropriate window
|
||||||
-- manipulation action.
|
-- manipulation action.
|
||||||
mouseWindow :: (Double -> Double) -> Window -> X ()
|
mouseWindow :: (Double -> Double) -> Window -> X ()
|
||||||
mouseWindow f w = whenX (isClient w) $ withDisplay $ \d -> do
|
mouseWindow f w = whenX (isClient w) $ withDisplay $ \d ->
|
||||||
[wpos, wsize] <- io $ getWindowAttributes d w <&> winAttrs
|
withWindowAttributes d w $ \wa -> do
|
||||||
|
let wpos = (fi (wa_x wa), fi (wa_y wa))
|
||||||
|
wsize = (fi (wa_width wa), fi (wa_height wa))
|
||||||
sh <- io $ getWMNormalHints d w
|
sh <- io $ getWMNormalHints d w
|
||||||
pointer <- io $ queryPointer d w <&> pointerPos
|
pointer <- io $ queryPointer d w <&> pointerPos
|
||||||
|
|
||||||
@ -104,18 +106,10 @@ mouseWindow f w = whenX (isClient w) $ withDisplay $ \d -> do
|
|||||||
|
|
||||||
where
|
where
|
||||||
pointerPos (_,_,_,px,py,_,_,_) = (fromIntegral px,fromIntegral py) :: Pnt
|
pointerPos (_,_,_,px,py,_,_,_) = (fromIntegral px,fromIntegral py) :: Pnt
|
||||||
winAttrs :: WindowAttributes -> [Pnt]
|
|
||||||
winAttrs x = pairUp $ map (fromIntegral . ($ x)) [wa_x, wa_y, wa_width, wa_height]
|
|
||||||
|
|
||||||
|
|
||||||
-- I'd rather I didn't have to do this, but I hate writing component 2d math
|
-- I'd rather I didn't have to do this, but I hate writing component 2d math
|
||||||
type Pnt = (Double, Double)
|
type Pnt = (Double, Double)
|
||||||
|
|
||||||
pairUp :: [a] -> [(a,a)]
|
|
||||||
pairUp [] = []
|
|
||||||
pairUp [_] = []
|
|
||||||
pairUp (x:y:xs) = (x, y) : pairUp xs
|
|
||||||
|
|
||||||
mapP :: (a -> b) -> (a, a) -> (b, b)
|
mapP :: (a -> b) -> (a, a) -> (b, b)
|
||||||
mapP f (x, y) = (f x, f y)
|
mapP f (x, y) = (f x, f y)
|
||||||
zipP :: (a -> b -> c) -> (a,a) -> (b,b) -> (c,c)
|
zipP :: (a -> b -> c) -> (a,a) -> (b,b) -> (c,c)
|
||||||
|
@ -25,7 +25,7 @@ import XMonad.Prelude (fi)
|
|||||||
import Foreign.C.Types
|
import Foreign.C.Types
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- To use, first import this module into your @~\/.xmonad\/xmonad.hs@ file:
|
-- To use, first import this module into your @xmonad.hs@ file:
|
||||||
--
|
--
|
||||||
-- > import qualified XMonad.Actions.FlexibleResize as Flex
|
-- > import qualified XMonad.Actions.FlexibleResize as Flex
|
||||||
--
|
--
|
||||||
@ -50,12 +50,15 @@ mouseResizeEdgeWindow
|
|||||||
:: Rational -- ^ The size of the area where only one edge is resized.
|
:: Rational -- ^ The size of the area where only one edge is resized.
|
||||||
-> Window -- ^ The window to resize.
|
-> Window -- ^ The window to resize.
|
||||||
-> X ()
|
-> X ()
|
||||||
mouseResizeEdgeWindow edge w = whenX (isClient w) $ withDisplay $ \d -> do
|
mouseResizeEdgeWindow edge w = whenX (isClient w) $ withDisplay $ \d ->
|
||||||
wa <- io $ getWindowAttributes d w
|
withWindowAttributes d w $ \wa -> do
|
||||||
sh <- io $ getWMNormalHints d w
|
sh <- io $ getWMNormalHints d w
|
||||||
(_, _, _, _, _, ix, iy, _) <- io $ queryPointer d w
|
(_, _, _, _, _, ix, iy, _) <- io $ queryPointer d w
|
||||||
let
|
let
|
||||||
[pos_x, pos_y, width, height] = map (fi . ($ wa)) [wa_x, wa_y, wa_width, wa_height]
|
pos_x = fi $ wa_x wa
|
||||||
|
pos_y = fi $ wa_y wa
|
||||||
|
width = fi $ wa_width wa
|
||||||
|
height = fi $ wa_height wa
|
||||||
west = findPos ix width
|
west = findPos ix width
|
||||||
north = findPos iy height
|
north = findPos iy height
|
||||||
(cx, fx, gx) = mkSel west width pos_x
|
(cx, fx, gx) = mkSel west width pos_x
|
||||||
|
@ -19,14 +19,18 @@ module XMonad.Actions.FloatKeys (
|
|||||||
keysMoveWindowTo,
|
keysMoveWindowTo,
|
||||||
keysResizeWindow,
|
keysResizeWindow,
|
||||||
keysAbsResizeWindow,
|
keysAbsResizeWindow,
|
||||||
P, G,
|
directionMoveWindow,
|
||||||
|
directionResizeWindow,
|
||||||
|
Direction2D(..),
|
||||||
|
P, G, ChangeDim
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import XMonad
|
import XMonad
|
||||||
import XMonad.Prelude (fi)
|
import XMonad.Prelude (fi)
|
||||||
|
import XMonad.Util.Types
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.FloatKeys
|
-- > import XMonad.Actions.FloatKeys
|
||||||
--
|
--
|
||||||
@ -38,14 +42,42 @@ import XMonad.Prelude (fi)
|
|||||||
-- > , ((modm .|. shiftMask, xK_s ), withFocused (keysAbsResizeWindow (10,10) (1024,752)))
|
-- > , ((modm .|. shiftMask, xK_s ), withFocused (keysAbsResizeWindow (10,10) (1024,752)))
|
||||||
-- > , ((modm, xK_a ), withFocused (keysMoveWindowTo (512,384) (1%2,1%2)))
|
-- > , ((modm, xK_a ), withFocused (keysMoveWindowTo (512,384) (1%2,1%2)))
|
||||||
--
|
--
|
||||||
|
-- Using "XMonad.Util.EZConfig" syntax, we can easily build keybindings
|
||||||
|
-- where @M-\<arrow-keys\>@ moves the currently focused window and
|
||||||
|
-- @M-S-\<arrow-keys\>@ resizes it using 'directionMoveWindow' and
|
||||||
|
-- 'directionResizeWindow':
|
||||||
|
--
|
||||||
|
-- > [ ("M-" <> m <> k, withFocused $ f i)
|
||||||
|
-- > | (i, k) <- zip [U, D, R, L] ["<Up>", "<Down>", "<Right>", "<Left>"]
|
||||||
|
-- > , (f, m) <- [(directionMoveWindow 10, ""), (directionResizeWindow 10, "S-")]
|
||||||
|
-- > ]
|
||||||
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
|
-- | @directionMoveWindow delta dir win@ moves the window @win@ by
|
||||||
|
-- @delta@ pixels in direction @dir@.
|
||||||
|
directionMoveWindow :: Int -> Direction2D -> Window -> X ()
|
||||||
|
directionMoveWindow delta dir win = case dir of
|
||||||
|
U -> keysMoveWindow (0, -delta) win
|
||||||
|
D -> keysMoveWindow (0, delta) win
|
||||||
|
R -> keysMoveWindow (delta, 0) win
|
||||||
|
L -> keysMoveWindow (-delta, 0) win
|
||||||
|
|
||||||
|
-- | @directionResizeWindow delta dir win@ resizes the window @win@ by
|
||||||
|
-- @delta@ pixels in direction @dir@.
|
||||||
|
directionResizeWindow :: Int -> Direction2D -> Window -> X ()
|
||||||
|
directionResizeWindow delta dir win = case dir of
|
||||||
|
U -> keysResizeWindow (0, -delta) (0, 0) win
|
||||||
|
D -> keysResizeWindow (0, delta) (0, 0) win
|
||||||
|
R -> keysResizeWindow (delta, 0) (0, 0) win
|
||||||
|
L -> keysResizeWindow (-delta, 0) (0, 0) win
|
||||||
|
|
||||||
-- | @keysMoveWindow (dx, dy)@ moves the window by @dx@ pixels to the
|
-- | @keysMoveWindow (dx, dy)@ moves the window by @dx@ pixels to the
|
||||||
-- right and @dy@ pixels down.
|
-- right and @dy@ pixels down.
|
||||||
keysMoveWindow :: D -> Window -> X ()
|
keysMoveWindow :: ChangeDim -> Window -> X ()
|
||||||
keysMoveWindow (dx,dy) w = whenX (isClient w) $ withDisplay $ \d -> do
|
keysMoveWindow (dx,dy) w = whenX (isClient w) $ withDisplay $ \d ->
|
||||||
wa <- io $ getWindowAttributes d w
|
withWindowAttributes d w $ \wa -> do
|
||||||
io $ moveWindow d w (fi (fi (wa_x wa) + dx))
|
io $ moveWindow d w (fi (fi (wa_x wa) + dx))
|
||||||
(fi (fi (wa_y wa) + dy))
|
(fi (fi (wa_y wa) + dy))
|
||||||
float w
|
float w
|
||||||
@ -61,8 +93,8 @@ keysMoveWindow (dx,dy) w = whenX (isClient w) $ withDisplay $ \d -> do
|
|||||||
-- > keysMoveWindowTo (512,384) (1%2, 1%2) -- center the window on screen
|
-- > keysMoveWindowTo (512,384) (1%2, 1%2) -- center the window on screen
|
||||||
-- > keysMoveWindowTo (1024,0) (1, 0) -- put window in the top right corner
|
-- > keysMoveWindowTo (1024,0) (1, 0) -- put window in the top right corner
|
||||||
keysMoveWindowTo :: P -> G -> Window -> X ()
|
keysMoveWindowTo :: P -> G -> Window -> X ()
|
||||||
keysMoveWindowTo (x,y) (gx, gy) w = whenX (isClient w) $ withDisplay $ \d -> do
|
keysMoveWindowTo (x,y) (gx, gy) w = whenX (isClient w) $ withDisplay $ \d ->
|
||||||
wa <- io $ getWindowAttributes d w
|
withWindowAttributes d w $ \wa -> do
|
||||||
io $ moveWindow d w (x - round (gx * fi (wa_width wa)))
|
io $ moveWindow d w (x - round (gx * fi (wa_width wa)))
|
||||||
(y - round (gy * fi (wa_height wa)))
|
(y - round (gy * fi (wa_height wa)))
|
||||||
float w
|
float w
|
||||||
@ -113,8 +145,8 @@ keysResizeWindow' sh (x,y) (w,h) (dx,dy) (gx, gy) = ((nx, ny), (nw, nh))
|
|||||||
ny = round $ fi y + gy * fi h - gy * fi nh
|
ny = round $ fi y + gy * fi h - gy * fi nh
|
||||||
|
|
||||||
keysMoveResize :: (SizeHints -> P -> D -> a -> b -> (P,D)) -> a -> b -> Window -> X ()
|
keysMoveResize :: (SizeHints -> P -> D -> a -> b -> (P,D)) -> a -> b -> Window -> X ()
|
||||||
keysMoveResize f move resize w = whenX (isClient w) $ withDisplay $ \d -> do
|
keysMoveResize f move resize w = whenX (isClient w) $ withDisplay $ \d ->
|
||||||
wa <- io $ getWindowAttributes d w
|
withWindowAttributes d w $ \wa -> do
|
||||||
sh <- io $ getWMNormalHints d w
|
sh <- io $ getWMNormalHints d w
|
||||||
let wa_dim = (fi $ wa_width wa, fi $ wa_height wa)
|
let wa_dim = (fi $ wa_width wa, fi $ wa_height wa)
|
||||||
wa_pos = (fi $ wa_x wa, fi $ wa_y wa)
|
wa_pos = (fi $ wa_x wa, fi $ wa_y wa)
|
||||||
|
@ -37,7 +37,7 @@ import XMonad.Util.Types (Direction2D(..))
|
|||||||
import XMonad.Actions.AfterDrag
|
import XMonad.Actions.AfterDrag
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.FloatSnap
|
-- > import XMonad.Actions.FloatSnap
|
||||||
--
|
--
|
||||||
@ -53,7 +53,7 @@ import XMonad.Actions.AfterDrag
|
|||||||
-- > , ((modm .|. shiftMask, xK_Down), withFocused $ snapGrow D Nothing)
|
-- > , ((modm .|. shiftMask, xK_Down), withFocused $ snapGrow D Nothing)
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
--
|
--
|
||||||
-- And possibly add appropriate mouse bindings, for example:
|
-- And possibly add appropriate mouse bindings, for example:
|
||||||
--
|
--
|
||||||
@ -92,8 +92,8 @@ snapMagicMouseResize
|
|||||||
-> Maybe Int -- ^ The maximum distance to snap. Use Nothing to not impose any boundary.
|
-> Maybe Int -- ^ The maximum distance to snap. Use Nothing to not impose any boundary.
|
||||||
-> Window -- ^ The window to move and resize.
|
-> Window -- ^ The window to move and resize.
|
||||||
-> X ()
|
-> X ()
|
||||||
snapMagicMouseResize middle collidedist snapdist w = whenX (isClient w) $ withDisplay $ \d -> do
|
snapMagicMouseResize middle collidedist snapdist w = whenX (isClient w) $ withDisplay $ \d ->
|
||||||
wa <- io $ getWindowAttributes d w
|
withWindowAttributes d w $ \wa -> do
|
||||||
(_, _, _, px, py, _, _, _) <- io $ queryPointer d w
|
(_, _, _, px, py, _, _, _) <- io $ queryPointer d w
|
||||||
let x = (fromIntegral px - wx wa)/ww wa
|
let x = (fromIntegral px - wx wa)/ww wa
|
||||||
y = (fromIntegral py - wy wa)/wh wa
|
y = (fromIntegral py - wy wa)/wh wa
|
||||||
@ -119,9 +119,8 @@ snapMagicResize
|
|||||||
-> Maybe Int -- ^ The maximum distance to snap. Use Nothing to not impose any boundary.
|
-> Maybe Int -- ^ The maximum distance to snap. Use Nothing to not impose any boundary.
|
||||||
-> Window -- ^ The window to move and resize.
|
-> Window -- ^ The window to move and resize.
|
||||||
-> X ()
|
-> X ()
|
||||||
snapMagicResize dir collidedist snapdist w = whenX (isClient w) $ withDisplay $ \d -> do
|
snapMagicResize dir collidedist snapdist w = whenX (isClient w) $ withDisplay $ \d ->
|
||||||
wa <- io $ getWindowAttributes d w
|
withWindowAttributes d w $ \wa -> do
|
||||||
|
|
||||||
(xbegin,xend) <- handleAxis True d wa
|
(xbegin,xend) <- handleAxis True d wa
|
||||||
(ybegin,yend) <- handleAxis False d wa
|
(ybegin,yend) <- handleAxis False d wa
|
||||||
|
|
||||||
@ -168,9 +167,8 @@ snapMagicMove
|
|||||||
-> Maybe Int -- ^ The maximum distance to snap. Use Nothing to not impose any boundary.
|
-> Maybe Int -- ^ The maximum distance to snap. Use Nothing to not impose any boundary.
|
||||||
-> Window -- ^ The window to move.
|
-> Window -- ^ The window to move.
|
||||||
-> X ()
|
-> X ()
|
||||||
snapMagicMove collidedist snapdist w = whenX (isClient w) $ withDisplay $ \d -> do
|
snapMagicMove collidedist snapdist w = whenX (isClient w) $ withDisplay $ \d ->
|
||||||
wa <- io $ getWindowAttributes d w
|
withWindowAttributes d w $ \wa -> do
|
||||||
|
|
||||||
nx <- handleAxis True d wa
|
nx <- handleAxis True d wa
|
||||||
ny <- handleAxis False d wa
|
ny <- handleAxis False d wa
|
||||||
|
|
||||||
@ -208,8 +206,8 @@ snapMove U = doSnapMove False True
|
|||||||
snapMove D = doSnapMove False False
|
snapMove D = doSnapMove False False
|
||||||
|
|
||||||
doSnapMove :: Bool -> Bool -> Maybe Int -> Window -> X ()
|
doSnapMove :: Bool -> Bool -> Maybe Int -> Window -> X ()
|
||||||
doSnapMove horiz rev collidedist w = whenX (isClient w) $ withDisplay $ \d -> do
|
doSnapMove horiz rev collidedist w = whenX (isClient w) $ withDisplay $ \d ->
|
||||||
wa <- io $ getWindowAttributes d w
|
withWindowAttributes d w $ \wa -> do
|
||||||
((bl,br,_),(fl,fr,_)) <- getSnap horiz collidedist d w
|
((bl,br,_),(fl,fr,_)) <- getSnap horiz collidedist d w
|
||||||
|
|
||||||
let (mb,mf) = if rev then (bl,fl)
|
let (mb,mf) = if rev then (bl,fl)
|
||||||
@ -247,8 +245,8 @@ snapShrink
|
|||||||
snapShrink = snapResize False
|
snapShrink = snapResize False
|
||||||
|
|
||||||
snapResize :: Bool -> Direction2D -> Maybe Int -> Window -> X ()
|
snapResize :: Bool -> Direction2D -> Maybe Int -> Window -> X ()
|
||||||
snapResize grow dir collidedist w = whenX (isClient w) $ withDisplay $ \d -> do
|
snapResize grow dir collidedist w = whenX (isClient w) $ withDisplay $ \d ->
|
||||||
wa <- io $ getWindowAttributes d w
|
withWindowAttributes d w $ \wa -> do
|
||||||
mr <- case dir of
|
mr <- case dir of
|
||||||
L -> do ((mg,ms,_),(_,_,_)) <- getSnap True collidedist d w
|
L -> do ((mg,ms,_),(_,_,_)) <- getSnap True collidedist d w
|
||||||
return $ case (if grow then mg else ms) of
|
return $ case (if grow then mg else ms) of
|
||||||
@ -285,7 +283,7 @@ getSnap horiz collidedist d w = do
|
|||||||
screen <- W.current <$> gets windowset
|
screen <- W.current <$> gets windowset
|
||||||
let sr = screenRect $ W.screenDetail screen
|
let sr = screenRect $ W.screenDetail screen
|
||||||
wl = W.integrate' . W.stack $ W.workspace screen
|
wl = W.integrate' . W.stack $ W.workspace screen
|
||||||
gr <- ($sr) <$> calcGap (S.fromList [minBound .. maxBound])
|
gr <- ($ sr) <$> calcGap (S.fromList [minBound .. maxBound])
|
||||||
wla <- filter (collides wa) <$> io (mapM (getWindowAttributes d) $ filter (/=w) wl)
|
wla <- filter (collides wa) <$> io (mapM (getWindowAttributes d) $ filter (/=w) wl)
|
||||||
|
|
||||||
return ( neighbours (back wa sr gr wla) (wpos wa)
|
return ( neighbours (back wa sr gr wla) (wpos wa)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.FocusNth
|
-- Module : XMonad.Actions.FocusNth
|
||||||
@ -18,11 +20,12 @@ module XMonad.Actions.FocusNth (
|
|||||||
focusNth,focusNth',
|
focusNth,focusNth',
|
||||||
swapNth,swapNth') where
|
swapNth,swapNth') where
|
||||||
|
|
||||||
import XMonad.StackSet
|
|
||||||
import XMonad
|
import XMonad
|
||||||
|
import XMonad.Prelude
|
||||||
|
import XMonad.StackSet
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- Add the import to your @~\/.xmonad\/xmonad.hs@:
|
-- Add the import to your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.FocusNth
|
-- > import XMonad.Actions.FocusNth
|
||||||
--
|
--
|
||||||
@ -33,15 +36,15 @@ import XMonad
|
|||||||
-- > | (i, k) <- zip [0 .. 8] [xK_1 ..]]
|
-- > | (i, k) <- zip [0 .. 8] [xK_1 ..]]
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | Give focus to the nth window of the current workspace.
|
-- | Give focus to the nth window of the current workspace.
|
||||||
focusNth :: Int -> X ()
|
focusNth :: Int -> X ()
|
||||||
focusNth = windows . modify' . focusNth'
|
focusNth = windows . modify' . focusNth'
|
||||||
|
|
||||||
focusNth' :: Int -> Stack a -> Stack a
|
focusNth' :: Int -> Stack a -> Stack a
|
||||||
focusNth' n s@(Stack _ ls rs) | (n < 0) || (n > length ls + length rs) = s
|
focusNth' n s | n >= 0, (ls, t:rs) <- splitAt n (integrate s) = Stack t (reverse ls) rs
|
||||||
| otherwise = listToStack n (integrate s)
|
| otherwise = s
|
||||||
|
|
||||||
-- | Swap current window with nth. Focus stays in the same position
|
-- | Swap current window with nth. Focus stays in the same position
|
||||||
swapNth :: Int -> X ()
|
swapNth :: Int -> X ()
|
||||||
@ -50,11 +53,5 @@ swapNth = windows . modify' . swapNth'
|
|||||||
swapNth' :: Int -> Stack a -> Stack a
|
swapNth' :: Int -> Stack a -> Stack a
|
||||||
swapNth' n s@(Stack c l r)
|
swapNth' n s@(Stack c l r)
|
||||||
| (n < 0) || (n > length l + length r) || (n == length l) = s
|
| (n < 0) || (n > length l + length r) || (n == length l) = s
|
||||||
| n < length l = let (nl, nc:nr) = splitAt (length l - n - 1) l in Stack nc (nl ++ c : nr) r
|
| n < length l = let (nl, notEmpty -> nc :| nr) = splitAt (length l - n - 1) l in Stack nc (nl ++ c : nr) r
|
||||||
| otherwise = let (nl, nc:nr) = splitAt (n - length l - 1) r in Stack nc l (nl ++ c : nr)
|
| otherwise = let (nl, notEmpty -> nc :| nr) = splitAt (n - length l - 1) r in Stack nc l (nl ++ c : nr)
|
||||||
|
|
||||||
listToStack :: Int -> [a] -> Stack a
|
|
||||||
listToStack n l = Stack t ls rs
|
|
||||||
where
|
|
||||||
(t:rs) = drop n l
|
|
||||||
ls = reverse (take n l)
|
|
||||||
|
@ -97,10 +97,11 @@ import XMonad.Actions.WindowBringer (bringWindow)
|
|||||||
import Text.Printf
|
import Text.Printf
|
||||||
import System.Random (mkStdGen, randomR)
|
import System.Random (mkStdGen, randomR)
|
||||||
import Data.Word (Word8)
|
import Data.Word (Word8)
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.GridSelect
|
-- > import XMonad.Actions.GridSelect
|
||||||
--
|
--
|
||||||
@ -124,7 +125,7 @@ import Data.Word (Word8)
|
|||||||
-- > ...
|
-- > ...
|
||||||
-- > gsconfig1 = def { gs_cellheight = 30, gs_cellwidth = 100 }
|
-- > gsconfig1 = def { gs_cellheight = 30, gs_cellwidth = 100 }
|
||||||
--
|
--
|
||||||
-- An example where 'buildDefaultGSConfig' is used instead of 'defaultGSConfig'
|
-- An example where 'buildDefaultGSConfig' is used instead of 'def'
|
||||||
-- in order to specify a custom colorizer is @gsconfig2@ (found in
|
-- in order to specify a custom colorizer is @gsconfig2@ (found in
|
||||||
-- "XMonad.Actions.GridSelect#Colorizers"):
|
-- "XMonad.Actions.GridSelect#Colorizers"):
|
||||||
--
|
--
|
||||||
@ -142,8 +143,8 @@ import Data.Word (Word8)
|
|||||||
--
|
--
|
||||||
-- Then you can bind to:
|
-- Then you can bind to:
|
||||||
--
|
--
|
||||||
-- > ,((modm, xK_g), goToSelected $ gsconfig2 myWinColorizer)
|
-- > ,((modm, xK_g), goToSelected $ gsconfig2 myWinColorizer)
|
||||||
-- > ,((modm, xK_p), spawnSelected $ spawnSelected defaultColorizer)
|
-- > ,((modm, xK_p), spawnSelected (gsconfig2 defaultColorizer) ["xterm","gvim"])
|
||||||
|
|
||||||
-- $keybindings
|
-- $keybindings
|
||||||
--
|
--
|
||||||
@ -202,10 +203,13 @@ data GSConfig a = GSConfig {
|
|||||||
gs_colorizer :: a -> Bool -> X (String, String),
|
gs_colorizer :: a -> Bool -> X (String, String),
|
||||||
gs_font :: String,
|
gs_font :: String,
|
||||||
gs_navigate :: TwoD a (Maybe a),
|
gs_navigate :: TwoD a (Maybe a),
|
||||||
|
-- ^ Customize key bindings for a GridSelect
|
||||||
gs_rearranger :: Rearranger a,
|
gs_rearranger :: Rearranger a,
|
||||||
gs_originFractX :: Double,
|
gs_originFractX :: Double,
|
||||||
gs_originFractY :: Double,
|
gs_originFractY :: Double,
|
||||||
gs_bordercolor :: String
|
gs_bordercolor :: String,
|
||||||
|
gs_cancelOnEmptyClick :: Bool
|
||||||
|
-- ^ When True, click on empty space will cancel GridSelect
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | That is 'fromClassName' if you are selecting a 'Window', or
|
-- | That is 'fromClassName' if you are selecting a 'Window', or
|
||||||
@ -285,11 +289,7 @@ orderElementmap searchString elements = if not $ null searchString then sortedEl
|
|||||||
|
|
||||||
|
|
||||||
newtype TwoD a b = TwoD { unTwoD :: StateT (TwoDState a) X b }
|
newtype TwoD a b = TwoD { unTwoD :: StateT (TwoDState a) X b }
|
||||||
deriving (Monad,Functor,MonadState (TwoDState a))
|
deriving (Functor, Applicative, Monad, MonadState (TwoDState a))
|
||||||
|
|
||||||
instance Applicative (TwoD a) where
|
|
||||||
(<*>) = ap
|
|
||||||
pure = return
|
|
||||||
|
|
||||||
liftX :: X a1 -> TwoD a a1
|
liftX :: X a1 -> TwoD a a1
|
||||||
liftX = TwoD . lift
|
liftX = TwoD . lift
|
||||||
@ -306,14 +306,14 @@ diamondLayer n =
|
|||||||
r = tr ++ map (\(x,y) -> (y,-x)) tr
|
r = tr ++ map (\(x,y) -> (y,-x)) tr
|
||||||
in r ++ map (negate *** negate) r
|
in r ++ map (negate *** negate) r
|
||||||
|
|
||||||
diamond :: (Enum a, Num a, Eq a) => [(a, a)]
|
diamond :: (Enum a, Num a, Eq a) => Stream (a, a)
|
||||||
diamond = concatMap diamondLayer [0..]
|
diamond = fromList $ concatMap diamondLayer [0..]
|
||||||
|
|
||||||
diamondRestrict :: Integer -> Integer -> Integer -> Integer -> [(Integer, Integer)]
|
diamondRestrict :: Integer -> Integer -> Integer -> Integer -> [(Integer, Integer)]
|
||||||
diamondRestrict x y originX originY =
|
diamondRestrict x y originX originY =
|
||||||
L.filter (\(x',y') -> abs x' <= x && abs y' <= y) .
|
L.filter (\(x',y') -> abs x' <= x && abs y' <= y) .
|
||||||
map (\(x', y') -> (x' + fromInteger originX, y' + fromInteger originY)) .
|
map (\(x', y') -> (x' + fromInteger originX, y' + fromInteger originY)) .
|
||||||
take 1000 $ diamond
|
takeS 1000 $ diamond
|
||||||
|
|
||||||
findInElementMap :: (Eq a) => a -> [(a, b)] -> Maybe (a, b)
|
findInElementMap :: (Eq a) => a -> [(a, b)] -> Maybe (a, b)
|
||||||
findInElementMap pos = find ((== pos) . fst)
|
findInElementMap pos = find ((== pos) . fst)
|
||||||
@ -389,13 +389,20 @@ updateElementsWithColorizer colorizer elementmap = do
|
|||||||
stdHandle :: Event -> TwoD a (Maybe a) -> TwoD a (Maybe a)
|
stdHandle :: Event -> TwoD a (Maybe a) -> TwoD a (Maybe a)
|
||||||
stdHandle ButtonEvent{ ev_event_type = t, ev_x = x, ev_y = y } contEventloop
|
stdHandle ButtonEvent{ ev_event_type = t, ev_x = x, ev_y = y } contEventloop
|
||||||
| t == buttonRelease = do
|
| t == buttonRelease = do
|
||||||
s@TwoDState { td_paneX = px, td_paneY = py,
|
s@TwoDState{ td_paneX = px
|
||||||
td_gsconfig = (GSConfig ch cw _ _ _ _ _ _ _ _) } <- get
|
, td_paneY = py
|
||||||
|
, td_gsconfig = GSConfig{ gs_cellheight = ch
|
||||||
|
, gs_cellwidth = cw
|
||||||
|
, gs_cancelOnEmptyClick = cancelOnEmptyClick
|
||||||
|
}
|
||||||
|
} <- get
|
||||||
let gridX = (fi x - (px - cw) `div` 2) `div` cw
|
let gridX = (fi x - (px - cw) `div` 2) `div` cw
|
||||||
gridY = (fi y - (py - ch) `div` 2) `div` ch
|
gridY = (fi y - (py - ch) `div` 2) `div` ch
|
||||||
case lookup (gridX,gridY) (td_elementmap s) of
|
case lookup (gridX,gridY) (td_elementmap s) of
|
||||||
Just (_,el) -> return (Just el)
|
Just (_,el) -> return (Just el)
|
||||||
Nothing -> contEventloop
|
Nothing -> if cancelOnEmptyClick
|
||||||
|
then return Nothing
|
||||||
|
else contEventloop
|
||||||
| otherwise = contEventloop
|
| otherwise = contEventloop
|
||||||
|
|
||||||
stdHandle ExposeEvent{} contEventloop = updateAllElements >> contEventloop
|
stdHandle ExposeEvent{} contEventloop = updateAllElements >> contEventloop
|
||||||
@ -411,10 +418,11 @@ makeXEventhandler keyhandler = fix $ \me -> join $ liftX $ withDisplay $ \d -> l
|
|||||||
ev <- getEvent e
|
ev <- getEvent e
|
||||||
if ev_event_type ev == keyPress
|
if ev_event_type ev == keyPress
|
||||||
then do
|
then do
|
||||||
(ks,s) <- lookupString $ asKeyEvent e
|
(_, s) <- lookupString $ asKeyEvent e
|
||||||
|
ks <- keycodeToKeysym d (ev_keycode ev) 0
|
||||||
return $ do
|
return $ do
|
||||||
mask <- liftX $ cleanMask (ev_state ev)
|
mask <- liftX $ cleanKeyMask <*> pure (ev_state ev)
|
||||||
keyhandler (fromMaybe xK_VoidSymbol ks, s, mask)
|
keyhandler (ks, s, mask)
|
||||||
else
|
else
|
||||||
return $ stdHandle ev me
|
return $ stdHandle ev me
|
||||||
|
|
||||||
@ -650,7 +658,7 @@ gridselect gsconfig elements =
|
|||||||
liftIO $ mapWindow dpy win
|
liftIO $ mapWindow dpy win
|
||||||
liftIO $ selectInput dpy win (exposureMask .|. keyPressMask .|. buttonReleaseMask)
|
liftIO $ selectInput dpy win (exposureMask .|. keyPressMask .|. buttonReleaseMask)
|
||||||
status <- io $ grabKeyboard dpy win True grabModeAsync grabModeAsync currentTime
|
status <- io $ grabKeyboard dpy win True grabModeAsync grabModeAsync currentTime
|
||||||
io $ grabPointer dpy win True buttonReleaseMask grabModeAsync grabModeAsync none none currentTime
|
void $ io $ grabPointer dpy win True buttonReleaseMask grabModeAsync grabModeAsync none none currentTime
|
||||||
font <- initXMF (gs_font gsconfig)
|
font <- initXMF (gs_font gsconfig)
|
||||||
let screenWidth = toInteger $ rect_width scr
|
let screenWidth = toInteger $ rect_width scr
|
||||||
screenHeight = toInteger $ rect_height scr
|
screenHeight = toInteger $ rect_height scr
|
||||||
@ -661,7 +669,7 @@ gridselect gsconfig elements =
|
|||||||
originPosX = floor $ (gs_originFractX gsconfig - (1/2)) * 2 * fromIntegral restrictX
|
originPosX = floor $ (gs_originFractX gsconfig - (1/2)) * 2 * fromIntegral restrictX
|
||||||
originPosY = floor $ (gs_originFractY gsconfig - (1/2)) * 2 * fromIntegral restrictY
|
originPosY = floor $ (gs_originFractY gsconfig - (1/2)) * 2 * fromIntegral restrictY
|
||||||
coords = diamondRestrict restrictX restrictY originPosX originPosY
|
coords = diamondRestrict restrictX restrictY originPosX originPosY
|
||||||
s = TwoDState { td_curpos = head coords,
|
s = TwoDState { td_curpos = NE.head (notEmpty coords),
|
||||||
td_availSlots = coords,
|
td_availSlots = coords,
|
||||||
td_elements = elements,
|
td_elements = elements,
|
||||||
td_gsconfig = gsconfig,
|
td_gsconfig = gsconfig,
|
||||||
@ -708,7 +716,7 @@ decorateName' w = do
|
|||||||
|
|
||||||
-- | Builds a default gs config from a colorizer function.
|
-- | Builds a default gs config from a colorizer function.
|
||||||
buildDefaultGSConfig :: (a -> Bool -> X (String,String)) -> GSConfig a
|
buildDefaultGSConfig :: (a -> Bool -> X (String,String)) -> GSConfig a
|
||||||
buildDefaultGSConfig col = GSConfig 50 130 10 col "xft:Sans-8" defaultNavigation noRearranger (1/2) (1/2) "white"
|
buildDefaultGSConfig col = GSConfig 50 130 10 col "xft:Sans-8" defaultNavigation noRearranger (1/2) (1/2) "white" True
|
||||||
|
|
||||||
-- | Brings selected window to the current workspace.
|
-- | Brings selected window to the current workspace.
|
||||||
bringSelected :: GSConfig Window -> X ()
|
bringSelected :: GSConfig Window -> X ()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
{-# language DeriveGeneric, DeriveAnyClass #-}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.GroupNavigation
|
-- Module : XMonad.Actions.GroupNavigation
|
||||||
@ -32,25 +33,27 @@ module XMonad.Actions.GroupNavigation ( -- * Usage
|
|||||||
, isOnAnyVisibleWS
|
, isOnAnyVisibleWS
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader (ask, asks)
|
||||||
import Control.Monad.State
|
import Control.Monad.State (gets)
|
||||||
|
import Control.DeepSeq
|
||||||
import Data.Map ((!))
|
import Data.Map ((!))
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import Data.Sequence (Seq, ViewL (EmptyL, (:<)), viewl, (<|), (><), (|>))
|
import Data.Sequence (Seq, ViewL (EmptyL, (:<)), viewl, (<|), (><), (|>))
|
||||||
import qualified Data.Sequence as Seq
|
import qualified Data.Sequence as Seq
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
import Graphics.X11.Types
|
import Graphics.X11.Types
|
||||||
import Prelude hiding (concatMap, drop, elem, filter, null, reverse)
|
import GHC.Generics
|
||||||
|
import Prelude hiding (drop, elem, filter, null, reverse)
|
||||||
import XMonad.Core
|
import XMonad.Core
|
||||||
import XMonad.ManageHook
|
import XMonad.ManageHook
|
||||||
import XMonad.Operations (windows, withFocused)
|
import XMonad.Operations (windows, withFocused)
|
||||||
import XMonad.Prelude (elem, foldl')
|
import XMonad.Prelude (elem, foldl', (>=>))
|
||||||
import qualified XMonad.StackSet as SS
|
import qualified XMonad.StackSet as SS
|
||||||
import qualified XMonad.Util.ExtensibleState as XS
|
import qualified XMonad.Util.ExtensibleState as XS
|
||||||
|
|
||||||
{- $usage
|
{- $usage
|
||||||
|
|
||||||
Import the module into your @~\/.xmonad\/xmonad.hs@:
|
Import the module into your @xmonad.hs@:
|
||||||
|
|
||||||
> import XMonad.Actions.GroupNavigation
|
> import XMonad.Actions.GroupNavigation
|
||||||
|
|
||||||
@ -126,7 +129,7 @@ focusNextMatchOrDo qry act = findM (runQuery qry)
|
|||||||
>=> maybe act (windows . SS.focusWindow)
|
>=> maybe act (windows . SS.focusWindow)
|
||||||
|
|
||||||
-- Returns the list of windows ordered by workspace as specified in
|
-- Returns the list of windows ordered by workspace as specified in
|
||||||
-- ~/.xmonad/xmonad.hs
|
-- @xmonad.hs@.
|
||||||
orderedWindowList :: Direction -> X (Seq Window)
|
orderedWindowList :: Direction -> X (Seq Window)
|
||||||
orderedWindowList History = fmap (\(HistoryDB w ws) -> maybe ws (ws |>) w) XS.get
|
orderedWindowList History = fmap (\(HistoryDB w ws) -> maybe ws (ws |>) w) XS.get
|
||||||
orderedWindowList dir = withWindowSet $ \ss -> do
|
orderedWindowList dir = withWindowSet $ \ss -> do
|
||||||
@ -142,7 +145,7 @@ orderedWindowList dir = withWindowSet $ \ss -> do
|
|||||||
dirfun _ = id
|
dirfun _ = id
|
||||||
rotfun wins x = rotate $ rotateTo (== x) wins
|
rotfun wins x = rotate $ rotateTo (== x) wins
|
||||||
|
|
||||||
-- Returns the ordered workspace list as specified in ~/.xmonad/xmonad.hs
|
-- Returns the ordered workspace list as specified in @xmonad.hs@.
|
||||||
orderedWorkspaceList :: WindowSet -> Seq String -> Seq WindowSpace
|
orderedWorkspaceList :: WindowSet -> Seq String -> Seq WindowSpace
|
||||||
orderedWorkspaceList ss wsids = rotateTo isCurWS wspcs'
|
orderedWorkspaceList ss wsids = rotateTo isCurWS wspcs'
|
||||||
where
|
where
|
||||||
@ -156,7 +159,7 @@ orderedWorkspaceList ss wsids = rotateTo isCurWS wspcs'
|
|||||||
-- The state extension that holds the history information
|
-- The state extension that holds the history information
|
||||||
data HistoryDB = HistoryDB (Maybe Window) -- currently focused window
|
data HistoryDB = HistoryDB (Maybe Window) -- currently focused window
|
||||||
(Seq Window) -- previously focused windows
|
(Seq Window) -- previously focused windows
|
||||||
deriving (Read, Show)
|
deriving (Read, Show, Generic, NFData)
|
||||||
|
|
||||||
instance ExtensionClass HistoryDB where
|
instance ExtensionClass HistoryDB where
|
||||||
|
|
||||||
@ -166,15 +169,15 @@ instance ExtensionClass HistoryDB where
|
|||||||
-- | Action that needs to be executed as a logHook to maintain the
|
-- | Action that needs to be executed as a logHook to maintain the
|
||||||
-- focus history of all windows as the WindowSet changes.
|
-- focus history of all windows as the WindowSet changes.
|
||||||
historyHook :: X ()
|
historyHook :: X ()
|
||||||
historyHook = XS.get >>= updateHistory >>= XS.put
|
historyHook = (XS.put $!) . force =<< updateHistory =<< XS.get
|
||||||
|
|
||||||
-- Updates the history in response to a WindowSet change
|
-- Updates the history in response to a WindowSet change
|
||||||
updateHistory :: HistoryDB -> X HistoryDB
|
updateHistory :: HistoryDB -> X HistoryDB
|
||||||
updateHistory (HistoryDB oldcur oldhist) = withWindowSet $ \ss -> do
|
updateHistory (HistoryDB oldcur oldhist) = withWindowSet $ \ss ->
|
||||||
let newcur = SS.peek ss
|
let newcur = SS.peek ss
|
||||||
wins = Set.fromList $ SS.allWindows ss
|
wins = Set.fromList $ SS.allWindows ss
|
||||||
newhist = Seq.filter (`Set.member` wins) (ins oldcur oldhist)
|
newhist = Seq.filter (`Set.member` wins) (ins oldcur oldhist)
|
||||||
return $ HistoryDB newcur (del newcur newhist)
|
in pure $ HistoryDB newcur (del newcur newhist)
|
||||||
where
|
where
|
||||||
ins x xs = maybe xs (<| xs) x
|
ins x xs = maybe xs (<| xs) x
|
||||||
del x xs = maybe xs (\x' -> Seq.filter (/= x') xs) x
|
del x xs = maybe xs (\x' -> Seq.filter (/= x') xs) x
|
||||||
@ -221,7 +224,7 @@ isOnAnyVisibleWS :: Query Bool
|
|||||||
isOnAnyVisibleWS = do
|
isOnAnyVisibleWS = do
|
||||||
w <- ask
|
w <- ask
|
||||||
ws <- liftX $ gets windowset
|
ws <- liftX $ gets windowset
|
||||||
let allVisible = concat $ maybe [] SS.integrate . SS.stack . SS.workspace <$> SS.current ws:SS.visible ws
|
let allVisible = concatMap (maybe [] SS.integrate . SS.stack . SS.workspace) (SS.current ws:SS.visible ws)
|
||||||
visibleWs = w `elem` allVisible
|
visibleWs = w `elem` allVisible
|
||||||
unfocused = Just w /= SS.peek ws
|
unfocused = Just w /= SS.peek ws
|
||||||
return $ visibleWs && unfocused
|
return $ visibleWs && unfocused
|
||||||
|
@ -148,8 +148,6 @@ dvorakProgrammerKeyRemap =
|
|||||||
|
|
||||||
layoutDvorakShift = map getShift layoutDvorak
|
layoutDvorakShift = map getShift layoutDvorak
|
||||||
layoutDvorakKey = map getKey layoutDvorak
|
layoutDvorakKey = map getKey layoutDvorak
|
||||||
getKey char = let Just index = elemIndex char layoutUs
|
getKey char = fromJust $ (layoutUsKey !?) =<< elemIndex char layoutUs
|
||||||
in layoutUsKey !! index
|
getShift char = fromJust $ (layoutUsShift !?) =<< elemIndex char layoutUs
|
||||||
getShift char = let Just index = elemIndex char layoutUs
|
|
||||||
in layoutUsShift !! index
|
|
||||||
charToMask char = if [char] == "0" then 0 else shiftMask
|
charToMask char = if [char] == "0" then 0 else shiftMask
|
||||||
|
@ -62,7 +62,7 @@ type ExtensionActions = M.Map String (String -> X())
|
|||||||
instance XPrompt CalculatorMode where
|
instance XPrompt CalculatorMode where
|
||||||
showXPrompt CalcMode = "calc %s> "
|
showXPrompt CalcMode = "calc %s> "
|
||||||
commandToComplete CalcMode = id --send the whole string to `calc`
|
commandToComplete CalcMode = id --send the whole string to `calc`
|
||||||
completionFunction CalcMode = \s -> if null s then return [] else
|
completionFunction CalcMode s = if null s then return [] else
|
||||||
lines <$> runProcessWithInput "calc" [s] ""
|
lines <$> runProcessWithInput "calc" [s] ""
|
||||||
modeAction CalcMode _ _ = return () -- do nothing; this might copy the result to the clipboard
|
modeAction CalcMode _ _ = return () -- do nothing; this might copy the result to the clipboard
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ instance XPrompt CalculatorMode where
|
|||||||
instance XPrompt HoogleMode where
|
instance XPrompt HoogleMode where
|
||||||
showXPrompt _ = "hoogle %s> "
|
showXPrompt _ = "hoogle %s> "
|
||||||
commandToComplete _ = id
|
commandToComplete _ = id
|
||||||
completionFunction (HMode pathToHoogleBin' _) = \s -> completionFunctionWith pathToHoogleBin' ["--count","8",s]
|
completionFunction (HMode pathToHoogleBin' _) s = completionFunctionWith pathToHoogleBin' ["--count","8",s]
|
||||||
-- This action calls hoogle again to find the URL corresponding to the autocompleted item
|
-- This action calls hoogle again to find the URL corresponding to the autocompleted item
|
||||||
modeAction (HMode pathToHoogleBin'' browser') query result = do
|
modeAction (HMode pathToHoogleBin'' browser') query result = do
|
||||||
completionsWithLink <- liftIO $ completionFunctionWith pathToHoogleBin'' ["--count","5","--link",query]
|
completionsWithLink <- liftIO $ completionFunctionWith pathToHoogleBin'' ["--count","5","--link",query]
|
||||||
|
@ -36,7 +36,7 @@ import qualified Data.Map as M
|
|||||||
( insert, delete, Map, lookup, empty, filter )
|
( insert, delete, Map, lookup, empty, filter )
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
|
-- You can use this module with the following in your @xmonad.hs@ file:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.LinkWorkspaces
|
-- > import XMonad.Actions.LinkWorkspaces
|
||||||
--
|
--
|
||||||
@ -58,7 +58,7 @@ import qualified Data.Map as M
|
|||||||
-- > , (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]]
|
-- > , (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]]
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
data MessageConfig = MessageConfig { messageFunction :: ScreenId -> [Char] -> [Char] -> [Char] -> X()
|
data MessageConfig = MessageConfig { messageFunction :: ScreenId -> [Char] -> [Char] -> [Char] -> X()
|
||||||
, foreground :: [Char]
|
, foreground :: [Char]
|
||||||
|
@ -42,24 +42,18 @@ module XMonad.Actions.MessageFeedback
|
|||||||
|
|
||||||
-- ** Aliases
|
-- ** Aliases
|
||||||
, sm
|
, sm
|
||||||
|
|
||||||
-- * Backwards Compatibility
|
|
||||||
-- $backwardsCompatibility
|
|
||||||
, send, sendSM, sendSM_
|
|
||||||
, tryInOrder, tryInOrder_
|
|
||||||
, tryMessage, tryMessage_
|
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import XMonad ( Window )
|
import XMonad ( Window )
|
||||||
import XMonad.Core ( X(), Message, SomeMessage(..), LayoutClass(..), windowset, catchX, WorkspaceId, Layout, whenJust )
|
import XMonad.Core ( X(), Message, SomeMessage(..), LayoutClass(..), windowset, catchX, WorkspaceId, Layout, whenJust )
|
||||||
import XMonad.Operations ( updateLayout, windowBracket, modifyWindowSet )
|
import XMonad.Operations ( updateLayout, windowBracket, modifyWindowSet )
|
||||||
import XMonad.Prelude ( isJust, liftA2, void )
|
import XMonad.Prelude
|
||||||
import XMonad.StackSet ( Workspace, current, workspace, layout, tag )
|
import XMonad.StackSet ( Workspace, current, workspace, layout, tag )
|
||||||
|
|
||||||
import Control.Monad.State ( gets )
|
import Control.Monad.State ( gets )
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.MessageFeedback
|
-- > import XMonad.Actions.MessageFeedback
|
||||||
--
|
--
|
||||||
@ -230,46 +224,3 @@ tryMessageWithNoRefreshToCurrent m = void . tryMessageWithNoRefreshToCurrentB m
|
|||||||
-- | Convenience shorthand for 'SomeMessage'.
|
-- | Convenience shorthand for 'SomeMessage'.
|
||||||
sm :: Message a => a -> SomeMessage
|
sm :: Message a => a -> SomeMessage
|
||||||
sm = SomeMessage
|
sm = SomeMessage
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
-- Backwards Compatibility:
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
{-# DEPRECATED send "Use sendMessageB instead." #-}
|
|
||||||
{-# DEPRECATED sendSM "Use sendSomeMessageB instead." #-}
|
|
||||||
{-# DEPRECATED sendSM_ "Use sendSomeMessage instead." #-}
|
|
||||||
{-# DEPRECATED tryInOrder "Use tryInOrderWithNoRefreshToCurrentB instead." #-}
|
|
||||||
{-# DEPRECATED tryInOrder_ "Use tryInOrderWithNoRefreshToCurrent instead." #-}
|
|
||||||
{-# DEPRECATED tryMessage "Use tryMessageWithNoRefreshToCurrentB instead." #-}
|
|
||||||
{-# DEPRECATED tryMessage_ "Use tryMessageWithNoRefreshToCurrent instead." #-}
|
|
||||||
|
|
||||||
-- $backwardsCompatibility
|
|
||||||
-- The following functions exist solely for compatibility with pre-0.14
|
|
||||||
-- releases.
|
|
||||||
|
|
||||||
-- | See 'sendMessageWithNoRefreshToCurrentB'.
|
|
||||||
send :: Message a => a -> X Bool
|
|
||||||
send = sendMessageWithNoRefreshToCurrentB
|
|
||||||
|
|
||||||
-- | See 'sendSomeMessageWithNoRefreshToCurrentB'.
|
|
||||||
sendSM :: SomeMessage -> X Bool
|
|
||||||
sendSM = sendSomeMessageWithNoRefreshToCurrentB
|
|
||||||
|
|
||||||
-- | See 'sendSomeMessageWithNoRefreshToCurrent'.
|
|
||||||
sendSM_ :: SomeMessage -> X ()
|
|
||||||
sendSM_ = sendSomeMessageWithNoRefreshToCurrent
|
|
||||||
|
|
||||||
-- | See 'tryInOrderWithNoRefreshToCurrentB'.
|
|
||||||
tryInOrder :: [SomeMessage] -> X Bool
|
|
||||||
tryInOrder = tryInOrderWithNoRefreshToCurrentB
|
|
||||||
|
|
||||||
-- | See 'tryInOrderWithNoRefreshToCurrent'.
|
|
||||||
tryInOrder_ :: [SomeMessage] -> X ()
|
|
||||||
tryInOrder_ = tryInOrderWithNoRefreshToCurrent
|
|
||||||
|
|
||||||
-- | See 'tryMessageWithNoRefreshToCurrentB'.
|
|
||||||
tryMessage :: (Message a, Message b) => a -> b -> X Bool
|
|
||||||
tryMessage = tryMessageWithNoRefreshToCurrentB
|
|
||||||
|
|
||||||
-- | See 'tryMessageWithNoRefreshToCurrent'.
|
|
||||||
tryMessage_ :: (Message a, Message b) => a -> b -> X ()
|
|
||||||
tryMessage_ = tryMessageWithNoRefreshToCurrent
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
-- Adds actions for minimizing and maximizing windows
|
-- Adds actions for minimizing and maximizing windows
|
||||||
--
|
--
|
||||||
-- This module should be used with "XMonad.Layout.Minimize". Add 'minimize' to your
|
-- This module should be used with "XMonad.Layout.Minimize". Add 'minimize' to your
|
||||||
-- layout modifiers as described in "XMonad.Layout.Minimized" and use actions from
|
-- layout modifiers as described in "XMonad.Layout.Minimize" and use actions from
|
||||||
-- this module
|
-- this module
|
||||||
--
|
--
|
||||||
-- Possible keybindings:
|
-- Possible keybindings:
|
||||||
@ -50,14 +50,18 @@ import qualified Data.Map as M
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- Import this module with "XMonad.Layout.Minimize" and "XMonad.Layout.BoringWindows":
|
-- Import this module with "XMonad.Layout.Minimize" and "XMonad.Layout.BoringWindows":
|
||||||
|
--
|
||||||
-- > import XMonad.Actions.Minimize
|
-- > import XMonad.Actions.Minimize
|
||||||
-- > import XMonad.Layout.Minimize
|
-- > import XMonad.Layout.Minimize
|
||||||
-- > import qualified XMonad.Layout.BoringWindows as BW
|
-- > import qualified XMonad.Layout.BoringWindows as BW
|
||||||
--
|
--
|
||||||
-- Then apply 'minimize' and 'boringWindows' to your layout hook and use some
|
-- Then apply 'minimize' and 'boringWindows' to your layout hook and use some
|
||||||
-- actions from this module:
|
-- actions from this module:
|
||||||
|
--
|
||||||
-- > main = xmonad def { layoutHook = minimize . BW.boringWindows $ whatever }
|
-- > main = xmonad def { layoutHook = minimize . BW.boringWindows $ whatever }
|
||||||
|
--
|
||||||
-- Example keybindings:
|
-- Example keybindings:
|
||||||
|
--
|
||||||
-- > , ((modm, xK_m ), withFocused minimizeWindow )
|
-- > , ((modm, xK_m ), withFocused minimizeWindow )
|
||||||
-- > , ((modm .|. shiftMask, xK_m ), withLastMinimized maximizeWindow)
|
-- > , ((modm .|. shiftMask, xK_m ), withLastMinimized maximizeWindow)
|
||||||
|
|
||||||
|
205
XMonad/Actions/MostRecentlyUsed.hs
Normal file
205
XMonad/Actions/MostRecentlyUsed.hs
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
{-# LANGUAGE NamedFieldPuns, GeneralizedNewtypeDeriving #-}
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Actions.MostRecentlyUsed
|
||||||
|
-- Description : Tab through windows by recency of use.
|
||||||
|
-- Copyright : (c) 2022 L. S. Leary
|
||||||
|
-- License : BSD3-style (see LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : @LSLeary (on github)
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- Based on the Alt+Tab behaviour common outside of xmonad.
|
||||||
|
--
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- --< Imports & Exports >-- {{{
|
||||||
|
|
||||||
|
module XMonad.Actions.MostRecentlyUsed (
|
||||||
|
|
||||||
|
-- * Usage
|
||||||
|
-- $usage
|
||||||
|
|
||||||
|
-- * Interface
|
||||||
|
configureMRU,
|
||||||
|
mostRecentlyUsed,
|
||||||
|
withMostRecentlyUsed,
|
||||||
|
Location(..),
|
||||||
|
|
||||||
|
) where
|
||||||
|
|
||||||
|
-- base
|
||||||
|
import Data.List.NonEmpty (nonEmpty)
|
||||||
|
import Data.IORef (newIORef, readIORef, writeIORef, modifyIORef)
|
||||||
|
import Control.Monad.IO.Class (MonadIO)
|
||||||
|
|
||||||
|
-- mtl
|
||||||
|
import Control.Monad.Trans (lift)
|
||||||
|
import Control.Monad.State (get, put, gets)
|
||||||
|
|
||||||
|
-- containers
|
||||||
|
import qualified Data.Map.Strict as M
|
||||||
|
|
||||||
|
-- xmonad
|
||||||
|
import XMonad
|
||||||
|
( Window, KeySym, keyPress, io
|
||||||
|
, Event (DestroyWindowEvent, UnmapEvent, ev_send_event, ev_window)
|
||||||
|
)
|
||||||
|
import XMonad.Core
|
||||||
|
( X, XConfig(..), windowset, WorkspaceId, ScreenId
|
||||||
|
, ExtensionClass(..), StateExtension(..)
|
||||||
|
, waitingUnmap
|
||||||
|
)
|
||||||
|
import XMonad.Operations (screenWorkspace)
|
||||||
|
import qualified XMonad.StackSet as W
|
||||||
|
|
||||||
|
-- xmonad-contrib
|
||||||
|
import qualified XMonad.Util.ExtensibleConf as XC
|
||||||
|
import qualified XMonad.Util.ExtensibleState as XS
|
||||||
|
import XMonad.Util.PureX
|
||||||
|
(handlingRefresh, curScreenId, curTag, greedyView, view, peek, focusWindow)
|
||||||
|
import XMonad.Util.History (History, origin, event, erase, ledger)
|
||||||
|
import XMonad.Actions.Repeatable (repeatableSt)
|
||||||
|
import XMonad.Prelude
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
-- --< Core Data Types: WindowHistory & Location >-- {{{
|
||||||
|
|
||||||
|
data WindowHistory = WinHist
|
||||||
|
{ busy :: !Bool
|
||||||
|
, hist :: !(History Window Location)
|
||||||
|
} deriving (Show, Read)
|
||||||
|
|
||||||
|
instance ExtensionClass WindowHistory where
|
||||||
|
initialValue = WinHist
|
||||||
|
{ busy = False
|
||||||
|
, hist = origin
|
||||||
|
}
|
||||||
|
extensionType = PersistentExtension
|
||||||
|
|
||||||
|
data Location = Location
|
||||||
|
{ workspace :: !WorkspaceId
|
||||||
|
, screen :: !ScreenId
|
||||||
|
} deriving (Show, Read, Eq, Ord)
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
-- --< Interface >-- {{{
|
||||||
|
|
||||||
|
-- $usage
|
||||||
|
--
|
||||||
|
-- 'configureMRU' must be applied to your config in order for 'mostRecentlyUsed'
|
||||||
|
-- to work.
|
||||||
|
--
|
||||||
|
-- > main :: IO ()
|
||||||
|
-- > main = xmonad . configureMRU . ... $ def
|
||||||
|
-- > { ...
|
||||||
|
-- > }
|
||||||
|
--
|
||||||
|
-- Once that's done, it can be used normally in keybinds:
|
||||||
|
--
|
||||||
|
-- > , ((mod1Mask, xK_Tab), mostRecentlyUsed [xK_Alt_L, xK_Alt_R] xK_Tab)
|
||||||
|
--
|
||||||
|
-- N.B.: This example assumes that 'mod1Mask' corresponds to alt, which is not
|
||||||
|
-- always the case, depending on how your system is configured.
|
||||||
|
|
||||||
|
-- | Configure xmonad to support 'mostRecentlyUsed'.
|
||||||
|
configureMRU :: XConfig l -> XConfig l
|
||||||
|
configureMRU = XC.once f (MRU ()) where
|
||||||
|
f cnf = cnf
|
||||||
|
{ logHook = logHook cnf <> logWinHist
|
||||||
|
, handleEventHook = handleEventHook cnf <> winHistEH
|
||||||
|
}
|
||||||
|
newtype MRU = MRU () deriving Semigroup
|
||||||
|
|
||||||
|
-- | An action to browse through the history of focused windows, taking
|
||||||
|
-- another step back with each tap of the key.
|
||||||
|
mostRecentlyUsed
|
||||||
|
:: [KeySym] -- ^ The 'KeySym's corresponding to the modifier to which the
|
||||||
|
-- action is bound.
|
||||||
|
-> KeySym -- ^ The 'KeySym' corresponding to the key to which the action
|
||||||
|
-- is bound.
|
||||||
|
-> X ()
|
||||||
|
mostRecentlyUsed mods key = do
|
||||||
|
(toUndo, undo) <- undoer
|
||||||
|
let undoably curThing withThing thing = curThing >>= \cur ->
|
||||||
|
when (cur /= thing) $ withThing thing >> toUndo (withThing cur)
|
||||||
|
withMostRecentlyUsed mods key $ \win Location{workspace,screen} ->
|
||||||
|
handlingRefresh $ do
|
||||||
|
undo
|
||||||
|
undoably curScreenId viewScreen screen
|
||||||
|
undoably curTag greedyView workspace
|
||||||
|
mi <- gets (W.findTag win . windowset)
|
||||||
|
for_ mi $ \i -> do
|
||||||
|
undoably curTag greedyView i
|
||||||
|
mfw <- peek
|
||||||
|
for_ mfw $ \fw -> do
|
||||||
|
undoably (pure fw) focusWindow win
|
||||||
|
where
|
||||||
|
undoer :: (MonadIO m, Monoid a) => m (m a -> m (), m a)
|
||||||
|
undoer = do
|
||||||
|
ref <- io . newIORef $ pure mempty
|
||||||
|
let toUndo = io . modifyIORef ref . liftA2 (<>)
|
||||||
|
undo = join (io $ readIORef ref)
|
||||||
|
<* io (writeIORef ref $ pure mempty)
|
||||||
|
pure (toUndo, undo)
|
||||||
|
viewScreen :: ScreenId -> X Any
|
||||||
|
viewScreen scr = screenWorkspace scr >>= foldMap view
|
||||||
|
|
||||||
|
-- | A version of 'mostRecentlyUsed' that allows you to customise exactly what
|
||||||
|
-- is done with each window you tab through (the default being to visit its
|
||||||
|
-- previous 'Location' and give it focus).
|
||||||
|
withMostRecentlyUsed
|
||||||
|
:: [KeySym] -- ^ The 'KeySym's corresponding to the
|
||||||
|
-- modifier to which the action is bound.
|
||||||
|
-> KeySym -- ^ The 'KeySym' corresponding to the key to
|
||||||
|
-- which the action is bound.
|
||||||
|
-> (Window -> Location -> X ()) -- ^ The function applied to each window.
|
||||||
|
-> X ()
|
||||||
|
withMostRecentlyUsed mods tab preview = do
|
||||||
|
wh@WinHist{busy,hist} <- XS.get
|
||||||
|
unless busy $ do
|
||||||
|
XS.put wh{ busy = True }
|
||||||
|
|
||||||
|
for_ (nonEmpty $ ledger hist) $ \ne -> do
|
||||||
|
mfw <- gets (W.peek . windowset)
|
||||||
|
let iSt = case cycleS ne of
|
||||||
|
(w, _) :~ s | mfw == Just w -> s
|
||||||
|
s -> s
|
||||||
|
repeatableSt iSt mods tab $ \t s ->
|
||||||
|
when (t == keyPress && s == tab) (pop >>= lift . uncurry preview)
|
||||||
|
|
||||||
|
XS.modify $ \ws@WinHist{} -> ws{ busy = False }
|
||||||
|
logWinHist
|
||||||
|
where
|
||||||
|
pop = do
|
||||||
|
h :~ t <- get
|
||||||
|
put t $> h
|
||||||
|
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
-- --< Raw Config >-- {{{
|
||||||
|
|
||||||
|
logWinHist :: X ()
|
||||||
|
logWinHist = do
|
||||||
|
wh@WinHist{busy,hist} <- XS.get
|
||||||
|
unless busy $ do
|
||||||
|
cs <- gets (W.current . windowset)
|
||||||
|
let cws = W.workspace cs
|
||||||
|
for_ (W.stack cws) $ \st -> do
|
||||||
|
let location = Location{ workspace = W.tag cws, screen = W.screen cs }
|
||||||
|
XS.put wh{ hist = event (W.focus st) location hist }
|
||||||
|
|
||||||
|
winHistEH :: Event -> X All
|
||||||
|
winHistEH ev = All True <$ case ev of
|
||||||
|
UnmapEvent{ ev_send_event = synth, ev_window = w } -> do
|
||||||
|
e <- gets (fromMaybe 0 . M.lookup w . waitingUnmap)
|
||||||
|
when (synth || e == 0) (collect w)
|
||||||
|
DestroyWindowEvent{ ev_window = w } -> collect w
|
||||||
|
_ -> pure ()
|
||||||
|
where collect w = XS.modify $ \wh@WinHist{hist} -> wh{ hist = erase w hist }
|
||||||
|
|
||||||
|
-- }}}
|
@ -32,7 +32,7 @@ import Data.Map (Map)
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.MouseGestures
|
-- > import XMonad.Actions.MouseGestures
|
||||||
-- > import qualified XMonad.StackSet as W
|
-- > import qualified XMonad.StackSet as W
|
||||||
@ -79,17 +79,11 @@ gauge :: (Direction2D -> X ()) -> Pos -> IORef (Maybe (Direction2D, Pos)) -> Pos
|
|||||||
gauge hook op st nx ny = do
|
gauge hook op st nx ny = do
|
||||||
let np = (nx, ny)
|
let np = (nx, ny)
|
||||||
stx <- io $ readIORef st
|
stx <- io $ readIORef st
|
||||||
let
|
let pivot = maybe op snd stx
|
||||||
(~(Just od), pivot) = case stx of
|
when (significant np pivot) $ do
|
||||||
Nothing -> (Nothing, op)
|
let d' = dir pivot np
|
||||||
Just (d, zp) -> (Just d, zp)
|
when ((fst <$> stx) /= Just d') $ hook d'
|
||||||
cont = do
|
io $ writeIORef st (Just (d', np))
|
||||||
guard $ significant np pivot
|
|
||||||
return $ do
|
|
||||||
let d' = dir pivot np
|
|
||||||
when (isNothing stx || od /= d') $ hook d'
|
|
||||||
io $ writeIORef st (Just (d', np))
|
|
||||||
fromMaybe (return ()) cont
|
|
||||||
where
|
where
|
||||||
significant a b = delta a b >= 10
|
significant a b = delta a b >= 10
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ import XMonad.Util.XUtils
|
|||||||
-- "XMonad.Layout.SimpleFloat" or "XMonad.Layout.DecorationMadness".
|
-- "XMonad.Layout.SimpleFloat" or "XMonad.Layout.DecorationMadness".
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your
|
-- You can use this module with the following in your
|
||||||
-- @~\/.xmonad\/xmonad.hs@:
|
-- @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.MouseResize
|
-- > import XMonad.Actions.MouseResize
|
||||||
-- > import XMonad.Layout.WindowArranger
|
-- > import XMonad.Layout.WindowArranger
|
||||||
@ -50,9 +50,9 @@ import XMonad.Util.XUtils
|
|||||||
--
|
--
|
||||||
-- > main = xmonad def { layoutHook = myLayout }
|
-- > main = xmonad def { layoutHook = myLayout }
|
||||||
--
|
--
|
||||||
-- For more detailed instructions on editing the layoutHook see:
|
-- For more detailed instructions on editing the layoutHook see
|
||||||
--
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial> and
|
||||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
-- "XMonad.Doc.Extending#Editing_the_layout_hook".
|
||||||
|
|
||||||
mouseResize :: l a -> ModifiedLayout MouseResize l a
|
mouseResize :: l a -> ModifiedLayout MouseResize l a
|
||||||
mouseResize = ModifiedLayout (MR [])
|
mouseResize = ModifiedLayout (MR [])
|
||||||
|
@ -46,7 +46,6 @@ module XMonad.Actions.Navigation2D ( -- * Usage
|
|||||||
, sideNavigation
|
, sideNavigation
|
||||||
, sideNavigationWithBias
|
, sideNavigationWithBias
|
||||||
, hybridOf
|
, hybridOf
|
||||||
, hybridNavigation
|
|
||||||
, fullScreenRect
|
, fullScreenRect
|
||||||
, singleWindowRect
|
, singleWindowRect
|
||||||
, switchLayer
|
, switchLayer
|
||||||
@ -67,6 +66,7 @@ import qualified XMonad.StackSet as W
|
|||||||
import qualified XMonad.Util.ExtensibleState as XS
|
import qualified XMonad.Util.ExtensibleState as XS
|
||||||
import XMonad.Util.EZConfig (additionalKeys, additionalKeysP)
|
import XMonad.Util.EZConfig (additionalKeys, additionalKeysP)
|
||||||
import XMonad.Util.Types
|
import XMonad.Util.Types
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- #Usage#
|
-- #Usage#
|
||||||
@ -85,7 +85,7 @@ import XMonad.Util.Types
|
|||||||
-- layers and allows customization of the navigation strategy for the tiled
|
-- layers and allows customization of the navigation strategy for the tiled
|
||||||
-- layer based on the layout currently in effect.
|
-- layer based on the layout currently in effect.
|
||||||
--
|
--
|
||||||
-- You can use this module with (a subset of) the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with (a subset of) the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.Navigation2D
|
-- > import XMonad.Actions.Navigation2D
|
||||||
--
|
--
|
||||||
@ -98,7 +98,15 @@ import XMonad.Util.Types
|
|||||||
-- > False
|
-- > False
|
||||||
-- > $ def
|
-- > $ def
|
||||||
--
|
--
|
||||||
-- Alternatively, you can use navigation2DP:
|
-- /NOTE/: the @def@ argument to 'navigation2D' contains the strategy
|
||||||
|
-- that decides which windows actually get selected. While the default
|
||||||
|
-- behaviour tries to keep them into account, if you use modules that
|
||||||
|
-- influence tiling in some way, like "XMonad.Layout.Spacing" or
|
||||||
|
-- "XMonad.Layout.Gaps", you should think about using a different
|
||||||
|
-- strategy, if you find the default behaviour to be unnatural. Check
|
||||||
|
-- out the [finer points](#g:Finer_Points) below for more information.
|
||||||
|
--
|
||||||
|
-- Alternatively to 'navigation2D', you can use 'navigation2DP':
|
||||||
--
|
--
|
||||||
-- > main = xmonad $ navigation2DP def
|
-- > main = xmonad $ navigation2DP def
|
||||||
-- > ("<Up>", "<Left>", "<Down>", "<Right>")
|
-- > ("<Up>", "<Left>", "<Down>", "<Right>")
|
||||||
@ -108,7 +116,7 @@ import XMonad.Util.Types
|
|||||||
-- > $ def
|
-- > $ def
|
||||||
--
|
--
|
||||||
-- That's it. If instead you'd like more control, you can combine
|
-- That's it. If instead you'd like more control, you can combine
|
||||||
-- withNavigation2DConfig and additionalNav2DKeys or additionalNav2DKeysP:
|
-- 'withNavigation2DConfig' and 'additionalNav2DKeys' or 'additionalNav2DKeysP':
|
||||||
--
|
--
|
||||||
-- > main = xmonad $ withNavigation2DConfig def
|
-- > main = xmonad $ withNavigation2DConfig def
|
||||||
-- > $ additionalNav2DKeys (xK_Up, xK_Left, xK_Down, xK_Right)
|
-- > $ additionalNav2DKeys (xK_Up, xK_Left, xK_Down, xK_Right)
|
||||||
@ -162,7 +170,7 @@ import XMonad.Util.Types
|
|||||||
--
|
--
|
||||||
-- For detailed instruction on editing the key binding see:
|
-- For detailed instruction on editing the key binding see:
|
||||||
--
|
--
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- $finer_points
|
-- $finer_points
|
||||||
-- #Finer_Points#
|
-- #Finer_Points#
|
||||||
@ -179,9 +187,19 @@ import XMonad.Util.Types
|
|||||||
-- values in the above example to 'True'. You could also decide you want
|
-- values in the above example to 'True'. You could also decide you want
|
||||||
-- wrapping only for a subset of the operations and no wrapping for others.
|
-- wrapping only for a subset of the operations and no wrapping for others.
|
||||||
--
|
--
|
||||||
-- By default, all layouts use the 'defaultTiledNavigation' strategy specified
|
-- By default, all layouts use the 'defaultTiledNavigation' strategy
|
||||||
-- in the 'Navigation2DConfig' (by default, line navigation is used). To
|
-- specified in the 'Navigation2DConfig' (by default, line navigation is
|
||||||
-- override this behaviour for some layouts, add a pair (\"layout name\",
|
-- used). Many more navigation strategies are available; some may feel
|
||||||
|
-- more natural, depending on the layout and user:
|
||||||
|
--
|
||||||
|
-- * 'lineNavigation'
|
||||||
|
-- * 'centerNavigation'
|
||||||
|
-- * 'sideNavigation'
|
||||||
|
-- * 'sideNavigationWithBias'
|
||||||
|
--
|
||||||
|
-- There is also the ability to combine two strategies with 'hybridOf'.
|
||||||
|
--
|
||||||
|
-- To override the default behaviour for some layouts, add a pair (\"layout name\",
|
||||||
-- navigation strategy) to the 'layoutNavigation' list in the
|
-- navigation strategy) to the 'layoutNavigation' list in the
|
||||||
-- 'Navigation2DConfig', where \"layout name\" is the string reported by the
|
-- 'Navigation2DConfig', where \"layout name\" is the string reported by the
|
||||||
-- layout's description method (normally what is shown as the layout name in
|
-- layout's description method (normally what is shown as the layout name in
|
||||||
@ -327,7 +345,7 @@ centerNavigation = N 2 doCenterNavigation
|
|||||||
-- and push it to the right until it intersects with at least one other window.
|
-- and push it to the right until it intersects with at least one other window.
|
||||||
-- Of those windows, one with a point that is the closest to the centre of the
|
-- Of those windows, one with a point that is the closest to the centre of the
|
||||||
-- line (+1) is selected. This is probably the most intuitive strategy for the
|
-- line (+1) is selected. This is probably the most intuitive strategy for the
|
||||||
-- tiled layer when using XMonad.Layout.Spacing.
|
-- tiled layer when using "XMonad.Layout.Spacing".
|
||||||
sideNavigation :: Navigation2D
|
sideNavigation :: Navigation2D
|
||||||
sideNavigation = N 1 (doSideNavigationWithBias 1)
|
sideNavigation = N 1 (doSideNavigationWithBias 1)
|
||||||
|
|
||||||
@ -359,10 +377,6 @@ hybridOf (N g1 s1) (N g2 s2) = N (max g1 g2) $ applyToBoth s1 s2
|
|||||||
where
|
where
|
||||||
applyToBoth f g a b c = f a b c <|> g a b c
|
applyToBoth f g a b c = f a b c <|> g a b c
|
||||||
|
|
||||||
{-# DEPRECATED hybridNavigation "Use hybridOf with lineNavigation and centerNavigation as arguments." #-}
|
|
||||||
hybridNavigation :: Navigation2D
|
|
||||||
hybridNavigation = hybridOf lineNavigation centerNavigation
|
|
||||||
|
|
||||||
-- | Stores the configuration of directional navigation. The 'Default' instance
|
-- | Stores the configuration of directional navigation. The 'Default' instance
|
||||||
-- uses line navigation for the tiled layer and for navigation between screens,
|
-- uses line navigation for the tiled layer and for navigation between screens,
|
||||||
-- and center navigation for the float layer. No custom navigation strategies
|
-- and center navigation for the float layer. No custom navigation strategies
|
||||||
@ -388,7 +402,7 @@ data Navigation2DConfig = Navigation2DConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- | Shorthand for the tedious screen type
|
-- | Shorthand for the tedious screen type
|
||||||
type Screen = W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
|
type Screen = WindowScreen
|
||||||
|
|
||||||
-- | Convenience function for enabling Navigation2D with typical keybindings.
|
-- | Convenience function for enabling Navigation2D with typical keybindings.
|
||||||
-- Takes a Navigation2DConfig, an (up, left, down, right) tuple, a mapping from
|
-- Takes a Navigation2DConfig, an (up, left, down, right) tuple, a mapping from
|
||||||
@ -433,7 +447,7 @@ additionalNav2DKeys (u, l, d, r) modifiers wrap =
|
|||||||
-- mapping from key prefix to action, and a bool to indicate if wrapping should
|
-- mapping from key prefix to action, and a bool to indicate if wrapping should
|
||||||
-- occur, and returns a function from XConfig to XConfig. Example:
|
-- occur, and returns a function from XConfig to XConfig. Example:
|
||||||
--
|
--
|
||||||
-- > additionalNav2DKeysP def ("w", "a", "s", "d") [("M-", windowGo), ("M-S-", windowSwap)] False myConfig
|
-- > additionalNav2DKeysP ("w", "a", "s", "d") [("M-", windowGo), ("M-S-", windowSwap)] False myConfig
|
||||||
additionalNav2DKeysP :: (String, String, String, String) -> [(String, Direction2D -> Bool -> X ())] ->
|
additionalNav2DKeysP :: (String, String, String, String) -> [(String, Direction2D -> Bool -> X ())] ->
|
||||||
Bool -> XConfig l -> XConfig l
|
Bool -> XConfig l -> XConfig l
|
||||||
additionalNav2DKeysP (u, l, d, r) modifiers wrap =
|
additionalNav2DKeysP (u, l, d, r) modifiers wrap =
|
||||||
@ -451,7 +465,7 @@ withNavigation2DConfig conf2d xconf = xconf { startupHook = startupHook xconf
|
|||||||
}
|
}
|
||||||
|
|
||||||
instance Default Navigation2DConfig where
|
instance Default Navigation2DConfig where
|
||||||
def = Navigation2DConfig { defaultTiledNavigation = lineNavigation
|
def = Navigation2DConfig { defaultTiledNavigation = hybridOf lineNavigation sideNavigation
|
||||||
, floatNavigation = centerNavigation
|
, floatNavigation = centerNavigation
|
||||||
, screenNavigation = lineNavigation
|
, screenNavigation = lineNavigation
|
||||||
, layoutNavigation = []
|
, layoutNavigation = []
|
||||||
@ -616,11 +630,8 @@ actOnScreens act wrap = withWindowSet $ \winset -> do
|
|||||||
|
|
||||||
-- | Determines whether a given window is mapped
|
-- | Determines whether a given window is mapped
|
||||||
isMapped :: Window -> X Bool
|
isMapped :: Window -> X Bool
|
||||||
isMapped win = withDisplay
|
isMapped = fmap (maybe False ((waIsUnmapped /=) . wa_map_state))
|
||||||
$ \dpy -> io
|
. safeGetWindowAttributes
|
||||||
$ (waIsUnmapped /=)
|
|
||||||
. wa_map_state
|
|
||||||
<$> getWindowAttributes dpy win
|
|
||||||
|
|
||||||
----------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------
|
||||||
----------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------
|
||||||
@ -773,8 +784,7 @@ doCenterNavigation dir (cur, rect) winrects
|
|||||||
|
|
||||||
-- All the points that coincide with the current center and succeed it
|
-- All the points that coincide with the current center and succeed it
|
||||||
-- in the (appropriately ordered) window stack.
|
-- in the (appropriately ordered) window stack.
|
||||||
onCtr' = L.tail $ L.dropWhile ((cur /=) . fst) onCtr
|
onCtr' = L.drop 1 $ L.dropWhile ((cur /=) . fst) onCtr
|
||||||
-- tail should be safe here because cur should be in onCtr
|
|
||||||
|
|
||||||
-- All the points that do not coincide with the current center and which
|
-- All the points that do not coincide with the current center and which
|
||||||
-- lie in the (rotated) right cone.
|
-- lie in the (rotated) right cone.
|
||||||
@ -824,8 +834,7 @@ doSideNavigationWithBias bias dir (cur, rect)
|
|||||||
rHalfPiCC r = SideRect (-y2 r) (-y1 r) (x1 r) (x2 r)
|
rHalfPiCC r = SideRect (-y2 r) (-y1 r) (x1 r) (x2 r)
|
||||||
|
|
||||||
-- Apply the above function until d becomes synonymous with R (wolog).
|
-- Apply the above function until d becomes synonymous with R (wolog).
|
||||||
rotateToR d = let (_, _:l) = break (d ==) [U, L, D, R]
|
rotateToR d = fromJust . lookup d . zip [R, D, L, U] . iterate rHalfPiCC
|
||||||
in foldr (const $ (.) rHalfPiCC) id l
|
|
||||||
|
|
||||||
transform = rotateToR dir . translate . toSR
|
transform = rotateToR dir . translate . toSR
|
||||||
|
|
||||||
@ -875,8 +884,8 @@ swap win winset = W.focusWindow cur
|
|||||||
-- Reconstruct the workspaces' window stacks to reflect the swap.
|
-- Reconstruct the workspaces' window stacks to reflect the swap.
|
||||||
newvisws = zipWith (\ws wns -> ws { W.stack = W.differentiate wns }) visws newwins
|
newvisws = zipWith (\ws wns -> ws { W.stack = W.differentiate wns }) visws newwins
|
||||||
newscrs = zipWith (\scr ws -> scr { W.workspace = ws }) scrs newvisws
|
newscrs = zipWith (\scr ws -> scr { W.workspace = ws }) scrs newvisws
|
||||||
newwinset = winset { W.current = head newscrs
|
newwinset = winset { W.current = NE.head (notEmpty newscrs) -- Always at least one screen.
|
||||||
, W.visible = tail newscrs
|
, W.visible = drop 1 newscrs
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | Calculates the center of a rectangle
|
-- | Calculates the center of a rectangle
|
||||||
|
@ -27,8 +27,7 @@ import XMonad
|
|||||||
toggleBorder :: Window -> X ()
|
toggleBorder :: Window -> X ()
|
||||||
toggleBorder w = do
|
toggleBorder w = do
|
||||||
bw <- asks (borderWidth . config)
|
bw <- asks (borderWidth . config)
|
||||||
withDisplay $ \d -> io $ do
|
withDisplay $ \d -> withWindowAttributes d w $ \wa -> io $
|
||||||
cw <- wa_border_width <$> getWindowAttributes d w
|
if wa_border_width wa == 0
|
||||||
if cw == 0
|
|
||||||
then setWindowBorderWidth d w bw
|
then setWindowBorderWidth d w bw
|
||||||
else setWindowBorderWidth d w 0
|
else setWindowBorderWidth d w 0
|
||||||
|
@ -1,155 +1,187 @@
|
|||||||
-----------------------------------------------------------------------------
|
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.OnScreen
|
-- Module : XMonad.Actions.OnScreen
|
||||||
-- Description : Control workspaces on different screens (in xinerama mode).
|
-- Description : Control workspaces on different screens (in xinerama mode).
|
||||||
-- Copyright : (c) 2009 Nils Schweinsberg
|
-- Copyright : (c) 2009-2025 Nils Schweinsberg
|
||||||
-- License : BSD3-style (see LICENSE)
|
-- License : BSD3-style (see LICENSE)
|
||||||
--
|
--
|
||||||
-- Maintainer : Nils Schweinsberg <mail@n-sch.de>
|
-- Maintainer : Nils Schweinsberg <mail@nils.cc>
|
||||||
-- Stability : unstable
|
-- Stability : unstable
|
||||||
-- Portability : unportable
|
-- Portability : unportable
|
||||||
--
|
--
|
||||||
-- Control workspaces on different screens (in xinerama mode).
|
-- Control workspaces on different screens (in xinerama mode).
|
||||||
--
|
module XMonad.Actions.OnScreen
|
||||||
-----------------------------------------------------------------------------
|
( -- * Usage
|
||||||
|
|
||||||
module XMonad.Actions.OnScreen (
|
|
||||||
-- * Usage
|
|
||||||
-- $usage
|
-- $usage
|
||||||
onScreen
|
onScreen,
|
||||||
, onScreen'
|
onScreen',
|
||||||
, Focus(..)
|
Focus (..),
|
||||||
, viewOnScreen
|
viewOnScreen,
|
||||||
, greedyViewOnScreen
|
greedyViewOnScreen,
|
||||||
, onlyOnScreen
|
onlyOnScreen,
|
||||||
, toggleOnScreen
|
toggleOnScreen,
|
||||||
, toggleGreedyOnScreen
|
toggleGreedyOnScreen,
|
||||||
) where
|
)
|
||||||
|
where
|
||||||
|
|
||||||
import XMonad
|
import XMonad
|
||||||
import XMonad.Prelude (fromMaybe, guard)
|
import XMonad.Prelude (empty, fromMaybe, guard)
|
||||||
import XMonad.StackSet hiding (new)
|
import XMonad.StackSet hiding (new)
|
||||||
|
|
||||||
|
|
||||||
-- | Focus data definitions
|
-- | Focus data definitions
|
||||||
data Focus = FocusNew -- ^ always focus the new screen
|
data Focus
|
||||||
| FocusCurrent -- ^ always keep the focus on the current screen
|
= -- | always focus the new screen
|
||||||
| FocusTag WorkspaceId -- ^ always focus tag i on the new stack
|
FocusNew
|
||||||
| FocusTagVisible WorkspaceId -- ^ focus tag i only if workspace with tag i is visible on the old stack
|
| -- | always keep the focus on the current screen
|
||||||
|
FocusCurrent
|
||||||
|
| -- | always focus tag i on the new stack
|
||||||
|
FocusTag WorkspaceId
|
||||||
|
| -- | focus tag i only if workspace with tag i is visible on the old stack
|
||||||
|
FocusTagVisible WorkspaceId
|
||||||
|
|
||||||
-- | Run any function that modifies the stack on a given screen. This function
|
-- | Run any function that modifies the stack on a given screen. This function
|
||||||
-- will also need to know which Screen to focus after the function has been
|
-- will also need to know which Screen to focus after the function has been
|
||||||
-- run.
|
-- run.
|
||||||
onScreen :: (WindowSet -> WindowSet) -- ^ function to run
|
onScreen ::
|
||||||
-> Focus -- ^ what to do with the focus
|
-- | function to run
|
||||||
-> ScreenId -- ^ screen id
|
(WindowSet -> WindowSet) ->
|
||||||
-> WindowSet -- ^ current stack
|
-- | what to do with the focus
|
||||||
-> WindowSet
|
Focus ->
|
||||||
|
-- | screen id
|
||||||
|
ScreenId ->
|
||||||
|
-- | current stack
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
onScreen f foc sc st = fromMaybe st $ do
|
onScreen f foc sc st = fromMaybe st $ do
|
||||||
ws <- lookupWorkspace sc st
|
ws <- lookupWorkspace sc st
|
||||||
|
|
||||||
let fStack = f $ view ws st
|
let fStack = f $ view ws st
|
||||||
|
|
||||||
return $ setFocus foc st fStack
|
|
||||||
|
|
||||||
|
return $ setFocus foc st fStack
|
||||||
|
|
||||||
-- set focus for new stack
|
-- set focus for new stack
|
||||||
setFocus :: Focus
|
setFocus ::
|
||||||
-> WindowSet -- ^ old stack
|
Focus ->
|
||||||
-> WindowSet -- ^ new stack
|
-- | old stack
|
||||||
-> WindowSet
|
WindowSet ->
|
||||||
setFocus FocusNew _ new = new
|
-- | new stack
|
||||||
setFocus FocusCurrent old new =
|
WindowSet ->
|
||||||
case lookupWorkspace (screen $ current old) new of
|
WindowSet
|
||||||
Nothing -> new
|
setFocus FocusNew _ new = new
|
||||||
Just i -> view i new
|
setFocus FocusCurrent old new =
|
||||||
setFocus (FocusTag i) _ new = view i new
|
case lookupWorkspace (screen $ current old) new of
|
||||||
|
Nothing -> new
|
||||||
|
Just i -> view i new
|
||||||
|
setFocus (FocusTag i) _ new = view i new
|
||||||
setFocus (FocusTagVisible i) old new =
|
setFocus (FocusTagVisible i) old new =
|
||||||
if i `elem` map (tag . workspace) (visible old)
|
if i `elem` map (tag . workspace) (visible old)
|
||||||
then setFocus (FocusTag i) old new
|
then setFocus (FocusTag i) old new
|
||||||
else setFocus FocusCurrent old new
|
else setFocus FocusCurrent old new
|
||||||
|
|
||||||
-- | A variation of @onScreen@ which will take any @X ()@ function and run it
|
-- | A variation of @onScreen@ which will take any @X ()@ function and run it
|
||||||
-- on the given screen.
|
-- on the given screen.
|
||||||
-- Warning: This function will change focus even if the function it's supposed
|
-- Warning: This function will change focus even if the function it's supposed
|
||||||
-- to run doesn't succeed.
|
-- to run doesn't succeed.
|
||||||
onScreen' :: X () -- ^ X function to run
|
onScreen' ::
|
||||||
-> Focus -- ^ focus
|
-- | X function to run
|
||||||
-> ScreenId -- ^ screen id
|
X () ->
|
||||||
-> X ()
|
-- | focus
|
||||||
|
Focus ->
|
||||||
|
-- | screen id
|
||||||
|
ScreenId ->
|
||||||
|
X ()
|
||||||
onScreen' x foc sc = do
|
onScreen' x foc sc = do
|
||||||
st <- gets windowset
|
st <- gets windowset
|
||||||
case lookupWorkspace sc st of
|
case lookupWorkspace sc st of
|
||||||
Nothing -> return ()
|
Nothing -> return ()
|
||||||
Just ws -> do
|
Just ws -> do
|
||||||
windows $ view ws
|
windows $ view ws
|
||||||
x
|
x
|
||||||
windows $ setFocus foc st
|
windows $ setFocus foc st
|
||||||
|
|
||||||
|
|
||||||
-- | Switch to workspace @i@ on screen @sc@. If @i@ is visible use @view@ to
|
-- | Switch to workspace @i@ on screen @sc@. If @i@ is visible use @view@ to
|
||||||
-- switch focus to the workspace @i@.
|
-- switch focus to the workspace @i@.
|
||||||
viewOnScreen :: ScreenId -- ^ screen id
|
viewOnScreen ::
|
||||||
-> WorkspaceId -- ^ index of the workspace
|
-- | screen id
|
||||||
-> WindowSet -- ^ current stack
|
ScreenId ->
|
||||||
-> WindowSet
|
-- | index of the workspace
|
||||||
|
WorkspaceId ->
|
||||||
|
-- | current stack
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
viewOnScreen sid i =
|
viewOnScreen sid i =
|
||||||
onScreen (view i) (FocusTag i) sid
|
onScreen (view i) (FocusTag i) sid
|
||||||
|
|
||||||
-- | Switch to workspace @i@ on screen @sc@. If @i@ is visible use @greedyView@
|
-- | Switch to workspace @i@ on screen @sc@. If @i@ is visible use @greedyView@
|
||||||
-- to switch the current workspace with workspace @i@.
|
-- to switch the current workspace with workspace @i@.
|
||||||
greedyViewOnScreen :: ScreenId -- ^ screen id
|
greedyViewOnScreen ::
|
||||||
-> WorkspaceId -- ^ index of the workspace
|
-- | screen id
|
||||||
-> WindowSet -- ^ current stack
|
ScreenId ->
|
||||||
-> WindowSet
|
-- | index of the workspace
|
||||||
|
WorkspaceId ->
|
||||||
|
-- | current stack
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
greedyViewOnScreen sid i =
|
greedyViewOnScreen sid i =
|
||||||
onScreen (greedyView i) (FocusTagVisible i) sid
|
onScreen (greedyView i) (FocusTagVisible i) sid
|
||||||
|
|
||||||
-- | Switch to workspace @i@ on screen @sc@. If @i@ is visible do nothing.
|
-- | Switch to workspace @i@ on screen @sc@. If @i@ is visible do nothing.
|
||||||
onlyOnScreen :: ScreenId -- ^ screen id
|
onlyOnScreen ::
|
||||||
-> WorkspaceId -- ^ index of the workspace
|
-- | screen id
|
||||||
-> WindowSet -- ^ current stack
|
ScreenId ->
|
||||||
-> WindowSet
|
-- | index of the workspace
|
||||||
|
WorkspaceId ->
|
||||||
|
-- | current stack
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
onlyOnScreen sid i =
|
onlyOnScreen sid i =
|
||||||
onScreen (view i) FocusCurrent sid
|
onScreen (view i) FocusCurrent sid
|
||||||
|
|
||||||
-- | @toggleOrView@ as in "XMonad.Actions.CycleWS" for @onScreen@ with view
|
-- | @toggleOrView@ as in "XMonad.Actions.CycleWS" for @onScreen@ with view
|
||||||
toggleOnScreen :: ScreenId -- ^ screen id
|
toggleOnScreen ::
|
||||||
-> WorkspaceId -- ^ index of the workspace
|
-- | screen id
|
||||||
-> WindowSet -- ^ current stack
|
ScreenId ->
|
||||||
-> WindowSet
|
-- | index of the workspace
|
||||||
|
WorkspaceId ->
|
||||||
|
-- | current stack
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
toggleOnScreen sid i =
|
toggleOnScreen sid i =
|
||||||
onScreen (toggleOrView' view i) FocusCurrent sid
|
onScreen (toggleOrView' view i) FocusCurrent sid
|
||||||
|
|
||||||
-- | @toggleOrView@ from "XMonad.Actions.CycleWS" for @onScreen@ with greedyView
|
-- | @toggleOrView@ from "XMonad.Actions.CycleWS" for @onScreen@ with greedyView
|
||||||
toggleGreedyOnScreen :: ScreenId -- ^ screen id
|
toggleGreedyOnScreen ::
|
||||||
-> WorkspaceId -- ^ index of the workspace
|
-- | screen id
|
||||||
-> WindowSet -- ^ current stack
|
ScreenId ->
|
||||||
-> WindowSet
|
-- | index of the workspace
|
||||||
|
WorkspaceId ->
|
||||||
|
-- | current stack
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
toggleGreedyOnScreen sid i =
|
toggleGreedyOnScreen sid i =
|
||||||
onScreen (toggleOrView' greedyView i) FocusCurrent sid
|
onScreen (toggleOrView' greedyView i) FocusCurrent sid
|
||||||
|
|
||||||
|
|
||||||
-- a \"pure\" version of X.A.CycleWS.toggleOrDoSkip
|
-- a \"pure\" version of X.A.CycleWS.toggleOrDoSkip
|
||||||
toggleOrView' :: (WorkspaceId -> WindowSet -> WindowSet) -- ^ function to run
|
toggleOrView' ::
|
||||||
-> WorkspaceId -- ^ tag to look for
|
-- | function to run
|
||||||
-> WindowSet -- ^ current stackset
|
(WorkspaceId -> WindowSet -> WindowSet) ->
|
||||||
-> WindowSet
|
-- | tag to look for
|
||||||
|
WorkspaceId ->
|
||||||
|
-- | current stackset
|
||||||
|
WindowSet ->
|
||||||
|
WindowSet
|
||||||
toggleOrView' f i st = fromMaybe (f i st) $ do
|
toggleOrView' f i st = fromMaybe (f i st) $ do
|
||||||
let st' = hidden st
|
let st' = hidden st
|
||||||
-- make sure we actually have to do something
|
-- make sure we actually have to do something
|
||||||
guard $ i == (tag . workspace $ current st)
|
guard $ i == (tag . workspace $ current st)
|
||||||
guard $ not (null st')
|
case st' of
|
||||||
-- finally, toggle!
|
[] -> empty
|
||||||
return $ f (tag . head $ st') st
|
(h : _) -> return $ f (tag h) st -- finally, toggle!
|
||||||
|
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- This module provides an easy way to control, what you see on other screens in
|
-- This module provides an easy way to control, what you see on other screens in
|
||||||
-- xinerama mode without having to focus them. Put this into your
|
-- xinerama mode without having to focus them. Put this into your
|
||||||
-- @~\/.xmonad\/xmonad.hs@:
|
-- @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.OnScreen
|
-- > import XMonad.Actions.OnScreen
|
||||||
--
|
--
|
||||||
@ -183,4 +215,4 @@ toggleOrView' f i st = fromMaybe (f i st) $ do
|
|||||||
-- where 0 is the first screen and \"1\" the workspace with the tag \"1\".
|
-- where 0 is the first screen and \"1\" the workspace with the tag \"1\".
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
49
XMonad/Actions/PerLayoutKeys.hs
Normal file
49
XMonad/Actions/PerLayoutKeys.hs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Actions.PerLayoutKeys
|
||||||
|
-- Description : Define key-bindings on per-layout basis.
|
||||||
|
-- Copyright : (c) brandon s allbery kf8nh 2022, Roman Cheplyaka, 2008
|
||||||
|
-- License : BSD3-style (see LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : brandon s allbery kf8ng <allbery.b@gmail.com>
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- Define key-bindings on per-layout basis.
|
||||||
|
--
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Actions.PerLayoutKeys (
|
||||||
|
-- * Usage
|
||||||
|
-- $usage
|
||||||
|
chooseActionByLayout,
|
||||||
|
bindByLayout
|
||||||
|
) where
|
||||||
|
|
||||||
|
import XMonad
|
||||||
|
import XMonad.StackSet as S
|
||||||
|
|
||||||
|
-- $usage
|
||||||
|
--
|
||||||
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
|
--
|
||||||
|
-- > import XMonad.Actions.PerLayoutKeys
|
||||||
|
--
|
||||||
|
-- > ,((0, xK_F2), bindByLayout [("Tall", spawn "rxvt"), ("Mirror Tall", spawn "xeyes"), ("", spawn "xmessage hello")])
|
||||||
|
--
|
||||||
|
-- For detailed instructions on editing your key bindings, see
|
||||||
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
|
-- | Uses supplied function to decide which action to run depending on current layout name.
|
||||||
|
chooseActionByLayout :: (String->X()) -> X()
|
||||||
|
chooseActionByLayout f = withWindowSet (f . description . S.layout. S.workspace . S.current)
|
||||||
|
|
||||||
|
-- | If current layout is listed, run appropriate action (only the first match counts!)
|
||||||
|
-- If it isn't listed, then run default action (marked with empty string, \"\"), or do nothing if default isn't supplied.
|
||||||
|
bindByLayout :: [(String, X())] -> X()
|
||||||
|
bindByLayout bindings = chooseActionByLayout chooser where
|
||||||
|
chooser l = case lookup l bindings of
|
||||||
|
Just action -> action
|
||||||
|
Nothing -> case lookup "" bindings of
|
||||||
|
Just action -> action
|
||||||
|
Nothing -> return ()
|
@ -24,7 +24,7 @@ import XMonad
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.PerWindowKeys
|
-- > import XMonad.Actions.PerWindowKeys
|
||||||
--
|
--
|
||||||
@ -41,7 +41,7 @@ import XMonad
|
|||||||
-- doThisIfTheOthersFail)]@.
|
-- doThisIfTheOthersFail)]@.
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | Run an action if a Query holds true. Doesn't stop at the first one that
|
-- | Run an action if a Query holds true. Doesn't stop at the first one that
|
||||||
-- does, however, and could potentially run all actions.
|
-- does, however, and could potentially run all actions.
|
||||||
|
@ -25,14 +25,14 @@ import XMonad.StackSet as S
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.PerWorkspaceKeys
|
-- > import XMonad.Actions.PerWorkspaceKeys
|
||||||
--
|
--
|
||||||
-- > ,((0, xK_F2), bindOn [("1", spawn "rxvt"), ("2", spawn "xeyes"), ("", spawn "xmessage hello")])
|
-- > ,((0, xK_F2), bindOn [("1", spawn "rxvt"), ("2", spawn "xeyes"), ("", spawn "xmessage hello")])
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | Uses supplied function to decide which action to run depending on current workspace name.
|
-- | Uses supplied function to decide which action to run depending on current workspace name.
|
||||||
chooseAction :: (String->X()) -> X()
|
chooseAction :: (String->X()) -> X()
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
{-# LANGUAGE ParallelListComp #-}
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.PhysicalScreens
|
-- Module : XMonad.Actions.PhysicalScreens
|
||||||
@ -28,10 +30,13 @@ module XMonad.Actions.PhysicalScreens (
|
|||||||
, getScreenIdAndRectangle
|
, getScreenIdAndRectangle
|
||||||
, screenComparatorById
|
, screenComparatorById
|
||||||
, screenComparatorByRectangle
|
, screenComparatorByRectangle
|
||||||
|
, rescreen
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import XMonad
|
import Data.List.NonEmpty (nonEmpty)
|
||||||
import XMonad.Prelude (elemIndex, fromMaybe, on, sortBy)
|
import XMonad hiding (rescreen)
|
||||||
|
import XMonad.Prelude (elemIndex, fromMaybe, on, sortBy, NonEmpty((:|)))
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
|
|
||||||
{- $usage
|
{- $usage
|
||||||
@ -46,7 +51,7 @@ To create a screen comparator you can use screenComparatorByRectangle or screenC
|
|||||||
The default ScreenComparator orders screens by the upper-left-most corner, from top-to-bottom
|
The default ScreenComparator orders screens by the upper-left-most corner, from top-to-bottom
|
||||||
and then left-to-right.
|
and then left-to-right.
|
||||||
|
|
||||||
Example usage in your @~\/.xmonad\/xmonad.hs@ file:
|
Example usage in your @xmonad.hs@ file:
|
||||||
|
|
||||||
> import XMonad.Actions.PhysicalScreens
|
> import XMonad.Actions.PhysicalScreens
|
||||||
> import Data.Default
|
> import Data.Default
|
||||||
@ -65,7 +70,7 @@ Example usage in your @~\/.xmonad\/xmonad.hs@ file:
|
|||||||
> , (f, mask) <- [(viewScreen def, 0), (sendToScreen def, shiftMask)]]
|
> , (f, mask) <- [(viewScreen def, 0), (sendToScreen def, shiftMask)]]
|
||||||
|
|
||||||
For detailed instructions on editing your key bindings, see
|
For detailed instructions on editing your key bindings, see
|
||||||
"XMonad.Doc.Extending#Editing_key_bindings".
|
<https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
-- | The type of the index of a screen by location
|
-- | The type of the index of a screen by location
|
||||||
@ -75,7 +80,7 @@ getScreenIdAndRectangle :: W.Screen i l a ScreenId ScreenDetail -> (ScreenId, Re
|
|||||||
getScreenIdAndRectangle screen = (W.screen screen, rect) where
|
getScreenIdAndRectangle screen = (W.screen screen, rect) where
|
||||||
rect = screenRect $ W.screenDetail screen
|
rect = screenRect $ W.screenDetail screen
|
||||||
|
|
||||||
-- | Translate a physical screen index to a "ScreenId"
|
-- | Translate a physical screen index to a 'ScreenId'
|
||||||
getScreen:: ScreenComparator -> PhysicalScreen -> X (Maybe ScreenId)
|
getScreen:: ScreenComparator -> PhysicalScreen -> X (Maybe ScreenId)
|
||||||
getScreen (ScreenComparator cmpScreen) (P i) = do w <- gets windowset
|
getScreen (ScreenComparator cmpScreen) (P i) = do w <- gets windowset
|
||||||
let screens = W.current w : W.visible w
|
let screens = W.current w : W.visible w
|
||||||
@ -146,3 +151,53 @@ onNextNeighbour sc = neighbourWindows sc 1
|
|||||||
-- | Apply operation on a WindowSet with the WorkspaceId of the previous screen in the physical order as parameter.
|
-- | Apply operation on a WindowSet with the WorkspaceId of the previous screen in the physical order as parameter.
|
||||||
onPrevNeighbour :: ScreenComparator -> (WorkspaceId -> WindowSet -> WindowSet) -> X ()
|
onPrevNeighbour :: ScreenComparator -> (WorkspaceId -> WindowSet -> WindowSet) -> X ()
|
||||||
onPrevNeighbour sc = neighbourWindows sc (-1)
|
onPrevNeighbour sc = neighbourWindows sc (-1)
|
||||||
|
|
||||||
|
-- | An alternative to 'XMonad.Operations.rescreen' that avoids reshuffling
|
||||||
|
-- the workspaces if the number of screens doesn't change and only their
|
||||||
|
-- locations do. Useful for users of @xrandr --setmonitor@.
|
||||||
|
--
|
||||||
|
-- See 'XMonad.Hooks.Rescreen.setRescreenWorkspacesHook', which lets you
|
||||||
|
-- replace the builtin rescreen handler.
|
||||||
|
rescreen :: ScreenComparator -> X ()
|
||||||
|
rescreen (ScreenComparator cmpScreen) = withDisplay (fmap nonEmpty . getCleanedScreenInfo) >>= \case
|
||||||
|
Nothing -> trace "getCleanedScreenInfo returned []"
|
||||||
|
Just xinescs -> windows $ rescreen' xinescs
|
||||||
|
where
|
||||||
|
rescreen' :: NonEmpty Rectangle -> WindowSet -> WindowSet
|
||||||
|
rescreen' xinescs ws
|
||||||
|
| NE.length xinescs == length (W.visible ws) + 1 = rescreenSameLength xinescs ws
|
||||||
|
| otherwise = rescreenCore xinescs ws
|
||||||
|
|
||||||
|
-- the 'XMonad.Operations.rescreen' implementation from core as a fallback
|
||||||
|
rescreenCore :: NonEmpty Rectangle -> WindowSet -> WindowSet
|
||||||
|
rescreenCore (xinesc :| xinescs) ws@W.StackSet{ W.current = v, W.visible = vs, W.hidden = hs } =
|
||||||
|
let (xs, ys) = splitAt (length xinescs) (map W.workspace vs ++ hs)
|
||||||
|
a = W.Screen (W.workspace v) 0 (SD xinesc)
|
||||||
|
as = zipWith3 W.Screen xs [1..] $ map SD xinescs
|
||||||
|
in ws{ W.current = a
|
||||||
|
, W.visible = as
|
||||||
|
, W.hidden = ys }
|
||||||
|
|
||||||
|
-- sort both existing screens and the screens we just got from xinerama
|
||||||
|
-- using cmpScreen, and then replace the rectangles in the WindowSet,
|
||||||
|
-- keeping the order of current/visible workspaces intact
|
||||||
|
rescreenSameLength :: NonEmpty Rectangle -> WindowSet -> WindowSet
|
||||||
|
rescreenSameLength xinescs ws =
|
||||||
|
ws{ W.current = (W.current ws){ W.screenDetail = SD newCurrentRect }
|
||||||
|
, W.visible = [ w{ W.screenDetail = SD r } | w <- W.visible ws | r <- newVisibleRects ]
|
||||||
|
}
|
||||||
|
where
|
||||||
|
undoSort =
|
||||||
|
NE.map fst $
|
||||||
|
NE.sortBy (cmpScreen `on` (getScreenIdAndRectangle . snd)) $
|
||||||
|
NE.zip ((0 :: Int) :| [1..]) $ -- add indices to undo the sort later
|
||||||
|
W.current ws :| W.visible ws
|
||||||
|
newCurrentRect :| newVisibleRects =
|
||||||
|
NE.map snd $ NE.sortWith fst $ NE.zip undoSort $ -- sort back into current:visible order
|
||||||
|
NE.map snd $ NE.sortBy cmpScreen $ NE.zip (0 :| [1..]) xinescs
|
||||||
|
|
||||||
|
-- TODO:
|
||||||
|
-- If number of screens before and after isn't the same, we might still
|
||||||
|
-- try to match locations and avoid changing the workspace for those that
|
||||||
|
-- didn't move, while making sure that the current workspace is still
|
||||||
|
-- visible somewhere.
|
||||||
|
@ -41,13 +41,13 @@ module XMonad.Actions.Plane
|
|||||||
|
|
||||||
import Data.Map (Map, fromList)
|
import Data.Map (Map, fromList)
|
||||||
|
|
||||||
import XMonad.Prelude
|
import XMonad.Prelude hiding (fromList)
|
||||||
import XMonad
|
import XMonad
|
||||||
import XMonad.StackSet hiding (workspaces)
|
import XMonad.StackSet hiding (workspaces)
|
||||||
import XMonad.Util.Run
|
import XMonad.Util.Run
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
|
-- You can use this module with the following in your @xmonad.hs@ file:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.Plane
|
-- > import XMonad.Actions.Plane
|
||||||
-- > import Data.Map (union)
|
-- > import Data.Map (union)
|
||||||
@ -59,7 +59,7 @@ import XMonad.Util.Run
|
|||||||
-- > myNewKeys (XConfig {modMask = modm}) = planeKeys modm (Lines 3) Finite
|
-- > myNewKeys (XConfig {modMask = modm}) = planeKeys modm (Lines 3) Finite
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | Direction to go in the plane.
|
-- | Direction to go in the plane.
|
||||||
data Direction = ToLeft | ToUp | ToRight | ToDown deriving Enum
|
data Direction = ToLeft | ToUp | ToRight | ToDown deriving Enum
|
||||||
|
@ -29,6 +29,7 @@ module XMonad.Actions.Prefix
|
|||||||
, withPrefixArgument
|
, withPrefixArgument
|
||||||
, isPrefixRaw
|
, isPrefixRaw
|
||||||
, isPrefixNumeric
|
, isPrefixNumeric
|
||||||
|
, orIfPrefixed
|
||||||
, ppFormatPrefix
|
, ppFormatPrefix
|
||||||
) where
|
) where
|
||||||
|
|
||||||
@ -40,11 +41,13 @@ import XMonad.Util.ExtensibleState as XS
|
|||||||
import XMonad.Util.Paste (sendKey)
|
import XMonad.Util.Paste (sendKey)
|
||||||
import XMonad.Actions.Submap (submapDefaultWithKey)
|
import XMonad.Actions.Submap (submapDefaultWithKey)
|
||||||
import XMonad.Util.EZConfig (readKeySequence)
|
import XMonad.Util.EZConfig (readKeySequence)
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
|
import Data.List.NonEmpty ((<|))
|
||||||
|
|
||||||
{- $usage
|
{- $usage
|
||||||
|
|
||||||
This module implements Emacs-style prefix argument. The argument
|
This module implements Emacs-style prefix argument. The argument
|
||||||
comes in two flavours, "Raw" and "Numeric".
|
comes in two flavours, 'Raw' and 'Numeric'.
|
||||||
|
|
||||||
To initiate the "prefix mode" you hit the prefix keybinding (default
|
To initiate the "prefix mode" you hit the prefix keybinding (default
|
||||||
C-u). This sets the Raw argument value to 1. Repeatedly hitting this
|
C-u). This sets the Raw argument value to 1. Repeatedly hitting this
|
||||||
@ -72,7 +75,7 @@ Binding it in your config
|
|||||||
|
|
||||||
> ((modm, xK_a), withPrefixArgument addMaybeClean)
|
> ((modm, xK_a), withPrefixArgument addMaybeClean)
|
||||||
|
|
||||||
Hitting MOD-a will add the <file> to the playlist while C-u MOD-a will
|
Hitting MOD-a will add the @\<file\>@ to the playlist while C-u MOD-a will
|
||||||
clear the playlist and then add the file.
|
clear the playlist and then add the file.
|
||||||
|
|
||||||
You can of course use an anonymous action, like so:
|
You can of course use an anonymous action, like so:
|
||||||
@ -128,11 +131,11 @@ usePrefixArgument :: LayoutClass l Window
|
|||||||
-> XConfig l
|
-> XConfig l
|
||||||
-> XConfig l
|
-> XConfig l
|
||||||
usePrefixArgument prefix conf =
|
usePrefixArgument prefix conf =
|
||||||
conf{ keys = M.insert binding (handlePrefixArg [binding]) . keys conf }
|
conf{ keys = M.insert binding (handlePrefixArg (binding :| [])) . keys conf }
|
||||||
where
|
where
|
||||||
binding = case readKeySequence conf prefix of
|
binding = case readKeySequence conf prefix of
|
||||||
Just [key] -> key
|
Just (key :| []) -> key
|
||||||
_ -> (controlMask, xK_u)
|
_ -> (controlMask, xK_u)
|
||||||
|
|
||||||
-- | Set Prefix up with default prefix key (C-u).
|
-- | Set Prefix up with default prefix key (C-u).
|
||||||
useDefaultPrefixArgument :: LayoutClass l Window
|
useDefaultPrefixArgument :: LayoutClass l Window
|
||||||
@ -140,7 +143,7 @@ useDefaultPrefixArgument :: LayoutClass l Window
|
|||||||
-> XConfig l
|
-> XConfig l
|
||||||
useDefaultPrefixArgument = usePrefixArgument "C-u"
|
useDefaultPrefixArgument = usePrefixArgument "C-u"
|
||||||
|
|
||||||
handlePrefixArg :: [(KeyMask, KeySym)] -> X ()
|
handlePrefixArg :: NonEmpty (KeyMask, KeySym) -> X ()
|
||||||
handlePrefixArg events = do
|
handlePrefixArg events = do
|
||||||
ks <- asks keyActions
|
ks <- asks keyActions
|
||||||
logger <- asks (logHook . config)
|
logger <- asks (logHook . config)
|
||||||
@ -161,19 +164,19 @@ handlePrefixArg events = do
|
|||||||
Raw _ -> XS.put $ Numeric x
|
Raw _ -> XS.put $ Numeric x
|
||||||
Numeric a -> XS.put $ Numeric $ a * 10 + x
|
Numeric a -> XS.put $ Numeric $ a * 10 + x
|
||||||
None -> return () -- should never happen
|
None -> return () -- should never happen
|
||||||
handlePrefixArg (key:events)
|
handlePrefixArg (key <| events)
|
||||||
else do
|
else do
|
||||||
prefix <- XS.get
|
prefix <- XS.get
|
||||||
mapM_ (uncurry sendKey) $ case prefix of
|
mapM_ (uncurry sendKey) $ case prefix of
|
||||||
Raw a -> replicate a (head events) ++ [key]
|
Raw a -> replicate a (NE.head events) ++ [key]
|
||||||
_ -> reverse (key:events)
|
_ -> reverse (key : toList events)
|
||||||
keyToNum = (xK_0, 0) : zip [xK_1 .. xK_9] [1..9]
|
keyToNum = (xK_0, 0) : zip [xK_1 .. xK_9] [1..9]
|
||||||
|
|
||||||
-- | Turn a prefix-aware X action into an X-action.
|
-- | Turn a prefix-aware X action into an X-action.
|
||||||
--
|
--
|
||||||
-- First, fetch the current prefix, then pass it as argument to the
|
-- First, fetch the current prefix, then pass it as argument to the
|
||||||
-- original function. You should use this to "run" your commands.
|
-- original function. You should use this to "run" your commands.
|
||||||
withPrefixArgument :: (PrefixArgument -> X ()) -> X ()
|
withPrefixArgument :: (PrefixArgument -> X a) -> X a
|
||||||
withPrefixArgument = (>>=) XS.get
|
withPrefixArgument = (>>=) XS.get
|
||||||
|
|
||||||
-- | Test if 'PrefixArgument' is 'Raw' or not.
|
-- | Test if 'PrefixArgument' is 'Raw' or not.
|
||||||
@ -186,6 +189,13 @@ isPrefixNumeric :: PrefixArgument -> Bool
|
|||||||
isPrefixNumeric (Numeric _) = True
|
isPrefixNumeric (Numeric _) = True
|
||||||
isPrefixNumeric _ = False
|
isPrefixNumeric _ = False
|
||||||
|
|
||||||
|
-- | Execute the first action, unless any prefix argument is given,
|
||||||
|
-- in which case the second action is chosen instead.
|
||||||
|
--
|
||||||
|
-- > action1 `orIfPrefixed` action2
|
||||||
|
orIfPrefixed :: X a -> X a -> X a
|
||||||
|
orIfPrefixed xa xb = withPrefixArgument $ bool xa xb . isPrefixRaw
|
||||||
|
|
||||||
-- | Format the prefix using the Emacs convetion for use in a
|
-- | Format the prefix using the Emacs convetion for use in a
|
||||||
-- statusbar, like xmobar.
|
-- statusbar, like xmobar.
|
||||||
--
|
--
|
||||||
|
545
XMonad/Actions/Profiles.hs
Normal file
545
XMonad/Actions/Profiles.hs
Normal file
@ -0,0 +1,545 @@
|
|||||||
|
{-# LANGUAGE TupleSections #-}
|
||||||
|
{-# LANGUAGE DerivingVia #-}
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Actions.Profiles
|
||||||
|
-- Description : Group your workspaces by similarity.
|
||||||
|
-- Copyright : (c) Mislav Zanic
|
||||||
|
-- License : BSD3-style (see LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : Mislav Zanic <mislavzanic3@gmail.com>
|
||||||
|
-- Stability : experimental
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Actions.Profiles
|
||||||
|
( -- * Overview
|
||||||
|
-- $overview
|
||||||
|
|
||||||
|
-- * Usage
|
||||||
|
-- $usage
|
||||||
|
|
||||||
|
-- * Types
|
||||||
|
ProfileId
|
||||||
|
, Profile(..)
|
||||||
|
, ProfileConfig(..)
|
||||||
|
|
||||||
|
-- * Hooks
|
||||||
|
, addProfiles
|
||||||
|
, addProfilesWithHistory
|
||||||
|
|
||||||
|
-- * Switching profiles
|
||||||
|
, switchToProfile
|
||||||
|
|
||||||
|
-- * Workspace navigation and keybindings
|
||||||
|
, wsFilter
|
||||||
|
, bindOn
|
||||||
|
|
||||||
|
-- * Loggers and pretty printers
|
||||||
|
, excludeWSPP
|
||||||
|
, profileLogger
|
||||||
|
|
||||||
|
-- * Prompts
|
||||||
|
, switchProfilePrompt
|
||||||
|
, addWSToProfilePrompt
|
||||||
|
, removeWSFromProfilePrompt
|
||||||
|
, switchProfileWSPrompt
|
||||||
|
, shiftProfileWSPrompt
|
||||||
|
|
||||||
|
-- * Utilities
|
||||||
|
, currentProfile
|
||||||
|
, profileIds
|
||||||
|
, previousProfile
|
||||||
|
, profileHistory
|
||||||
|
, allProfileWindows
|
||||||
|
, profileWorkspaces
|
||||||
|
)where
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
import Data.Map.Strict (Map)
|
||||||
|
import Data.List
|
||||||
|
import qualified Data.Map.Strict as Map
|
||||||
|
|
||||||
|
import Control.DeepSeq
|
||||||
|
|
||||||
|
import XMonad
|
||||||
|
import XMonad.Prelude
|
||||||
|
import qualified XMonad.StackSet as W
|
||||||
|
|
||||||
|
import XMonad.Actions.CycleWS
|
||||||
|
|
||||||
|
import qualified XMonad.Util.ExtensibleState as XS
|
||||||
|
import XMonad.Util.Loggers (Logger)
|
||||||
|
import XMonad.Prompt.Window (XWindowMap)
|
||||||
|
import XMonad.Actions.WindowBringer (WindowBringerConfig(..))
|
||||||
|
import XMonad.Actions.OnScreen (greedyViewOnScreen)
|
||||||
|
import XMonad.Hooks.Rescreen (addAfterRescreenHook)
|
||||||
|
import XMonad.Hooks.DynamicLog (PP(ppRename))
|
||||||
|
import XMonad.Prompt
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- $overview
|
||||||
|
-- This module allows you to group your workspaces into 'Profile's based on certain similarities.
|
||||||
|
-- The idea is to expand upon the philosophy set by "XMonad.Actions.TopicSpace"
|
||||||
|
-- which states that you can look at a topic/workspace as a
|
||||||
|
-- single unit of work instead of multiple related units of work.
|
||||||
|
-- This comes in handy if you have lots of workspaces with windows open and need only to
|
||||||
|
-- work with a few of them at a time. With 'Profile's, you can focus on those few workspaces that
|
||||||
|
-- require your attention by not displaying, or allowing you to switch to the rest of the workspaces.
|
||||||
|
-- The best example is having a profile for development and a profile for leisure activities.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- $usage
|
||||||
|
-- To use @Profiles@ you need to add it to your XMonad configuration
|
||||||
|
-- and configure your profiles.
|
||||||
|
--
|
||||||
|
-- First you'll need to handle the imports.
|
||||||
|
--
|
||||||
|
-- > import XMonad.Actions.Profiles
|
||||||
|
-- > import XMonad.Util.EZConfig -- for keybindings
|
||||||
|
-- > import qualified XMonad.StackSet as W
|
||||||
|
-- > import qualified XMonad.Actions.DynamicWorkspaceOrder as DO -- for workspace navigation
|
||||||
|
--
|
||||||
|
-- Next you'll need to define your profiles.
|
||||||
|
--
|
||||||
|
-- > myStartingProfile :: ProfileId
|
||||||
|
-- > myStartingProfile = "Work"
|
||||||
|
-- >
|
||||||
|
-- > myProfiles :: [Profile]
|
||||||
|
-- > myProfiles =
|
||||||
|
-- > [ Profile { profileId = "Home"
|
||||||
|
-- > , profileWS = [ "www"
|
||||||
|
-- > , "rss"
|
||||||
|
-- > , "vid"
|
||||||
|
-- > , "vms"
|
||||||
|
-- > , "writing"
|
||||||
|
-- > , "notes"
|
||||||
|
-- > ]
|
||||||
|
-- > }
|
||||||
|
-- > , Profile { profileId = "Work"
|
||||||
|
-- > , profileWS = [ "www"
|
||||||
|
-- > , "slack"
|
||||||
|
-- > , "dev"
|
||||||
|
-- > , "k8s"
|
||||||
|
-- > , "notes"
|
||||||
|
-- > ]
|
||||||
|
-- > }
|
||||||
|
-- > ]
|
||||||
|
--
|
||||||
|
-- So, while using @Home@ 'Profile', you'll only be able to see, navigate to and
|
||||||
|
-- do actions with @["www", "rss", "vid", "vms", "writing", "notes"]@ workspaces.
|
||||||
|
--
|
||||||
|
-- You may also need to define some keybindings. Since @M-1@ .. @M-9@ are
|
||||||
|
-- sensible keybindings for switching workspaces, you'll need to use
|
||||||
|
-- 'bindOn' to have different keybindings per profile.
|
||||||
|
-- Here, we'll use "XMonad.Util.EZConfig" syntax:
|
||||||
|
--
|
||||||
|
-- > myKeys :: [(String, X())]
|
||||||
|
-- > myKeys =
|
||||||
|
-- > [ ("M-p", switchProfilePrompt xpConfig)
|
||||||
|
-- > , ("M-g", switchProfileWSPrompt xpConfig)
|
||||||
|
-- > , ("M1-j", DO.moveTo Next wsFilter)
|
||||||
|
-- > , ("M1-k", DO.moveTo Prev wsFilter)
|
||||||
|
-- > ]
|
||||||
|
-- > <>
|
||||||
|
-- > [ ("M-" ++ m ++ k, bindOn $ map (\x -> (fst x, f $ snd x)) i)
|
||||||
|
-- > | (i, k) <- map (\(x:xs) -> (map fst (x:xs), snd x)) $ sortGroupBy snd tupleList
|
||||||
|
-- > , (f, m) <- [(mby $ windows . W.greedyView, ""), (mby $ windows . W.shift, "S-")]
|
||||||
|
-- > ]
|
||||||
|
-- > where
|
||||||
|
-- > mby f wid = if wid == "" then return () else f wid
|
||||||
|
-- > sortGroupBy f = groupBy (\ x y -> f x == f y) . sortBy (\x y -> compare (f x) (f y))
|
||||||
|
-- > tupleList = concatMap (\p -> zip (map (\wid -> (profileId p, wid)) (profileWS p <> repeat "")) (map show [1..9 :: Int])) myProfiles
|
||||||
|
--
|
||||||
|
-- After that, you'll need to hook @Profiles@ into your XMonad config:
|
||||||
|
--
|
||||||
|
-- > main = xmonad $ addProfiles def { profiles = myProfiles
|
||||||
|
-- > , startingProfile = myStartingProfile
|
||||||
|
-- > }
|
||||||
|
-- > $ def `additionalKeysP` myKeys
|
||||||
|
--
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
type ProfileId = String
|
||||||
|
type ProfileMap = Map ProfileId Profile
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Profile representation.
|
||||||
|
data Profile = Profile
|
||||||
|
{ profileId :: !ProfileId -- ^ Profile name.
|
||||||
|
, profileWS :: ![WorkspaceId] -- ^ A list of workspaces contained within a profile.
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Internal profile state.
|
||||||
|
data ProfileState = ProfileState
|
||||||
|
{ profilesMap :: !ProfileMap
|
||||||
|
, current :: !(Maybe Profile)
|
||||||
|
, previous :: !(Maybe ProfileId)
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | User config for profiles.
|
||||||
|
data ProfileConfig = ProfileConfig
|
||||||
|
{ workspaceExcludes :: ![WorkspaceId] -- ^ A list of workspaces to exclude from the @profileHistoryHook@.
|
||||||
|
, profiles :: ![Profile] -- ^ A list of user-defined profiles.
|
||||||
|
, startingProfile :: !ProfileId -- ^ Profile shown on startup.
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
instance Default ProfileConfig where
|
||||||
|
def = ProfileConfig { workspaceExcludes = []
|
||||||
|
, profiles = []
|
||||||
|
, startingProfile = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
instance ExtensionClass ProfileState where
|
||||||
|
initialValue = ProfileState Map.empty Nothing Nothing
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Internal type for history tracking.
|
||||||
|
-- Main problem with @XMonad.Hooks.HistoryHook@ is that it isn't profile aware.
|
||||||
|
-- Because of that, when switching to a previous workspace, you might switch to
|
||||||
|
-- a workspace
|
||||||
|
newtype ProfileHistory = ProfileHistory
|
||||||
|
{ history :: Map ProfileId [(ScreenId, WorkspaceId)]
|
||||||
|
}
|
||||||
|
deriving (Read, Show)
|
||||||
|
deriving NFData via Map ProfileId [(Int, WorkspaceId)]
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
instance ExtensionClass ProfileHistory where
|
||||||
|
extensionType = PersistentExtension
|
||||||
|
initialValue = ProfileHistory Map.empty
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
newtype ProfilePrompt = ProfilePrompt String
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
instance XPrompt ProfilePrompt where
|
||||||
|
showXPrompt (ProfilePrompt x) = x
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
defaultProfile :: Profile
|
||||||
|
defaultProfile = defaultProfile
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Returns current profile.
|
||||||
|
currentProfile :: X ProfileId
|
||||||
|
currentProfile = profileId . fromMaybe defaultProfile . current <$> XS.get
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Returns previous profile.
|
||||||
|
previousProfile :: X (Maybe ProfileId)
|
||||||
|
previousProfile = XS.gets previous
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Returns the history of viewed workspaces per profile.
|
||||||
|
profileHistory :: X (Map ProfileId [(ScreenId, WorkspaceId)])
|
||||||
|
profileHistory = XS.gets history
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
profileMap :: X ProfileMap
|
||||||
|
profileMap = XS.gets profilesMap
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Returns ids of all profiles.
|
||||||
|
profileIds :: X [ProfileId]
|
||||||
|
profileIds = Map.keys <$> XS.gets profilesMap
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
currentProfileWorkspaces :: X [WorkspaceId]
|
||||||
|
currentProfileWorkspaces = XS.gets current <&> profileWS . fromMaybe defaultProfile
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Hook profiles into XMonad. This function adds a startup hook that
|
||||||
|
-- sets up ProfileState. Also adds an afterRescreenHook for viewing correct
|
||||||
|
-- workspaces when adding new screens.
|
||||||
|
addProfiles :: ProfileConfig -> XConfig a -> XConfig a
|
||||||
|
addProfiles profConf conf = addAfterRescreenHook hook $ conf
|
||||||
|
{ startupHook = profileStartupHook' <> startupHook conf
|
||||||
|
}
|
||||||
|
where
|
||||||
|
profileStartupHook' :: X()
|
||||||
|
profileStartupHook' = profilesStartupHook (profiles profConf) (startingProfile profConf)
|
||||||
|
hook = currentProfile >>= switchWSOnScreens
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Hooks profiles into XMonad and enables Profile history logging.
|
||||||
|
addProfilesWithHistory :: ProfileConfig -> XConfig a -> XConfig a
|
||||||
|
addProfilesWithHistory profConf conf = conf'
|
||||||
|
{ logHook = profileHistoryHookExclude (workspaceExcludes profConf) <> logHook conf
|
||||||
|
}
|
||||||
|
where
|
||||||
|
conf' = addProfiles profConf conf
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
profileHistoryHookExclude :: [WorkspaceId] -> X()
|
||||||
|
profileHistoryHookExclude ews = do
|
||||||
|
cur <- gets $ W.current . windowset
|
||||||
|
vis <- gets $ W.visible . windowset
|
||||||
|
pws <- currentProfileWorkspaces
|
||||||
|
p <- currentProfile
|
||||||
|
|
||||||
|
updateHist p $ workspaceScreenPairs $ filterWS pws $ cur:vis
|
||||||
|
where
|
||||||
|
workspaceScreenPairs wins = zip (W.screen <$> wins) (W.tag . W.workspace <$> wins)
|
||||||
|
filterWS pws = filter ((\wid -> (wid `elem` pws) && (wid `notElem` ews)) . W.tag . W.workspace)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
updateHist :: ProfileId -> [(ScreenId, WorkspaceId)] -> X()
|
||||||
|
updateHist pid xs = profileWorkspaces pid >>= XS.modify' . update
|
||||||
|
where
|
||||||
|
update pws hs = force $ hs { history = doUpdate pws $ history hs }
|
||||||
|
|
||||||
|
doUpdate pws hist = foldl (\acc (sid, wid) -> Map.alter (f pws sid wid) pid acc) hist xs
|
||||||
|
|
||||||
|
f pws sid wid val = case val of
|
||||||
|
Nothing -> pure [(sid, wid)]
|
||||||
|
Just hs -> pure $ let new = (sid, wid) in new:filterWS pws new hs
|
||||||
|
|
||||||
|
filterWS :: [WorkspaceId] -> (ScreenId, WorkspaceId) -> [(ScreenId, WorkspaceId)] -> [(ScreenId, WorkspaceId)]
|
||||||
|
filterWS pws new = filter (\x -> snd x `elem` pws && x /= new)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Adds profiles to ProfileState and sets current profile using .
|
||||||
|
|
||||||
|
profilesStartupHook :: [Profile] -> ProfileId -> X ()
|
||||||
|
profilesStartupHook ps pid = XS.modify go >> switchWSOnScreens pid
|
||||||
|
where
|
||||||
|
go :: ProfileState -> ProfileState
|
||||||
|
go s = s {profilesMap = update $ profilesMap s, current = setCurrentProfile $ Map.fromList $ map entry ps}
|
||||||
|
|
||||||
|
update :: ProfileMap -> ProfileMap
|
||||||
|
update = Map.union (Map.fromList $ map entry ps)
|
||||||
|
|
||||||
|
entry :: Profile -> (ProfileId, Profile)
|
||||||
|
entry p = (profileId p, p)
|
||||||
|
|
||||||
|
setCurrentProfile :: ProfileMap -> Maybe Profile
|
||||||
|
setCurrentProfile s = case Map.lookup pid s of
|
||||||
|
Nothing -> Just $ Profile pid []
|
||||||
|
Just pn -> Just pn
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
setPrevious :: ProfileId -> X()
|
||||||
|
setPrevious name = XS.modify update
|
||||||
|
where
|
||||||
|
update ps = ps { previous = doUpdate ps }
|
||||||
|
doUpdate ps = case Map.lookup name $ profilesMap ps of
|
||||||
|
Nothing -> previous ps
|
||||||
|
Just p -> Just $ profileId p
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
setProfile :: ProfileId -> X ()
|
||||||
|
setProfile p = currentProfile >>= setPrevious >> setProfile' p
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
setProfile' :: ProfileId -> X ()
|
||||||
|
setProfile' name = XS.modify update
|
||||||
|
where
|
||||||
|
update ps = ps { current = doUpdate ps }
|
||||||
|
doUpdate ps = case Map.lookup name $ profilesMap ps of
|
||||||
|
Nothing -> current ps
|
||||||
|
Just p -> Just p
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Switch to a profile.
|
||||||
|
switchToProfile :: ProfileId -> X()
|
||||||
|
switchToProfile pid = setProfile pid >> switchWSOnScreens pid
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Returns the workspace ids associated with a profile id.
|
||||||
|
profileWorkspaces :: ProfileId -> X [WorkspaceId]
|
||||||
|
profileWorkspaces pid = profileMap >>= findPWs
|
||||||
|
where
|
||||||
|
findPWs pm = return . profileWS . fromMaybe defaultProfile $ Map.lookup pid pm
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Prompt for adding a workspace id to a profile.
|
||||||
|
addWSToProfilePrompt :: XPConfig -> X()
|
||||||
|
addWSToProfilePrompt c = do
|
||||||
|
ps <- profileIds
|
||||||
|
mkXPrompt (ProfilePrompt "Add ws to profile:") c (mkComplFunFromList' c ps) f
|
||||||
|
where
|
||||||
|
f :: String -> X()
|
||||||
|
f p = do
|
||||||
|
vis <- gets $ fmap (W.tag . W.workspace) . W.visible . windowset
|
||||||
|
cur <- gets $ W.tag . W.workspace . W.current . windowset
|
||||||
|
hid <- gets $ fmap W.tag . W.hidden . windowset
|
||||||
|
let
|
||||||
|
arr = cur:(vis <> hid)
|
||||||
|
in mkXPrompt (ProfilePrompt "Ws to add to profile:") c (mkComplFunFromList' c arr) (`addWSToProfile` p)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Prompt for switching profiles.
|
||||||
|
switchProfilePrompt :: XPConfig -> X()
|
||||||
|
switchProfilePrompt c = do
|
||||||
|
ps <- profileIds
|
||||||
|
mkXPrompt (ProfilePrompt "Profile: ") c (mkComplFunFromList' c ps) switchToProfile
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Prompt for switching workspaces.
|
||||||
|
switchProfileWSPrompt :: XPConfig -> X ()
|
||||||
|
switchProfileWSPrompt c = mkPrompt =<< currentProfileWorkspaces
|
||||||
|
where
|
||||||
|
mkPrompt pws = mkXPrompt (ProfilePrompt "Switch to workspace:") c (mkComplFunFromList' c pws) mbygoto
|
||||||
|
mbygoto wid = do
|
||||||
|
pw <- profileWorkspaces =<< currentProfile
|
||||||
|
unless (wid `notElem` pw) (windows . W.greedyView $ wid)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Prompt for shifting windows to a different workspace.
|
||||||
|
shiftProfileWSPrompt :: XPConfig -> X ()
|
||||||
|
shiftProfileWSPrompt c = mkPrompt =<< currentProfileWorkspaces
|
||||||
|
where
|
||||||
|
mkPrompt pws = mkXPrompt (ProfilePrompt "Send window to workspace:") c (mkComplFunFromList' c pws) mbyshift
|
||||||
|
mbyshift wid = do
|
||||||
|
pw <- profileWorkspaces =<< currentProfile
|
||||||
|
unless (wid `notElem` pw) (windows . W.shift $ wid)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
addWSToProfile :: WorkspaceId -> ProfileId -> X()
|
||||||
|
addWSToProfile wid pid = XS.modify go
|
||||||
|
where
|
||||||
|
go :: ProfileState -> ProfileState
|
||||||
|
go ps = ps {profilesMap = update $ profilesMap ps, current = update' $ fromMaybe defaultProfile $ current ps}
|
||||||
|
|
||||||
|
update :: ProfileMap -> ProfileMap
|
||||||
|
update mp = case Map.lookup pid mp of
|
||||||
|
Nothing -> mp
|
||||||
|
Just p -> if wid `elem` profileWS p then mp else Map.adjust f pid mp
|
||||||
|
|
||||||
|
f :: Profile -> Profile
|
||||||
|
f p = Profile pid (wid : profileWS p)
|
||||||
|
|
||||||
|
update' :: Profile -> Maybe Profile
|
||||||
|
update' cp = if profileId cp == pid && wid `notElem` profileWS cp then Just (Profile pid $ wid:profileWS cp) else Just cp
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Prompt for removing a workspace from a profile.
|
||||||
|
removeWSFromProfilePrompt :: XPConfig -> X()
|
||||||
|
removeWSFromProfilePrompt c = do
|
||||||
|
ps <- profileIds
|
||||||
|
mkXPrompt (ProfilePrompt "Remove ws from profile:") c (mkComplFunFromList' c ps) f
|
||||||
|
where
|
||||||
|
f :: String -> X()
|
||||||
|
f p = do
|
||||||
|
arr <- profileWorkspaces p
|
||||||
|
mkXPrompt (ProfilePrompt "Ws to remove from profile:") c (mkComplFunFromList' c arr) $
|
||||||
|
\ws -> do
|
||||||
|
cp <- currentProfile
|
||||||
|
ws `removeWSFromProfile` p
|
||||||
|
when (cp == p) $ currentProfile >>= switchWSOnScreens
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
removeWSFromProfile :: WorkspaceId -> ProfileId -> X()
|
||||||
|
removeWSFromProfile wid pid = XS.modify go
|
||||||
|
where
|
||||||
|
go :: ProfileState -> ProfileState
|
||||||
|
go ps = ps {profilesMap = update $ profilesMap ps, current = update' $ fromMaybe defaultProfile $ current ps}
|
||||||
|
|
||||||
|
update :: ProfileMap -> ProfileMap
|
||||||
|
update mp = case Map.lookup pid mp of
|
||||||
|
Nothing -> mp
|
||||||
|
Just p -> if wid `elem` profileWS p then Map.adjust f pid mp else mp
|
||||||
|
|
||||||
|
f :: Profile -> Profile
|
||||||
|
f p = Profile pid (delete wid $ profileWS p)
|
||||||
|
|
||||||
|
update' :: Profile -> Maybe Profile
|
||||||
|
update' cp = if profileId cp == pid && wid `elem` profileWS cp then Just (Profile pid $ delete wid $ profileWS cp) else Just cp
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Pretty printer for a bar. Prints workspace ids of current profile.
|
||||||
|
excludeWSPP :: PP -> X PP
|
||||||
|
excludeWSPP pp = modifyPP <$> currentProfileWorkspaces
|
||||||
|
where
|
||||||
|
modifyPP pws = pp { ppRename = ppRename pp . printTag pws }
|
||||||
|
printTag pws tag = if tag `elem` pws then tag else ""
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | For cycling through workspaces associated with the current.
|
||||||
|
wsFilter :: WSType
|
||||||
|
wsFilter = WSIs $ currentProfileWorkspaces >>= (\ws -> return $ (`elem` ws) . W.tag)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Takes care of placing correct workspaces on their respective screens.
|
||||||
|
-- It does this by reducing the history of a Profile until it gets an array of length
|
||||||
|
-- equal to the number of screens with pairs that have unique workspace ids.
|
||||||
|
switchWSOnScreens :: ProfileId -> X()
|
||||||
|
switchWSOnScreens pid = do
|
||||||
|
hist <- profileHistory
|
||||||
|
vis <- gets $ W.visible . windowset
|
||||||
|
cur <- gets $ W.current . windowset
|
||||||
|
pws <- profileMap <&> (profileWS . fromMaybe (Profile pid []) . Map.lookup pid)
|
||||||
|
case Map.lookup pid hist of
|
||||||
|
Nothing -> switchScreens $ zip (W.screen <$> (cur:vis)) pws
|
||||||
|
Just xs -> compareAndSwitch (f (W.screen <$> cur:vis) xs) (cur:vis) pws
|
||||||
|
where
|
||||||
|
f :: [ScreenId] -> [(ScreenId, WorkspaceId)] -> [(ScreenId, WorkspaceId)]
|
||||||
|
f sids = reorderUniq . reorderUniq . reverse . filter ((`elem` sids) . fst)
|
||||||
|
|
||||||
|
reorderUniq :: (Ord k, Ord v) => [(k,v)] -> [(v,k)]
|
||||||
|
reorderUniq = map (\(x,y) -> (y,x)) . uniq
|
||||||
|
|
||||||
|
uniq :: (Ord k, Ord v) => [(k,v)] -> [(k,v)]
|
||||||
|
uniq = Map.toList . Map.fromList
|
||||||
|
|
||||||
|
viewWS fview sid wid = windows $ fview sid wid
|
||||||
|
|
||||||
|
switchScreens = mapM_ (uncurry $ viewWS greedyViewOnScreen)
|
||||||
|
|
||||||
|
compareAndSwitch hist wins pws | length hist < length wins = switchScreens $ hist <> populateScreens hist wins pws
|
||||||
|
| otherwise = switchScreens hist
|
||||||
|
|
||||||
|
populateScreens hist wins pws = zip (filter (`notElem` map fst hist) $ W.screen <$> wins) (filter (`notElem` map snd hist) pws)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
chooseAction :: (String -> X ()) -> X ()
|
||||||
|
chooseAction f = XS.gets current <&> (profileId . fromMaybe defaultProfile) >>= f
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Create keybindings per profile.
|
||||||
|
bindOn :: [(String, X ())] -> X ()
|
||||||
|
bindOn bindings = chooseAction chooser
|
||||||
|
where
|
||||||
|
chooser profile = case lookup profile bindings of
|
||||||
|
Just action -> action
|
||||||
|
Nothing -> case lookup "" bindings of
|
||||||
|
Just action -> action
|
||||||
|
Nothing -> return ()
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Loggs currentProfile and all profiles with hidden workspaces
|
||||||
|
-- (workspaces that aren't shown on a screen but have windows).
|
||||||
|
profileLogger :: (String -> String) -> (String -> String) -> Logger
|
||||||
|
profileLogger formatFocused formatUnfocused = do
|
||||||
|
hws <- gets $ W.hidden . windowset
|
||||||
|
p <- currentProfile
|
||||||
|
hm <- map fst
|
||||||
|
. filter (\(p', xs) -> any ((`elem` htags hws) . snd) xs || p' == p)
|
||||||
|
. Map.toList <$> profileHistory
|
||||||
|
return $ Just $ foldl (\a b -> a ++ " " ++ b) "" $ format p <$> hm
|
||||||
|
where
|
||||||
|
format p a = if a == p then formatFocused a else formatUnfocused a
|
||||||
|
htags wins = W.tag <$> filter (isJust . W.stack) wins
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | @XWindowMap@ of all windows contained in a profile.
|
||||||
|
allProfileWindows :: XWindowMap
|
||||||
|
allProfileWindows = allProfileWindows' def
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
allProfileWindows' :: WindowBringerConfig -> XWindowMap
|
||||||
|
allProfileWindows' WindowBringerConfig{ windowTitler = titler, windowFilter = include } = do
|
||||||
|
pws <- currentProfileWorkspaces
|
||||||
|
windowSet <- gets windowset
|
||||||
|
Map.fromList . concat <$> mapM keyValuePairs (filter ((`elem` pws) . W.tag) $ W.workspaces windowSet)
|
||||||
|
where keyValuePairs ws = let wins = W.integrate' (W.stack ws)
|
||||||
|
in mapM (keyValuePair ws) =<< filterM include wins
|
||||||
|
keyValuePair ws w = (, w) <$> titler ws w
|
@ -28,7 +28,7 @@ import XMonad.StackSet
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.Promote
|
-- > import XMonad.Actions.Promote
|
||||||
--
|
--
|
||||||
@ -37,7 +37,7 @@ import XMonad.StackSet
|
|||||||
-- > , ((modm, xK_Return), promote)
|
-- > , ((modm, xK_Return), promote)
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | Move the focused window to the master pane. All other windows
|
-- | Move the focused window to the master pane. All other windows
|
||||||
-- retain their order. If focus is in the master, swap it with the
|
-- retain their order. If focus is in the master, swap it with the
|
||||||
|
84
XMonad/Actions/RepeatAction.hs
Normal file
84
XMonad/Actions/RepeatAction.hs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Actions.RepeatAction
|
||||||
|
-- Description : Repeat the last performed action.
|
||||||
|
-- Copyright : (c) 2022 Martin Kozlovsky
|
||||||
|
-- License : BSD3-style (see LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : <kozlovsky.m7@gmail.com>
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : not portable
|
||||||
|
--
|
||||||
|
-- Ability to repeat the last action.
|
||||||
|
--
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Actions.RepeatAction (
|
||||||
|
-- * Usage
|
||||||
|
-- $usage
|
||||||
|
rememberAction,
|
||||||
|
rememberActions,
|
||||||
|
repeatLast,
|
||||||
|
) where
|
||||||
|
|
||||||
|
import XMonad
|
||||||
|
|
||||||
|
import qualified XMonad.Util.ExtensibleState as XS
|
||||||
|
|
||||||
|
-- $usage
|
||||||
|
--
|
||||||
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
|
--
|
||||||
|
-- > import XMonad.Actions.RepeatAction
|
||||||
|
--
|
||||||
|
-- Then join a dedicated key to run the last action with the rest of your
|
||||||
|
-- key bindings using the 'rememberActions':
|
||||||
|
--
|
||||||
|
-- > rememberActions (modm, xK_period) [((modm, xK_c), kill), …]
|
||||||
|
--
|
||||||
|
-- It can be also used in the same way for "XMonad.Util.EZConfig":
|
||||||
|
--
|
||||||
|
-- > rememberActions "M-." [("M-c", kill), …]
|
||||||
|
--
|
||||||
|
-- For example, if you use 'XMonad.Util.EZConfig.additionalKeysP',
|
||||||
|
--
|
||||||
|
-- > main = xmonad $ … $ def
|
||||||
|
-- > {
|
||||||
|
-- > …
|
||||||
|
-- > }
|
||||||
|
-- > `additionalKeysP` myKeys
|
||||||
|
--
|
||||||
|
-- you would adjust the call to 'XMonad.Util.EZConfig.additionalKeysP'
|
||||||
|
-- like so:
|
||||||
|
--
|
||||||
|
-- > `additionalKeysP` (rememberActions "M-." myKeys)
|
||||||
|
--
|
||||||
|
-- For more detailed instructions on editing your key bindings, see
|
||||||
|
-- <https://xmonad.org/TUTORIAL.html the tutorial>.
|
||||||
|
|
||||||
|
newtype LastAction = LastAction { runLastAction :: X () }
|
||||||
|
|
||||||
|
instance ExtensionClass LastAction where
|
||||||
|
initialValue = LastAction $ pure ()
|
||||||
|
|
||||||
|
-- | Transforms an action into an action that can be remembered and repeated.
|
||||||
|
rememberAction :: X () -> X ()
|
||||||
|
rememberAction x = userCode x >>= \case
|
||||||
|
Nothing -> pure ()
|
||||||
|
Just () -> XS.put (LastAction x) -- Only remember action if nothing went wrong.
|
||||||
|
|
||||||
|
-- | Maps 'rememberAction' over a list of key bindings.
|
||||||
|
rememberActions' :: [(a, X ())] -> [(a, X ())]
|
||||||
|
rememberActions' = map (fmap rememberAction)
|
||||||
|
|
||||||
|
infixl 4 `rememberActions`
|
||||||
|
-- | Maps 'rememberAction' over a list of key bindings and adds a dedicated
|
||||||
|
-- key to repeat the last action.
|
||||||
|
rememberActions :: a -> [(a, X ())] -> [(a, X ())]
|
||||||
|
rememberActions key keyList = (key, repeatLast) : rememberActions' keyList
|
||||||
|
|
||||||
|
-- | Runs the last remembered action.
|
||||||
|
-- / Be careful not to include this action in the remembered actions! /
|
||||||
|
repeatLast :: X ()
|
||||||
|
repeatLast = XS.get >>= runLastAction
|
89
XMonad/Actions/Repeatable.hs
Normal file
89
XMonad/Actions/Repeatable.hs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Actions.Repeatable
|
||||||
|
-- Description : Actions you'd like to repeat.
|
||||||
|
-- Copyright : (c) 2022 L. S. Leary
|
||||||
|
-- License : BSD3-style (see LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : @LSLeary (on github)
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- This module factors out the shared logic of "XMonad.Actions.CycleRecentWS",
|
||||||
|
-- "XMonad.Actions.CycleWorkspaceByScreen", "XMonad.Actions.CycleWindows" and
|
||||||
|
-- "XMonad.Actions.MostRecentlyUsed".
|
||||||
|
--
|
||||||
|
-- See the source of these modules for usage examples.
|
||||||
|
--
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Actions.Repeatable
|
||||||
|
( repeatable
|
||||||
|
, repeatableSt
|
||||||
|
, repeatableM
|
||||||
|
) where
|
||||||
|
|
||||||
|
-- mtl
|
||||||
|
import Control.Monad.State (StateT(..))
|
||||||
|
|
||||||
|
-- X11
|
||||||
|
import Graphics.X11.Xlib.Extras
|
||||||
|
|
||||||
|
-- xmonad
|
||||||
|
import XMonad
|
||||||
|
|
||||||
|
|
||||||
|
-- | An action that temporarily usurps and responds to key press/release events,
|
||||||
|
-- concluding when one of the modifier keys is released.
|
||||||
|
repeatable
|
||||||
|
:: [KeySym] -- ^ The list of 'KeySym's under the
|
||||||
|
-- modifiers used to invoke the action.
|
||||||
|
-> KeySym -- ^ The keypress that invokes the action.
|
||||||
|
-> (EventType -> KeySym -> X ()) -- ^ The keypress handler.
|
||||||
|
-> X ()
|
||||||
|
repeatable = repeatableM id
|
||||||
|
|
||||||
|
-- | A more general variant of 'repeatable' with a stateful handler,
|
||||||
|
-- accumulating a monoidal return value throughout the events.
|
||||||
|
repeatableSt
|
||||||
|
:: Monoid a
|
||||||
|
=> s -- ^ Initial state.
|
||||||
|
-> [KeySym] -- ^ The list of 'KeySym's under the
|
||||||
|
-- modifiers used to invoke the
|
||||||
|
-- action.
|
||||||
|
-> KeySym -- ^ The keypress that invokes the
|
||||||
|
-- action.
|
||||||
|
-> (EventType -> KeySym -> StateT s X a) -- ^ The keypress handler.
|
||||||
|
-> X (a, s)
|
||||||
|
repeatableSt iSt = repeatableM $ \m -> runStateT m iSt
|
||||||
|
|
||||||
|
-- | A more general variant of 'repeatable' with an arbitrary monadic handler,
|
||||||
|
-- accumulating a monoidal return value throughout the events.
|
||||||
|
repeatableM
|
||||||
|
:: (MonadIO m, Monoid a)
|
||||||
|
=> (m a -> X b) -- ^ How to run the monad in 'X'.
|
||||||
|
-> [KeySym] -- ^ The list of 'KeySym's under the
|
||||||
|
-- modifiers used to invoke the action.
|
||||||
|
-> KeySym -- ^ The keypress that invokes the action.
|
||||||
|
-> (EventType -> KeySym -> m a) -- ^ The keypress handler.
|
||||||
|
-> X b
|
||||||
|
repeatableM run mods key pressHandler = do
|
||||||
|
XConf{ theRoot = root, display = d } <- ask
|
||||||
|
run (repeatableRaw d root mods key pressHandler)
|
||||||
|
|
||||||
|
repeatableRaw
|
||||||
|
:: (MonadIO m, Monoid a)
|
||||||
|
=> Display -> Window
|
||||||
|
-> [KeySym] -> KeySym -> (EventType -> KeySym -> m a) -> m a
|
||||||
|
repeatableRaw d root mods key pressHandler = do
|
||||||
|
io (grabKeyboard d root False grabModeAsync grabModeAsync currentTime)
|
||||||
|
handleEvent (keyPress, key) <* io (ungrabKeyboard d currentTime)
|
||||||
|
where
|
||||||
|
getNextEvent = io $ allocaXEvent $ \p -> do
|
||||||
|
maskEvent d (keyPressMask .|. keyReleaseMask) p
|
||||||
|
KeyEvent{ ev_event_type = t, ev_keycode = c } <- getEvent p
|
||||||
|
s <- keycodeToKeysym d c 0
|
||||||
|
return (t, s)
|
||||||
|
handleEvent (t, s)
|
||||||
|
| t == keyRelease && s `elem` mods = pure mempty
|
||||||
|
| otherwise = (<>) <$> pressHandler t s <*> (getNextEvent >>= handleEvent)
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.RotSlaves
|
-- Module : XMonad.Actions.RotSlaves
|
||||||
@ -15,11 +17,16 @@
|
|||||||
module XMonad.Actions.RotSlaves (
|
module XMonad.Actions.RotSlaves (
|
||||||
-- $usage
|
-- $usage
|
||||||
rotSlaves', rotSlavesUp, rotSlavesDown,
|
rotSlaves', rotSlavesUp, rotSlavesDown,
|
||||||
rotAll', rotAllUp, rotAllDown
|
rotAll', rotAllUp, rotAllDown,
|
||||||
|
|
||||||
|
-- * Generic list rotations
|
||||||
|
-- $generic
|
||||||
|
rotUp, rotDown
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import XMonad.StackSet
|
|
||||||
import XMonad
|
import XMonad
|
||||||
|
import XMonad.StackSet
|
||||||
|
import XMonad.Prelude
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
@ -36,28 +43,37 @@ import XMonad
|
|||||||
-- TwoPane layout (see "XMonad.Layout.TwoPane").
|
-- TwoPane layout (see "XMonad.Layout.TwoPane").
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | Rotate the windows in the current stack, excluding the first one
|
-- | Rotate the windows in the current stack, excluding the first one
|
||||||
-- (master).
|
-- (master).
|
||||||
rotSlavesUp,rotSlavesDown :: X ()
|
rotSlavesUp,rotSlavesDown :: X ()
|
||||||
rotSlavesUp = windows $ modify' (rotSlaves' (\l -> tail l++[head l]))
|
rotSlavesUp = windows $ modify' (rotSlaves' rotUp)
|
||||||
rotSlavesDown = windows $ modify' (rotSlaves' (\l -> last l : init l))
|
rotSlavesDown = windows $ modify' (rotSlaves' rotDown)
|
||||||
|
|
||||||
-- | The actual rotation, as a pure function on the window stack.
|
-- | The actual rotation, as a pure function on the window stack.
|
||||||
rotSlaves' :: ([a] -> [a]) -> Stack a -> Stack a
|
rotSlaves' :: ([a] -> [a]) -> Stack a -> Stack a
|
||||||
rotSlaves' _ s@(Stack _ [] []) = s
|
rotSlaves' _ s@(Stack _ [] []) = s
|
||||||
rotSlaves' f (Stack t [] rs) = Stack t [] (f rs) -- Master has focus
|
rotSlaves' f (Stack t [] rs) = Stack t [] (f rs) -- Master has focus
|
||||||
rotSlaves' f s@(Stack _ ls _ ) = Stack t' (reverse revls') rs' -- otherwise
|
rotSlaves' f s@(Stack _ ls _ ) = Stack t' (reverse revls') rs' -- otherwise
|
||||||
where (master:ws) = integrate s
|
where (notEmpty -> master :| ws) = integrate s
|
||||||
(revls',t':rs') = splitAt (length ls) (master:f ws)
|
(revls', notEmpty -> t' :| rs') = splitAt (length ls) (master:f ws)
|
||||||
|
|
||||||
-- | Rotate all the windows in the current stack.
|
-- | Rotate all the windows in the current stack.
|
||||||
rotAllUp,rotAllDown :: X ()
|
rotAllUp,rotAllDown :: X ()
|
||||||
rotAllUp = windows $ modify' (rotAll' (\l -> tail l++[head l]))
|
rotAllUp = windows $ modify' (rotAll' rotUp)
|
||||||
rotAllDown = windows $ modify' (rotAll' (\l -> last l : init l))
|
rotAllDown = windows $ modify' (rotAll' rotDown)
|
||||||
|
|
||||||
-- | The actual rotation, as a pure function on the window stack.
|
-- | The actual rotation, as a pure function on the window stack.
|
||||||
rotAll' :: ([a] -> [a]) -> Stack a -> Stack a
|
rotAll' :: ([a] -> [a]) -> Stack a -> Stack a
|
||||||
rotAll' f s = Stack r (reverse revls) rs
|
rotAll' f s = Stack r (reverse revls) rs
|
||||||
where (revls,r:rs) = splitAt (length (up s)) (f (integrate s))
|
where (revls, notEmpty -> r :| rs) = splitAt (length (up s)) (f (integrate s))
|
||||||
|
|
||||||
|
-- $generic
|
||||||
|
-- Generic list rotations such that @rotUp [1..4]@ is equivalent to
|
||||||
|
-- @[2,3,4,1]@ and @rotDown [1..4]@ to @[4,1,2,3]@. They both are
|
||||||
|
-- @id@ for null or singleton lists.
|
||||||
|
rotUp :: [a] -> [a]
|
||||||
|
rotUp l = drop 1 l ++ take 1 l
|
||||||
|
rotDown :: [a] -> [a]
|
||||||
|
rotDown = reverse . rotUp . reverse
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.RotateSome
|
-- Module : XMonad.Actions.RotateSome
|
||||||
@ -26,14 +28,14 @@ module XMonad.Actions.RotateSome (
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Arrow ((***))
|
import Control.Arrow ((***))
|
||||||
import XMonad.Prelude (partition, sortOn, (\\))
|
import XMonad.Prelude (NonEmpty(..), notEmpty, partition, sortOn, (\\))
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import XMonad (Window, WindowSpace, Rectangle, X, runLayout, screenRect, windows, withWindowSet)
|
import XMonad (Window, WindowSpace, Rectangle, X, runLayout, screenRect, windows, withWindowSet)
|
||||||
import XMonad.StackSet (Screen (Screen), Stack (Stack), current, floating, modify', stack)
|
import XMonad.StackSet (Screen (Screen), Stack (Stack), current, floating, modify', stack)
|
||||||
import XMonad.Util.Stack (reverseS)
|
import XMonad.Util.Stack (reverseS)
|
||||||
|
|
||||||
{- $usage
|
{- $usage
|
||||||
You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
You can use this module with the following in your @xmonad.hs@:
|
||||||
|
|
||||||
> import XMonad.Actions.RotateSome
|
> import XMonad.Actions.RotateSome
|
||||||
|
|
||||||
@ -148,7 +150,7 @@ rotateSome p (Stack t ls rs) =
|
|||||||
-- Append anchored elements, along with their unchanged indices, and sort
|
-- Append anchored elements, along with their unchanged indices, and sort
|
||||||
-- by index. Separate lefts (negative indices) from the rest, and grab the
|
-- by index. Separate lefts (negative indices) from the rest, and grab the
|
||||||
-- new focus from the head of the remaining elements.
|
-- new focus from the head of the remaining elements.
|
||||||
(ls', t':rs') =
|
(ls', notEmpty -> t' :| rs') =
|
||||||
(map snd *** map snd)
|
(map snd *** map snd)
|
||||||
. span ((< 0) . fst)
|
. span ((< 0) . fst)
|
||||||
. sortOn fst
|
. sortOn fst
|
||||||
|
@ -31,33 +31,50 @@ module XMonad.Actions.Search ( -- * Usage
|
|||||||
prefixAware,
|
prefixAware,
|
||||||
namedEngine,
|
namedEngine,
|
||||||
|
|
||||||
amazon,
|
|
||||||
alpha,
|
alpha,
|
||||||
|
amazon,
|
||||||
|
arXiv,
|
||||||
|
aur,
|
||||||
|
clojureDocs,
|
||||||
codesearch,
|
codesearch,
|
||||||
|
cratesIo,
|
||||||
deb,
|
deb,
|
||||||
debbts,
|
debbts,
|
||||||
debpts,
|
debpts,
|
||||||
dictionary,
|
dictionary,
|
||||||
|
duckduckgo,
|
||||||
ebay,
|
ebay,
|
||||||
|
flora,
|
||||||
github,
|
github,
|
||||||
google,
|
google,
|
||||||
hackage,
|
hackage,
|
||||||
|
homeManager,
|
||||||
hoogle,
|
hoogle,
|
||||||
images,
|
images,
|
||||||
imdb,
|
imdb,
|
||||||
lucky,
|
lucky,
|
||||||
maps,
|
maps,
|
||||||
mathworld,
|
mathworld,
|
||||||
|
ncatlab,
|
||||||
|
nixos,
|
||||||
|
noogle,
|
||||||
openstreetmap,
|
openstreetmap,
|
||||||
|
protondb,
|
||||||
|
rosettacode,
|
||||||
|
rustStd,
|
||||||
scholar,
|
scholar,
|
||||||
|
sourcehut,
|
||||||
stackage,
|
stackage,
|
||||||
|
steam,
|
||||||
thesaurus,
|
thesaurus,
|
||||||
|
vocabulary,
|
||||||
|
voidpgks_x86_64,
|
||||||
|
voidpgks_x86_64_musl,
|
||||||
wayback,
|
wayback,
|
||||||
wikipedia,
|
wikipedia,
|
||||||
wiktionary,
|
wiktionary,
|
||||||
youtube,
|
youtube,
|
||||||
vocabulary,
|
zbmath,
|
||||||
duckduckgo,
|
|
||||||
multi,
|
multi,
|
||||||
-- * Use case: searching with a submap
|
-- * Use case: searching with a submap
|
||||||
-- $tip
|
-- $tip
|
||||||
@ -102,12 +119,20 @@ import XMonad.Util.XSelection (getSelection)
|
|||||||
|
|
||||||
The currently available search engines are:
|
The currently available search engines are:
|
||||||
|
|
||||||
* 'amazon' -- Amazon keyword search.
|
|
||||||
|
|
||||||
* 'alpha' -- Wolfram|Alpha query.
|
* 'alpha' -- Wolfram|Alpha query.
|
||||||
|
|
||||||
|
* 'amazon' -- Amazon keyword search.
|
||||||
|
|
||||||
|
* 'arXiv' -- Open-access preprint archive.
|
||||||
|
|
||||||
|
* 'aur' -- Arch User Repository.
|
||||||
|
|
||||||
|
* 'clojureDocs' -- Documentation and examples repository for Clojure.
|
||||||
|
|
||||||
* 'codesearch' -- Google Labs Code Search search.
|
* 'codesearch' -- Google Labs Code Search search.
|
||||||
|
|
||||||
|
* 'cratesIo' -- Rust crate registry.
|
||||||
|
|
||||||
* 'deb' -- Debian package search.
|
* 'deb' -- Debian package search.
|
||||||
|
|
||||||
* 'debbts' -- Debian Bug Tracking System.
|
* 'debbts' -- Debian Bug Tracking System.
|
||||||
@ -116,17 +141,21 @@ import XMonad.Util.XSelection (getSelection)
|
|||||||
|
|
||||||
* 'dictionary' -- dictionary.reference.com search.
|
* 'dictionary' -- dictionary.reference.com search.
|
||||||
|
|
||||||
|
* 'duckduckgo' -- DuckDuckGo search engine.
|
||||||
|
|
||||||
* 'ebay' -- Ebay keyword search.
|
* 'ebay' -- Ebay keyword search.
|
||||||
|
|
||||||
|
* 'flora' -- Prettier Haskell package database.
|
||||||
|
|
||||||
* 'github' -- GitHub keyword search.
|
* 'github' -- GitHub keyword search.
|
||||||
|
|
||||||
* 'google' -- basic Google search.
|
* 'google' -- basic Google search.
|
||||||
|
|
||||||
* 'hackage' -- Hackage, the Haskell package database.
|
* 'hackage' -- Hackage, the Haskell package database.
|
||||||
|
|
||||||
* 'hoogle' -- Hoogle, the Haskell libraries API search engine.
|
* 'homeManager' -- Search Nix's home-manager's options.
|
||||||
|
|
||||||
* 'stackage' -- Stackage, An alternative Haskell libraries API search engine.
|
* 'hoogle' -- Hoogle, the Haskell libraries API search engine.
|
||||||
|
|
||||||
* 'images' -- Google images.
|
* 'images' -- Google images.
|
||||||
|
|
||||||
@ -138,21 +167,45 @@ import XMonad.Util.XSelection (getSelection)
|
|||||||
|
|
||||||
* 'mathworld' -- Wolfram MathWorld search.
|
* 'mathworld' -- Wolfram MathWorld search.
|
||||||
|
|
||||||
|
* 'ncatlab' -- Higer Algebra, Homotopy and Category Theory Wiki.
|
||||||
|
|
||||||
|
* 'nixos' -- Search NixOS packages and options.
|
||||||
|
|
||||||
|
* 'noogle' -- 'hoogle'-like Nix API search engine.
|
||||||
|
|
||||||
* 'openstreetmap' -- OpenStreetMap free wiki world map.
|
* 'openstreetmap' -- OpenStreetMap free wiki world map.
|
||||||
|
|
||||||
|
* 'protondb' -- Steam Proton Game Database.
|
||||||
|
|
||||||
|
* 'rosettacode' -- Programming chrestomathy wiki.
|
||||||
|
|
||||||
|
* 'rustStd' -- Rust standard library documentation.
|
||||||
|
|
||||||
* 'scholar' -- Google scholar academic search.
|
* 'scholar' -- Google scholar academic search.
|
||||||
|
|
||||||
|
* 'sourcehut' -- Sourcehut projects search.
|
||||||
|
|
||||||
|
* 'stackage' -- Stackage, An alternative Haskell libraries API search engine.
|
||||||
|
|
||||||
|
* 'steam' -- Steam games search.
|
||||||
|
|
||||||
* 'thesaurus' -- thesaurus.com search.
|
* 'thesaurus' -- thesaurus.com search.
|
||||||
|
|
||||||
|
* 'vocabulary' -- Dictionary search.
|
||||||
|
|
||||||
|
* 'voidpgks_x86_64' -- Void Linux packages search for @x86_64@.
|
||||||
|
|
||||||
|
* 'voidpgks_x86_64_musl' -- Void Linux packages search for @x86_64-musl@.
|
||||||
|
|
||||||
* 'wayback' -- the Wayback Machine.
|
* 'wayback' -- the Wayback Machine.
|
||||||
|
|
||||||
* 'wikipedia' -- basic Wikipedia search.
|
* 'wikipedia' -- basic Wikipedia search.
|
||||||
|
|
||||||
|
* 'wiktionary' -- Wiktionary search.
|
||||||
|
|
||||||
* 'youtube' -- Youtube video search.
|
* 'youtube' -- Youtube video search.
|
||||||
|
|
||||||
* 'vocabulary' -- Dictionary search
|
* 'zbmath' -- Open alternative to MathSciNet.
|
||||||
|
|
||||||
* 'duckduckgo' -- DuckDuckGo search engine.
|
|
||||||
|
|
||||||
* 'multi' -- Search based on the prefix. \"amazon:Potter\" will use amazon, etc. With no prefix searches google.
|
* 'multi' -- Search based on the prefix. \"amazon:Potter\" will use amazon, etc. With no prefix searches google.
|
||||||
|
|
||||||
@ -269,7 +322,7 @@ searchEngine name site = searchEngineF name (\s -> site ++ escape s)
|
|||||||
inside of a URL instead of in the end) you can use the alternative 'searchEngineF' function.
|
inside of a URL instead of in the end) you can use the alternative 'searchEngineF' function.
|
||||||
|
|
||||||
> searchFunc :: String -> String
|
> searchFunc :: String -> String
|
||||||
> searchFunc s | "wiki:" `isPrefixOf` s = "https://en.wikipedia.org/wiki/" ++ (escape $ tail $ snd $ break (==':') s)
|
> searchFunc s | "wiki:" `isPrefixOf` s = "https://en.wikipedia.org/wiki/" ++ (escape $ drop 1 $ snd $ break (==':') s)
|
||||||
> | "https://" `isPrefixOf` s = s
|
> | "https://" `isPrefixOf` s = s
|
||||||
> | otherwise = (use google) s
|
> | otherwise = (use google) s
|
||||||
> myNewEngine = searchEngineF "mymulti" searchFunc
|
> myNewEngine = searchEngineF "mymulti" searchFunc
|
||||||
@ -286,39 +339,57 @@ searchEngineF :: Name -> Site -> SearchEngine
|
|||||||
searchEngineF = SearchEngine
|
searchEngineF = SearchEngine
|
||||||
|
|
||||||
-- The engines.
|
-- The engines.
|
||||||
amazon, alpha, codesearch, deb, debbts, debpts, dictionary, ebay, github, google, hackage, hoogle,
|
alpha, amazon, arXiv, aur, clojureDocs, codesearch, cratesIo, deb, debbts, debpts, dictionary, duckduckgo, ebay, flora,
|
||||||
images, imdb, lucky, maps, mathworld, openstreetmap, scholar, stackage, thesaurus, vocabulary, wayback, wikipedia, wiktionary,
|
github, google, hackage, homeManager, hoogle, images, imdb, lucky, maps, mathworld, ncatlab, nixos, noogle, openstreetmap, protondb,
|
||||||
youtube, duckduckgo :: SearchEngine
|
rosettacode, rustStd, scholar, sourcehut, stackage, steam, thesaurus, vocabulary, voidpgks_x86_64, voidpgks_x86_64_musl, wayback,
|
||||||
amazon = searchEngine "amazon" "https://www.amazon.com/s/ref=nb_sb_noss_2?url=search-alias%3Daps&field-keywords="
|
wikipedia, wiktionary, youtube, zbmath :: SearchEngine
|
||||||
alpha = searchEngine "alpha" "https://www.wolframalpha.com/input/?i="
|
alpha = searchEngine "alpha" "https://www.wolframalpha.com/input/?i="
|
||||||
|
amazon = searchEngine "amazon" "https://www.amazon.com/s/ref=nb_sb_noss_2?url=search-alias%3Daps&field-keywords="
|
||||||
|
arXiv = searchEngineF "arXiv" (\s -> "https://arxiv.org/search/?query=" <> s <> "&searchtype=all")
|
||||||
|
aur = searchEngine "aur" "https://aur.archlinux.org/packages?&K="
|
||||||
|
clojureDocs = searchEngine "clojureDocs" "https://clojuredocs.org/search?q="
|
||||||
codesearch = searchEngine "codesearch" "https://developers.google.com/s/results/code-search?q="
|
codesearch = searchEngine "codesearch" "https://developers.google.com/s/results/code-search?q="
|
||||||
|
cratesIo = searchEngine "cratesIo" "https://crates.io/search?q="
|
||||||
deb = searchEngine "deb" "https://packages.debian.org/"
|
deb = searchEngine "deb" "https://packages.debian.org/"
|
||||||
debbts = searchEngine "debbts" "https://bugs.debian.org/"
|
debbts = searchEngine "debbts" "https://bugs.debian.org/"
|
||||||
debpts = searchEngine "debpts" "https://packages.qa.debian.org/"
|
debpts = searchEngine "debpts" "https://packages.qa.debian.org/"
|
||||||
dictionary = searchEngine "dict" "https://dictionary.reference.com/browse/"
|
dictionary = searchEngine "dict" "https://dictionary.reference.com/browse/"
|
||||||
|
duckduckgo = searchEngine "duckduckgo" "https://duckduckgo.com/?t=lm&q="
|
||||||
ebay = searchEngine "ebay" "https://www.ebay.com/sch/i.html?_nkw="
|
ebay = searchEngine "ebay" "https://www.ebay.com/sch/i.html?_nkw="
|
||||||
|
flora = searchEngine "flora" "https://flora.pm/search?q="
|
||||||
github = searchEngine "github" "https://github.com/search?q="
|
github = searchEngine "github" "https://github.com/search?q="
|
||||||
google = searchEngine "google" "https://www.google.com/search?num=100&q="
|
google = searchEngine "google" "https://www.google.com/search?q="
|
||||||
hackage = searchEngine "hackage" "https://hackage.haskell.org/package/"
|
hackage = searchEngine "hackage" "https://hackage.haskell.org/package/"
|
||||||
|
homeManager = searchEngine "homeManager" "https://mipmip.github.io/home-manager-option-search/?query="
|
||||||
hoogle = searchEngine "hoogle" "https://hoogle.haskell.org/?hoogle="
|
hoogle = searchEngine "hoogle" "https://hoogle.haskell.org/?hoogle="
|
||||||
images = searchEngine "images" "https://images.google.fr/images?q="
|
images = searchEngine "images" "https://images.google.fr/images?q="
|
||||||
imdb = searchEngine "imdb" "https://www.imdb.com/find?s=all&q="
|
imdb = searchEngine "imdb" "https://www.imdb.com/find?s=all&q="
|
||||||
lucky = searchEngine "lucky" "https://www.google.com/search?btnI&q="
|
lucky = searchEngine "lucky" "https://www.google.com/search?btnI&q="
|
||||||
maps = searchEngine "maps" "https://maps.google.com/maps?q="
|
maps = searchEngine "maps" "https://maps.google.com/maps?q="
|
||||||
mathworld = searchEngine "mathworld" "https://mathworld.wolfram.com/search/?query="
|
mathworld = searchEngine "mathworld" "https://mathworld.wolfram.com/search/?query="
|
||||||
|
ncatlab = searchEngine "ncatlab" "https://ncatlab.org/nlab/search?query="
|
||||||
|
nixos = searchEngine "nixos" "https://search.nixos.org/packages?channel=unstable&from=0&size=200&sort=relevance&type=packages&query="
|
||||||
|
noogle = searchEngineF "noogle" (\s -> "https://noogle.dev/?search=" <> s <> "&page=1&to=any&from=any")
|
||||||
openstreetmap = searchEngine "openstreetmap" "https://www.openstreetmap.org/search?query="
|
openstreetmap = searchEngine "openstreetmap" "https://www.openstreetmap.org/search?query="
|
||||||
|
protondb = searchEngine "protondb" "https://www.protondb.com/search?q="
|
||||||
|
rosettacode = searchEngine "rosettacode" "https://rosettacode.org/w/index.php?search="
|
||||||
|
rustStd = searchEngine "rustStd" "https://doc.rust-lang.org/std/index.html?search="
|
||||||
scholar = searchEngine "scholar" "https://scholar.google.com/scholar?q="
|
scholar = searchEngine "scholar" "https://scholar.google.com/scholar?q="
|
||||||
|
sourcehut = searchEngine "sourcehut" "https://sr.ht/projects?search="
|
||||||
stackage = searchEngine "stackage" "https://www.stackage.org/lts/hoogle?q="
|
stackage = searchEngine "stackage" "https://www.stackage.org/lts/hoogle?q="
|
||||||
|
steam = searchEngine "steam" "https://store.steampowered.com/search/?term="
|
||||||
thesaurus = searchEngine "thesaurus" "https://thesaurus.com/browse/"
|
thesaurus = searchEngine "thesaurus" "https://thesaurus.com/browse/"
|
||||||
|
vocabulary = searchEngine "vocabulary" "https://www.vocabulary.com/search?q="
|
||||||
|
voidpgks_x86_64 = searchEngine "voidpackages" "https://voidlinux.org/packages/?arch=x86_64&q="
|
||||||
|
voidpgks_x86_64_musl = searchEngine "voidpackages" "https://voidlinux.org/packages/?arch=x86_64-musl&q="
|
||||||
|
wayback = searchEngineF "wayback" ("https://web.archive.org/web/*/"++)
|
||||||
wikipedia = searchEngine "wiki" "https://en.wikipedia.org/wiki/Special:Search?go=Go&search="
|
wikipedia = searchEngine "wiki" "https://en.wikipedia.org/wiki/Special:Search?go=Go&search="
|
||||||
wiktionary = searchEngine "wikt" "https://en.wiktionary.org/wiki/Special:Search?go=Go&search="
|
wiktionary = searchEngine "wikt" "https://en.wiktionary.org/wiki/Special:Search?go=Go&search="
|
||||||
youtube = searchEngine "youtube" "https://www.youtube.com/results?search_type=search_videos&search_query="
|
youtube = searchEngine "youtube" "https://www.youtube.com/results?search_type=search_videos&search_query="
|
||||||
wayback = searchEngineF "wayback" ("https://web.archive.org/web/*/"++)
|
zbmath = searchEngine "zbmath" "https://zbmath.org/?q="
|
||||||
vocabulary = searchEngine "vocabulary" "https://www.vocabulary.com/search?q="
|
|
||||||
duckduckgo = searchEngine "duckduckgo" "https://duckduckgo.com/?t=lm&q="
|
|
||||||
|
|
||||||
multi :: SearchEngine
|
multi :: SearchEngine
|
||||||
multi = namedEngine "multi" $ foldr1 (!>) [amazon, alpha, codesearch, deb, debbts, debpts, dictionary, ebay, github, google, hackage, hoogle, images, imdb, lucky, maps, mathworld, openstreetmap, scholar, thesaurus, wayback, wikipedia, wiktionary, duckduckgo, prefixAware google]
|
multi = namedEngine "multi" $ foldr1 (!>) [alpha, amazon, aur, codesearch, deb, debbts, debpts, dictionary, duckduckgo, ebay, flora, github, hackage, hoogle, images, imdb, lucky, maps, mathworld, ncatlab, openstreetmap, protondb, rosettacode, scholar, sourcehut, stackage, steam, thesaurus, vocabulary, voidpgks_x86_64, voidpgks_x86_64_musl, wayback, wikipedia, wiktionary, youtube, prefixAware google]
|
||||||
|
|
||||||
{- | This function wraps up a search engine and creates a new one, which works
|
{- | This function wraps up a search engine and creates a new one, which works
|
||||||
like the argument, but goes directly to a URL if one is given rather than
|
like the argument, but goes directly to a URL if one is given rather than
|
||||||
@ -366,14 +437,14 @@ namedEngine name (SearchEngine _ site) = searchEngineF name site
|
|||||||
browser. -}
|
browser. -}
|
||||||
promptSearchBrowser :: XPConfig -> Browser -> SearchEngine -> X ()
|
promptSearchBrowser :: XPConfig -> Browser -> SearchEngine -> X ()
|
||||||
promptSearchBrowser config browser (SearchEngine name site) = do
|
promptSearchBrowser config browser (SearchEngine name site) = do
|
||||||
hc <- historyCompletionP ("Search [" `isPrefixOf`)
|
hc <- historyCompletionP config ("Search [" `isPrefixOf`)
|
||||||
mkXPrompt (Search name) config hc $ search browser site
|
mkXPrompt (Search name) config hc $ search browser site
|
||||||
|
|
||||||
{- | Like 'promptSearchBrowser', but only suggest previous searches for the
|
{- | Like 'promptSearchBrowser', but only suggest previous searches for the
|
||||||
given 'SearchEngine' in the prompt. -}
|
given 'SearchEngine' in the prompt. -}
|
||||||
promptSearchBrowser' :: XPConfig -> Browser -> SearchEngine -> X ()
|
promptSearchBrowser' :: XPConfig -> Browser -> SearchEngine -> X ()
|
||||||
promptSearchBrowser' config browser (SearchEngine name site) = do
|
promptSearchBrowser' config browser (SearchEngine name site) = do
|
||||||
hc <- historyCompletionP (searchName `isPrefixOf`)
|
hc <- historyCompletionP config (searchName `isPrefixOf`)
|
||||||
mkXPrompt (Search name) config hc $ search browser site
|
mkXPrompt (Search name) config hc $ search browser site
|
||||||
where
|
where
|
||||||
searchName = showXPrompt (Search name)
|
searchName = showXPrompt (Search name)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# LANGUAGE MultiWayIf #-}
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.ShowText
|
-- Module : XMonad.Actions.ShowText
|
||||||
@ -26,7 +27,7 @@ module XMonad.Actions.ShowText
|
|||||||
import Data.Map (Map,empty,insert,lookup)
|
import Data.Map (Map,empty,insert,lookup)
|
||||||
import Prelude hiding (lookup)
|
import Prelude hiding (lookup)
|
||||||
import XMonad
|
import XMonad
|
||||||
import XMonad.Prelude (All, fi, when)
|
import XMonad.Prelude (All, fi, listToMaybe)
|
||||||
import XMonad.StackSet (current,screen)
|
import XMonad.StackSet (current,screen)
|
||||||
import XMonad.Util.Font (Align(AlignCenter)
|
import XMonad.Util.Font (Align(AlignCenter)
|
||||||
, initXMF
|
, initXMF
|
||||||
@ -41,13 +42,13 @@ import XMonad.Util.XUtils (createNewWindow
|
|||||||
import qualified XMonad.Util.ExtensibleState as ES
|
import qualified XMonad.Util.ExtensibleState as ES
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.ShowText
|
-- > import XMonad.Actions.ShowText
|
||||||
--
|
--
|
||||||
-- Then add the event hook handler:
|
-- Then add the event hook handler:
|
||||||
--
|
--
|
||||||
-- > xmonad { handleEventHook = myHandleEventHooks <+> handleTimerEvent }
|
-- > xmonad { handleEventHook = myHandleEventHooks <> handleTimerEvent }
|
||||||
--
|
--
|
||||||
-- You can then use flashText in your keybindings:
|
-- You can then use flashText in your keybindings:
|
||||||
--
|
--
|
||||||
@ -87,8 +88,9 @@ handleTimerEvent :: Event -> X All
|
|||||||
handleTimerEvent (ClientMessageEvent _ _ _ dis _ mtyp d) = do
|
handleTimerEvent (ClientMessageEvent _ _ _ dis _ mtyp d) = do
|
||||||
(ShowText m) <- ES.get :: X ShowText
|
(ShowText m) <- ES.get :: X ShowText
|
||||||
a <- io $ internAtom dis "XMONAD_TIMER" False
|
a <- io $ internAtom dis "XMONAD_TIMER" False
|
||||||
when (mtyp == a && not (null d))
|
if | mtyp == a, Just dh <- listToMaybe d ->
|
||||||
(whenJust (lookup (fromIntegral $ head d) m) deleteWindow)
|
whenJust (lookup (fromIntegral dh) m) deleteWindow
|
||||||
|
| otherwise -> pure ()
|
||||||
mempty
|
mempty
|
||||||
handleTimerEvent _ = mempty
|
handleTimerEvent _ = mempty
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ import XMonad.StackSet (Stack (Stack), StackSet, modify')
|
|||||||
import XMonad.Util.Stack (reverseS)
|
import XMonad.Util.Stack (reverseS)
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.Sift
|
-- > import XMonad.Actions.Sift
|
||||||
--
|
--
|
||||||
|
@ -24,7 +24,7 @@ import XMonad.Core
|
|||||||
import XMonad.Util.Run
|
import XMonad.Util.Run
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- To use, import this module into @~\/.xmonad\/xmonad.hs@:
|
-- To use, import this module into @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.SimpleDate
|
-- > import XMonad.Actions.SimpleDate
|
||||||
--
|
--
|
||||||
@ -35,7 +35,7 @@ import XMonad.Util.Run
|
|||||||
-- In this example, a popup date menu will now be bound to @mod-d@.
|
-- In this example, a popup date menu will now be bound to @mod-d@.
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
date :: X ()
|
date :: X ()
|
||||||
date = unsafeSpawn "(date; sleep 10) | dzen2"
|
date = unsafeSpawn "(date; sleep 10) | dzen2"
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
-- 'sinkAll' function for backwards compatibility.
|
-- 'sinkAll' function for backwards compatibility.
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
module XMonad.Actions.SinkAll (
|
module XMonad.Actions.SinkAll {-# DEPRECATED "Use XMonad.Actions.WithAll instead" #-} (
|
||||||
-- * Usage
|
-- * Usage
|
||||||
-- $usage
|
-- $usage
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ import XMonad.Actions.WithAll (sinkAll)
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.SinkAll
|
-- > import XMonad.Actions.SinkAll
|
||||||
--
|
--
|
||||||
@ -32,4 +32,4 @@ import XMonad.Actions.WithAll (sinkAll)
|
|||||||
-- > , ((modm .|. shiftMask, xK_t), sinkAll)
|
-- > , ((modm .|. shiftMask, xK_t), sinkAll)
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.SpawnOn
|
-- Module : XMonad.Actions.SpawnOn
|
||||||
@ -28,11 +30,7 @@ module XMonad.Actions.SpawnOn (
|
|||||||
shellPromptOn
|
shellPromptOn
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Exception (tryJust)
|
|
||||||
import System.IO.Error (isDoesNotExistError)
|
|
||||||
import System.IO.Unsafe (unsafePerformIO)
|
|
||||||
import System.Posix.Types (ProcessID)
|
import System.Posix.Types (ProcessID)
|
||||||
import Text.Printf (printf)
|
|
||||||
|
|
||||||
import XMonad
|
import XMonad
|
||||||
import XMonad.Prelude
|
import XMonad.Prelude
|
||||||
@ -42,16 +40,17 @@ import XMonad.Hooks.ManageHelpers
|
|||||||
import XMonad.Prompt
|
import XMonad.Prompt
|
||||||
import XMonad.Prompt.Shell
|
import XMonad.Prompt.Shell
|
||||||
import qualified XMonad.Util.ExtensibleState as XS
|
import qualified XMonad.Util.ExtensibleState as XS
|
||||||
|
import XMonad.Util.Process (getPPIDChain)
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.SpawnOn
|
-- > import XMonad.Actions.SpawnOn
|
||||||
--
|
--
|
||||||
-- > main = do
|
-- > main = do
|
||||||
-- > xmonad def {
|
-- > xmonad def {
|
||||||
-- > ...
|
-- > ...
|
||||||
-- > manageHook = manageSpawn <+> manageHook def
|
-- > manageHook = manageSpawn <> manageHook def
|
||||||
-- > ...
|
-- > ...
|
||||||
-- > }
|
-- > }
|
||||||
--
|
--
|
||||||
@ -64,7 +63,7 @@ import qualified XMonad.Util.ExtensibleState as XS
|
|||||||
-- the spawned application(e.g. float or resize it).
|
-- the spawned application(e.g. float or resize it).
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
newtype Spawner = Spawner {pidsRef :: [(ProcessID, ManageHook)]}
|
newtype Spawner = Spawner {pidsRef :: [(ProcessID, ManageHook)]}
|
||||||
|
|
||||||
@ -72,29 +71,13 @@ instance ExtensionClass Spawner where
|
|||||||
initialValue = Spawner []
|
initialValue = Spawner []
|
||||||
|
|
||||||
|
|
||||||
getPPIDOf :: ProcessID -> Maybe ProcessID
|
|
||||||
getPPIDOf thisPid =
|
|
||||||
case unsafePerformIO . tryJust (guard . isDoesNotExistError) . readFile . printf "/proc/%d/stat" $ toInteger thisPid of
|
|
||||||
Left _ -> Nothing
|
|
||||||
Right contents -> case lines contents of
|
|
||||||
[] -> Nothing
|
|
||||||
first : _ -> case words first of
|
|
||||||
_ : _ : _ : ppid : _ -> Just $ fromIntegral (read ppid :: Int)
|
|
||||||
_ -> Nothing
|
|
||||||
|
|
||||||
getPPIDChain :: ProcessID -> [ProcessID]
|
|
||||||
getPPIDChain thisPid = ppid_chain thisPid []
|
|
||||||
where ppid_chain pid' acc =
|
|
||||||
if pid' == 0
|
|
||||||
then acc
|
|
||||||
else case getPPIDOf pid' of
|
|
||||||
Nothing -> acc
|
|
||||||
Just ppid -> ppid_chain ppid (ppid : acc)
|
|
||||||
|
|
||||||
-- | Get the current Spawner or create one if it doesn't exist.
|
-- | Get the current Spawner or create one if it doesn't exist.
|
||||||
modifySpawner :: ([(ProcessID, ManageHook)] -> [(ProcessID, ManageHook)]) -> X ()
|
modifySpawner :: ([(ProcessID, ManageHook)] -> [(ProcessID, ManageHook)]) -> X ()
|
||||||
modifySpawner f = XS.modify (Spawner . f . pidsRef)
|
modifySpawner f = XS.modify (Spawner . f . pidsRef)
|
||||||
|
|
||||||
|
modifySpawnerM :: ([(ProcessID, ManageHook)] -> X [(ProcessID, ManageHook)]) -> X ()
|
||||||
|
modifySpawnerM f = XS.modifyM (fmap Spawner . f . pidsRef)
|
||||||
|
|
||||||
-- | Provides a manage hook to react on process spawned with
|
-- | Provides a manage hook to react on process spawned with
|
||||||
-- 'spawnOn', 'spawnHere' etc.
|
-- 'spawnOn', 'spawnHere' etc.
|
||||||
manageSpawn :: ManageHook
|
manageSpawn :: ManageHook
|
||||||
@ -103,24 +86,16 @@ manageSpawn = manageSpawnWithGC (return . take 20)
|
|||||||
manageSpawnWithGC :: ([(ProcessID, ManageHook)] -> X [(ProcessID, ManageHook)])
|
manageSpawnWithGC :: ([(ProcessID, ManageHook)] -> X [(ProcessID, ManageHook)])
|
||||||
-- ^ function to stop accumulation of entries for windows that never set @_NET_WM_PID@
|
-- ^ function to stop accumulation of entries for windows that never set @_NET_WM_PID@
|
||||||
-> ManageHook
|
-> ManageHook
|
||||||
manageSpawnWithGC garbageCollect = do
|
manageSpawnWithGC garbageCollect = pid >>= \case
|
||||||
Spawner pids <- liftX XS.get
|
Nothing -> mempty
|
||||||
mp <- pid
|
Just p -> do
|
||||||
let ppid_chain = case mp of
|
Spawner pids <- liftX XS.get
|
||||||
Just winpid -> winpid : getPPIDChain winpid
|
ppid_chain <- io $ getPPIDChain p
|
||||||
Nothing -> []
|
case mapMaybe (`lookup` pids) ppid_chain of
|
||||||
known_window_handlers = [ mh
|
[] -> mempty
|
||||||
| ppid <- ppid_chain
|
mh : _ -> liftX (gc p) >> mh
|
||||||
, let mpid = lookup ppid pids
|
where
|
||||||
, isJust mpid
|
gc p = modifySpawnerM $ garbageCollect . filter ((/= p) . fst)
|
||||||
, let (Just mh) = mpid ]
|
|
||||||
case known_window_handlers of
|
|
||||||
[] -> idHook
|
|
||||||
(mh:_) -> do
|
|
||||||
whenJust mp $ \p -> liftX $ do
|
|
||||||
ps <- XS.gets pidsRef
|
|
||||||
XS.put . Spawner =<< garbageCollect (filter ((/= p) . fst) ps)
|
|
||||||
mh
|
|
||||||
|
|
||||||
mkPrompt :: (String -> X ()) -> XPConfig -> X ()
|
mkPrompt :: (String -> X ()) -> XPConfig -> X ()
|
||||||
mkPrompt cb c = do
|
mkPrompt cb c = do
|
||||||
|
@ -17,20 +17,23 @@ module XMonad.Actions.Submap (
|
|||||||
-- * Usage
|
-- * Usage
|
||||||
-- $usage
|
-- $usage
|
||||||
submap,
|
submap,
|
||||||
|
visualSubmap,
|
||||||
|
visualSubmapSorted,
|
||||||
submapDefault,
|
submapDefault,
|
||||||
submapDefaultWithKey
|
submapDefaultWithKey,
|
||||||
|
|
||||||
|
-- * Utilities
|
||||||
|
subName,
|
||||||
) where
|
) where
|
||||||
import Data.Bits
|
import Data.Bits
|
||||||
import XMonad.Prelude (fix, fromMaybe)
|
|
||||||
import XMonad hiding (keys)
|
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
import XMonad hiding (keys)
|
||||||
|
import XMonad.Prelude (fix, fromMaybe, keyToString, cleanKeyMask)
|
||||||
|
import XMonad.Util.XUtils
|
||||||
|
|
||||||
{- $usage
|
{- $usage
|
||||||
|
|
||||||
|
First, import this module into your @xmonad.hs@:
|
||||||
|
|
||||||
|
|
||||||
First, import this module into your @~\/.xmonad\/xmonad.hs@:
|
|
||||||
|
|
||||||
> import XMonad.Actions.Submap
|
> import XMonad.Actions.Submap
|
||||||
|
|
||||||
@ -51,7 +54,7 @@ because that is a special value passed to XGrabKey() and not an actual
|
|||||||
modifier.
|
modifier.
|
||||||
|
|
||||||
For detailed instructions on editing your key bindings, see
|
For detailed instructions on editing your key bindings, see
|
||||||
"XMonad.Doc.Extending#Editing_key_bindings".
|
<https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
@ -62,6 +65,61 @@ For detailed instructions on editing your key bindings, see
|
|||||||
submap :: M.Map (KeyMask, KeySym) (X ()) -> X ()
|
submap :: M.Map (KeyMask, KeySym) (X ()) -> X ()
|
||||||
submap = submapDefault (return ())
|
submap = submapDefault (return ())
|
||||||
|
|
||||||
|
-- | Like 'submap', but visualise the relevant options.
|
||||||
|
--
|
||||||
|
-- ==== __Example__
|
||||||
|
--
|
||||||
|
-- > import qualified Data.Map as Map
|
||||||
|
-- > import XMonad.Actions.Submap
|
||||||
|
-- >
|
||||||
|
-- > gotoLayout :: [(String, X ())] -- for use with EZConfig
|
||||||
|
-- > gotoLayout = -- assumes you have a layout named "Tall" and one named "Full".
|
||||||
|
-- > [("M-l", visualSubmap def $ Map.fromList $ map (\(k, s, a) -> ((0, k), (s, a)))
|
||||||
|
-- > [ (xK_t, "Tall", switchToLayout "Tall") -- "M-l t" switches to "Tall"
|
||||||
|
-- > , (xK_r, "Full", switchToLayout "Full") -- "M-l r" switches to "full"
|
||||||
|
-- > ])]
|
||||||
|
--
|
||||||
|
-- One could alternatively also write @gotoLayout@ as
|
||||||
|
--
|
||||||
|
-- > gotoLayout = [("M-l", visualSubmap def $ Map.fromList $
|
||||||
|
-- > [ ((0, xK_t), subName "Tall" $ switchToLayout "Tall")
|
||||||
|
-- > , ((0, xK_r), subName "Full" $ switchToLayout "Full")
|
||||||
|
-- > ])]
|
||||||
|
visualSubmap :: WindowConfig -- ^ The config for the spawned window.
|
||||||
|
-> M.Map (KeyMask, KeySym) (String, X ())
|
||||||
|
-- ^ A map @keybinding -> (description, action)@.
|
||||||
|
-> X ()
|
||||||
|
visualSubmap = visualSubmapSorted id
|
||||||
|
|
||||||
|
-- | Like 'visualSubmap', but is able to sort the descriptions.
|
||||||
|
-- For example,
|
||||||
|
--
|
||||||
|
-- > import Data.Ord (comparing, Down)
|
||||||
|
-- >
|
||||||
|
-- > visualSubmapSorted (sortBy (comparing Down)) def
|
||||||
|
--
|
||||||
|
-- would sort the @(key, description)@ pairs by their keys in descending
|
||||||
|
-- order.
|
||||||
|
visualSubmapSorted :: ([((KeyMask, KeySym), String)] -> [((KeyMask, KeySym), String)])
|
||||||
|
-- ^ A function to resort the descriptions
|
||||||
|
-> WindowConfig -- ^ The config for the spawned window.
|
||||||
|
-> M.Map (KeyMask, KeySym) (String, X ())
|
||||||
|
-- ^ A map @keybinding -> (description, action)@.
|
||||||
|
-> X ()
|
||||||
|
visualSubmapSorted sorted wc keys =
|
||||||
|
withSimpleWindow wc descriptions waitForKeyPress >>= \(m', s) ->
|
||||||
|
maybe (pure ()) snd (M.lookup (m', s) keys)
|
||||||
|
where
|
||||||
|
descriptions :: [String]
|
||||||
|
descriptions =
|
||||||
|
map (\(key, desc) -> keyToString key <> ": " <> desc)
|
||||||
|
. sorted
|
||||||
|
$ zip (M.keys keys) (map fst (M.elems keys))
|
||||||
|
|
||||||
|
-- | Give a name to an action.
|
||||||
|
subName :: String -> X () -> (String, X ())
|
||||||
|
subName = (,)
|
||||||
|
|
||||||
-- | Like 'submap', but executes a default action if the key did not match.
|
-- | Like 'submap', but executes a default action if the key did not match.
|
||||||
submapDefault :: X () -> M.Map (KeyMask, KeySym) (X ()) -> X ()
|
submapDefault :: X () -> M.Map (KeyMask, KeySym) (X ()) -> X ()
|
||||||
submapDefault = submapDefaultWithKey . const
|
submapDefault = submapDefaultWithKey . const
|
||||||
@ -71,28 +129,32 @@ submapDefault = submapDefaultWithKey . const
|
|||||||
submapDefaultWithKey :: ((KeyMask, KeySym) -> X ())
|
submapDefaultWithKey :: ((KeyMask, KeySym) -> X ())
|
||||||
-> M.Map (KeyMask, KeySym) (X ())
|
-> M.Map (KeyMask, KeySym) (X ())
|
||||||
-> X ()
|
-> X ()
|
||||||
submapDefaultWithKey defAction keys = do
|
submapDefaultWithKey defAction keys = waitForKeyPress >>=
|
||||||
XConf { theRoot = root, display = d } <- ask
|
\(m', s) -> fromMaybe (defAction (m', s)) (M.lookup (m', s) keys)
|
||||||
|
|
||||||
io $ grabKeyboard d root False grabModeAsync grabModeAsync currentTime
|
-----------------------------------------------------------------------
|
||||||
io $ grabPointer d root False buttonPressMask grabModeAsync grabModeAsync
|
-- Internal stuff
|
||||||
none none currentTime
|
|
||||||
|
waitForKeyPress :: X (KeyMask, KeySym)
|
||||||
|
waitForKeyPress = do
|
||||||
|
XConf{ theRoot = root, display = dpy } <- ask
|
||||||
|
|
||||||
|
io $ do grabKeyboard dpy root False grabModeAsync grabModeAsync currentTime
|
||||||
|
grabPointer dpy root False buttonPressMask grabModeAsync grabModeAsync
|
||||||
|
none none currentTime
|
||||||
|
|
||||||
(m, s) <- io $ allocaXEvent $ \p -> fix $ \nextkey -> do
|
(m, s) <- io $ allocaXEvent $ \p -> fix $ \nextkey -> do
|
||||||
maskEvent d (keyPressMask .|. buttonPressMask) p
|
maskEvent dpy (keyPressMask .|. buttonPressMask) p
|
||||||
ev <- getEvent p
|
ev <- getEvent p
|
||||||
case ev of
|
case ev of
|
||||||
KeyEvent { ev_keycode = code, ev_state = m } -> do
|
KeyEvent { ev_keycode = code, ev_state = m } -> do
|
||||||
keysym <- keycodeToKeysym d code 0
|
keysym <- keycodeToKeysym dpy code 0
|
||||||
if isModifierKey keysym
|
if isModifierKey keysym
|
||||||
then nextkey
|
then nextkey
|
||||||
else return (m, keysym)
|
else return (m, keysym)
|
||||||
_ -> return (0, 0)
|
_ -> return (0, 0)
|
||||||
-- Remove num lock mask and Xkb group state bits
|
m' <- cleanKeyMask <*> pure m
|
||||||
m' <- cleanMask $ m .&. ((1 `shiftL` 12) - 1)
|
io $ do ungrabPointer dpy currentTime
|
||||||
|
ungrabKeyboard dpy currentTime
|
||||||
io $ ungrabPointer d currentTime
|
sync dpy False
|
||||||
io $ ungrabKeyboard d currentTime
|
pure (m', s)
|
||||||
io $ sync d False
|
|
||||||
|
|
||||||
fromMaybe (defAction (m', s)) (M.lookup (m', s) keys)
|
|
||||||
|
@ -63,6 +63,7 @@ import qualified XMonad.Util.ExtensibleState as XS
|
|||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
import Control.Arrow
|
import Control.Arrow
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
|
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
@ -99,7 +100,7 @@ import Control.Arrow
|
|||||||
-- So far floating windows have been treated no differently than tiled windows
|
-- So far floating windows have been treated no differently than tiled windows
|
||||||
-- even though their positions are independent of the stack. Often, yanking
|
-- even though their positions are independent of the stack. Often, yanking
|
||||||
-- floating windows in and out of the workspace will obliterate the stack
|
-- floating windows in and out of the workspace will obliterate the stack
|
||||||
-- history - particularly frustrating with 'XMonad.Util.Scratchpad' since it is
|
-- history - particularly frustrating with "XMonad.Util.Scratchpad" since it is
|
||||||
-- toggled so frequenty and always replaces the master window. That's why the
|
-- toggled so frequenty and always replaces the master window. That's why the
|
||||||
-- swap functions accept a boolean argument; when @True@ non-focused floating
|
-- swap functions accept a boolean argument; when @True@ non-focused floating
|
||||||
-- windows will be ignored.
|
-- windows will be ignored.
|
||||||
@ -240,8 +241,8 @@ swapApply ignoreFloats swapFunction = do
|
|||||||
(r,s2) = stackSplit s1 fl' :: ([(Int,Window)],W.Stack Window)
|
(r,s2) = stackSplit s1 fl' :: ([(Int,Window)],W.Stack Window)
|
||||||
(b,s3) = swapFunction pm s2
|
(b,s3) = swapFunction pm s2
|
||||||
s4 = stackMerge s3 r
|
s4 = stackMerge s3 r
|
||||||
mh = let w = head . W.integrate $ s3
|
mh = let w = NE.head . notEmpty . W.integrate $ s3
|
||||||
in const $ w : delete w ch
|
in const $ w : delete w ch
|
||||||
in (b,Just s4,mh)
|
in (b,Just s4,mh)
|
||||||
(x,y,z) = maybe (False,Nothing,id) swapApply' st
|
(x,y,z) = maybe (False,Nothing,id) swapApply' st
|
||||||
-- Any floating master windows will be added to the history when 'windows'
|
-- Any floating master windows will be added to the history when 'windows'
|
||||||
|
@ -30,7 +30,7 @@ import XMonad.Util.WorkspaceCompare
|
|||||||
|
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- Add this import to your @~\/.xmonad\/xmonad.hs@:
|
-- Add this import to your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.SwapWorkspaces
|
-- > import XMonad.Actions.SwapWorkspaces
|
||||||
--
|
--
|
||||||
@ -44,7 +44,7 @@ import XMonad.Util.WorkspaceCompare
|
|||||||
-- will swap workspaces 1 and 5.
|
-- will swap workspaces 1 and 5.
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | Swaps the currently focused workspace with the given workspace tag, via
|
-- | Swaps the currently focused workspace with the given workspace tag, via
|
||||||
-- @swapWorkspaces@.
|
-- @swapWorkspaces@.
|
||||||
|
@ -39,7 +39,7 @@ econst = const . return
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- To use window tags, import this module into your @~\/.xmonad\/xmonad.hs@:
|
-- To use window tags, import this module into your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.TagWindows
|
-- > import XMonad.Actions.TagWindows
|
||||||
-- > import XMonad.Prompt -- to use tagPrompt
|
-- > import XMonad.Prompt -- to use tagPrompt
|
||||||
@ -64,7 +64,7 @@ econst = const . return
|
|||||||
-- the tags \"a\" and \"b\" but not \"a b\".
|
-- the tags \"a\" and \"b\" but not \"a b\".
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | set multiple tags for a window at once (overriding any previous tags)
|
-- | set multiple tags for a window at once (overriding any previous tags)
|
||||||
setTags :: [String] -> Window -> X ()
|
setTags :: [String] -> Window -> X ()
|
||||||
@ -134,11 +134,6 @@ focusTagged' :: (WindowSet -> [Window]) -> String -> X ()
|
|||||||
focusTagged' wl t = gets windowset >>= findM (hasTag t) . wl >>=
|
focusTagged' wl t = gets windowset >>= findM (hasTag t) . wl >>=
|
||||||
maybe (return ()) (windows . focusWindow)
|
maybe (return ()) (windows . focusWindow)
|
||||||
|
|
||||||
findM :: (Monad m) => (a -> m Bool) -> [a] -> m (Maybe a)
|
|
||||||
findM _ [] = return Nothing
|
|
||||||
findM p (x:xs) = do b <- p x
|
|
||||||
if b then return (Just x) else findM p xs
|
|
||||||
|
|
||||||
-- | apply a pure function to windows with a tag
|
-- | apply a pure function to windows with a tag
|
||||||
withTaggedP, withTaggedGlobalP :: String -> (Window -> WindowSet -> WindowSet) -> X ()
|
withTaggedP, withTaggedGlobalP :: String -> (Window -> WindowSet -> WindowSet) -> X ()
|
||||||
withTaggedP t f = withTagged' t (winMap f)
|
withTaggedP t f = withTagged' t (winMap f)
|
||||||
|
@ -27,7 +27,7 @@ import qualified XMonad.StackSet as W
|
|||||||
import XMonad.Layout.DraggingVisualizer
|
import XMonad.Layout.DraggingVisualizer
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.TiledWindowDragging
|
-- > import XMonad.Actions.TiledWindowDragging
|
||||||
-- > import XMonad.Layout.DraggingVisualizer
|
-- > import XMonad.Layout.DraggingVisualizer
|
||||||
@ -48,10 +48,11 @@ import XMonad.Layout.DraggingVisualizer
|
|||||||
-- | Create a mouse binding for this to be able to drag your windows around.
|
-- | Create a mouse binding for this to be able to drag your windows around.
|
||||||
-- You need "XMonad.Layout.DraggingVisualizer" for this to look good.
|
-- You need "XMonad.Layout.DraggingVisualizer" for this to look good.
|
||||||
dragWindow :: Window -> X ()
|
dragWindow :: Window -> X ()
|
||||||
dragWindow window = whenX (isClient window) $ do
|
dragWindow window = whenX (isClient window) $ withDisplay $ \dpy ->
|
||||||
|
withWindowAttributes dpy window $ \wa -> do
|
||||||
focus window
|
focus window
|
||||||
(offsetX, offsetY) <- getPointerOffset window
|
(offsetX, offsetY) <- getPointerOffset window
|
||||||
(winX, winY, winWidth, winHeight) <- getWindowPlacement window
|
let (winX, winY, winWidth, winHeight) = getWindowPlacement wa
|
||||||
|
|
||||||
mouseDrag
|
mouseDrag
|
||||||
(\posX posY ->
|
(\posX posY ->
|
||||||
@ -71,11 +72,8 @@ getPointerOffset win = do
|
|||||||
return (fi oX, fi oY)
|
return (fi oX, fi oY)
|
||||||
|
|
||||||
-- | return a tuple of windowX, windowY, windowWidth, windowHeight
|
-- | return a tuple of windowX, windowY, windowWidth, windowHeight
|
||||||
getWindowPlacement :: Window -> X (Int, Int, Int, Int)
|
getWindowPlacement :: WindowAttributes -> (Int, Int, Int, Int)
|
||||||
getWindowPlacement window = do
|
getWindowPlacement wa = (fi $ wa_x wa, fi $ wa_y wa, fi $ wa_width wa, fi $ wa_height wa)
|
||||||
wa <- withDisplay (\d -> io $ getWindowAttributes d window)
|
|
||||||
return (fi $ wa_x wa, fi $ wa_y wa, fi $ wa_width wa, fi $ wa_height wa)
|
|
||||||
|
|
||||||
|
|
||||||
performWindowSwitching :: Window -> X ()
|
performWindowSwitching :: Window -> X ()
|
||||||
performWindowSwitching win = do
|
performWindowSwitching win = do
|
||||||
@ -85,7 +83,7 @@ performWindowSwitching win = do
|
|||||||
let allWindows = W.index ws
|
let allWindows = W.index ws
|
||||||
when ((win `elem` allWindows) && (selWin `elem` allWindows)) $ do
|
when ((win `elem` allWindows) && (selWin `elem` allWindows)) $ do
|
||||||
let allWindowsSwitched = map (switchEntries win selWin) allWindows
|
let allWindowsSwitched = map (switchEntries win selWin) allWindows
|
||||||
let (ls, t : rs) = break (== win) allWindowsSwitched
|
(ls, t : rs) <- pure $ break (== win) allWindowsSwitched
|
||||||
let newStack = W.Stack t (reverse ls) rs
|
let newStack = W.Stack t (reverse ls) rs
|
||||||
windows $ W.modify' $ const newStack
|
windows $ W.modify' $ const newStack
|
||||||
where
|
where
|
||||||
|
122
XMonad/Actions/ToggleFullFloat.hs
Normal file
122
XMonad/Actions/ToggleFullFloat.hs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
-- |
|
||||||
|
-- Module : XMonad.Actions.ToggleFullFloat
|
||||||
|
-- Description : Fullscreen (float) a window while remembering its original state.
|
||||||
|
-- Copyright : (c) 2022 Tomáš Janoušek <tomi@nomi.cz>
|
||||||
|
-- License : BSD3
|
||||||
|
-- Maintainer : Tomáš Janoušek <tomi@nomi.cz>
|
||||||
|
--
|
||||||
|
module XMonad.Actions.ToggleFullFloat (
|
||||||
|
-- * Usage
|
||||||
|
-- $usage
|
||||||
|
toggleFullFloatEwmhFullscreen,
|
||||||
|
toggleFullFloat,
|
||||||
|
fullFloat,
|
||||||
|
unFullFloat,
|
||||||
|
gcToggleFullFloat,
|
||||||
|
) where
|
||||||
|
|
||||||
|
import qualified Data.Map.Strict as M
|
||||||
|
|
||||||
|
import XMonad
|
||||||
|
import XMonad.Prelude
|
||||||
|
import XMonad.Hooks.EwmhDesktops (setEwmhFullscreenHooks)
|
||||||
|
import XMonad.Hooks.ManageHelpers
|
||||||
|
import qualified XMonad.StackSet as W
|
||||||
|
import qualified XMonad.Util.ExtensibleState as XS
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- $usage
|
||||||
|
--
|
||||||
|
-- The main use-case is to make 'ewmhFullscreen' (re)store the size and
|
||||||
|
-- position of floating windows instead of just unconditionally sinking them
|
||||||
|
-- into the floating layer. To enable this, you'll need this in your
|
||||||
|
-- @xmonad.hs@:
|
||||||
|
--
|
||||||
|
-- > import XMonad
|
||||||
|
-- > import XMonad.Actions.ToggleFullFloat
|
||||||
|
-- > import XMonad.Hooks.EwmhDesktops
|
||||||
|
-- >
|
||||||
|
-- > main = xmonad $ … . toggleFullFloatEwmhFullscreen . ewmhFullscreen . ewmh . … $ def{…}
|
||||||
|
--
|
||||||
|
-- Additionally, this "smart" fullscreening can be bound to a key and invoked
|
||||||
|
-- manually whenever one needs a larger window temporarily:
|
||||||
|
--
|
||||||
|
-- > , ((modMask .|. shiftMask, xK_t), withFocused toggleFullFloat)
|
||||||
|
|
||||||
|
newtype ToggleFullFloat = ToggleFullFloat{ fromToggleFullFloat :: M.Map Window (Maybe W.RationalRect) }
|
||||||
|
deriving (Show, Read)
|
||||||
|
|
||||||
|
instance ExtensionClass ToggleFullFloat where
|
||||||
|
extensionType = PersistentExtension
|
||||||
|
initialValue = ToggleFullFloat mempty
|
||||||
|
|
||||||
|
-- | Full-float a window, remembering its state (tiled/floating and
|
||||||
|
-- position/size).
|
||||||
|
fullFloat :: Window -> X ()
|
||||||
|
fullFloat = windows . appEndo <=< runQuery doFullFloatSave
|
||||||
|
|
||||||
|
-- | Restore window to its remembered state.
|
||||||
|
unFullFloat :: Window -> X ()
|
||||||
|
unFullFloat = windows . appEndo <=< runQuery doFullFloatRestore
|
||||||
|
|
||||||
|
-- | Full-float a window, if it's not already full-floating. Otherwise,
|
||||||
|
-- restore its original state.
|
||||||
|
toggleFullFloat :: Window -> X ()
|
||||||
|
toggleFullFloat w = ifM (isFullFloat w) (unFullFloat w) (fullFloat w)
|
||||||
|
|
||||||
|
isFullFloat :: Window -> X Bool
|
||||||
|
isFullFloat w = gets $ (Just fullRect ==) . M.lookup w . W.floating . windowset
|
||||||
|
where
|
||||||
|
fullRect = W.RationalRect 0 0 1 1
|
||||||
|
|
||||||
|
doFullFloatSave :: ManageHook
|
||||||
|
doFullFloatSave = do
|
||||||
|
w <- ask
|
||||||
|
liftX $ do
|
||||||
|
f <- gets $ M.lookup w . W.floating . windowset
|
||||||
|
-- @M.insertWith const@ = don't overwrite stored original state
|
||||||
|
XS.modify' $ ToggleFullFloat . M.insertWith const w f . fromToggleFullFloat
|
||||||
|
doFullFloat
|
||||||
|
|
||||||
|
doFullFloatRestore :: ManageHook
|
||||||
|
doFullFloatRestore = do
|
||||||
|
w <- ask
|
||||||
|
mf <- liftX $ do
|
||||||
|
mf <- XS.gets $ M.lookup w . fromToggleFullFloat
|
||||||
|
XS.modify' $ ToggleFullFloat . M.delete w . fromToggleFullFloat
|
||||||
|
pure mf
|
||||||
|
doF $ case mf of
|
||||||
|
Just (Just f) -> W.float w f -- was floating before
|
||||||
|
Just Nothing -> W.sink w -- was tiled before
|
||||||
|
Nothing -> W.sink w -- fallback when not found in ToggleFullFloat
|
||||||
|
|
||||||
|
-- | Install ToggleFullFloat garbage collection hooks.
|
||||||
|
--
|
||||||
|
-- Note: This is included in 'toggleFullFloatEwmhFullscreen', only needed if
|
||||||
|
-- using the 'toggleFullFloat' separately from the EWMH hook.
|
||||||
|
gcToggleFullFloat :: XConfig a -> XConfig a
|
||||||
|
gcToggleFullFloat c = c { startupHook = startupHook c <> gcToggleFullFloatStartupHook
|
||||||
|
, handleEventHook = handleEventHook c <> gcToggleFullFloatEventHook }
|
||||||
|
|
||||||
|
-- | ToggleFullFloat garbage collection: drop windows when they're destroyed.
|
||||||
|
gcToggleFullFloatEventHook :: Event -> X All
|
||||||
|
gcToggleFullFloatEventHook DestroyWindowEvent{ev_window = w} = do
|
||||||
|
XS.modify' $ ToggleFullFloat . M.delete w . fromToggleFullFloat
|
||||||
|
mempty
|
||||||
|
gcToggleFullFloatEventHook _ = mempty
|
||||||
|
|
||||||
|
-- | ToggleFullFloat garbage collection: restrict to existing windows at
|
||||||
|
-- startup.
|
||||||
|
gcToggleFullFloatStartupHook :: X ()
|
||||||
|
gcToggleFullFloatStartupHook = withWindowSet $ \ws ->
|
||||||
|
XS.modify' $ ToggleFullFloat . M.filterWithKey (\w _ -> w `W.member` ws) . fromToggleFullFloat
|
||||||
|
|
||||||
|
-- | Hook this module into 'XMonad.Hooks.EwmhDesktops.ewmhFullscreen'. This
|
||||||
|
-- makes windows restore their original state (size and position if floating)
|
||||||
|
-- instead of unconditionally sinking into the tiling layer.
|
||||||
|
--
|
||||||
|
-- ('gcToggleFullFloat' is included here.)
|
||||||
|
toggleFullFloatEwmhFullscreen :: XConfig a -> XConfig a
|
||||||
|
toggleFullFloatEwmhFullscreen =
|
||||||
|
setEwmhFullscreenHooks doFullFloatSave doFullFloatRestore .
|
||||||
|
gcToggleFullFloat
|
@ -103,9 +103,12 @@ import XMonad.Hooks.WorkspaceHistory
|
|||||||
-- display your topics in an historical way using a custom `pprWindowSet'
|
-- display your topics in an historical way using a custom `pprWindowSet'
|
||||||
-- function. You can also easily switch to recent topics using this history
|
-- function. You can also easily switch to recent topics using this history
|
||||||
-- of last focused topics.
|
-- of last focused topics.
|
||||||
|
--
|
||||||
|
-- A blog post highlighting some features of this module can be found
|
||||||
|
-- <https://tony-zorman.com/posts/topic-space/2022-09-11-topic-spaces.html here>.
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import qualified Data.Map.Strict as M
|
-- > import qualified Data.Map.Strict as M
|
||||||
-- > import qualified XMonad.StackSet as W
|
-- > import qualified XMonad.StackSet as W
|
||||||
|
@ -79,8 +79,9 @@ import XMonad.Hooks.WorkspaceHistory
|
|||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
|
||||||
#ifdef XFT
|
#ifdef XFT
|
||||||
import Graphics.X11.Xft
|
import qualified Data.List.NonEmpty as NE
|
||||||
import Graphics.X11.Xrender
|
import Graphics.X11.Xrender
|
||||||
|
import Graphics.X11.Xft
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
@ -532,11 +533,11 @@ navigate = gets tss_display >>= \d -> join . liftIO . allocaXEvent $ \e -> do
|
|||||||
ev <- getEvent e
|
ev <- getEvent e
|
||||||
|
|
||||||
if | ev_event_type ev == keyPress -> do
|
if | ev_event_type ev == keyPress -> do
|
||||||
(ks, _) <- lookupString $ asKeyEvent e
|
ks <- keycodeToKeysym d (ev_keycode ev) 0
|
||||||
return $ do
|
return $ do
|
||||||
mask <- liftX $ cleanMask (ev_state ev)
|
mask <- liftX $ cleanKeyMask <*> pure (ev_state ev)
|
||||||
f <- asks ts_navigate
|
f <- asks ts_navigate
|
||||||
fromMaybe navigate $ M.lookup (mask, fromMaybe xK_VoidSymbol ks) f
|
fromMaybe navigate $ M.lookup (mask, ks) f
|
||||||
| ev_event_type ev == buttonPress -> do
|
| ev_event_type ev == buttonPress -> do
|
||||||
-- See XMonad.Prompt Note [Allow ButtonEvents]
|
-- See XMonad.Prompt Note [Allow ButtonEvents]
|
||||||
allowEvents d replayPointer currentTime
|
allowEvents d replayPointer currentTime
|
||||||
@ -648,10 +649,14 @@ drawStringXMF display window visual colormap gc font col x y text = case font of
|
|||||||
setForeground display gc col
|
setForeground display gc col
|
||||||
wcDrawImageString display window fnt gc x y text
|
wcDrawImageString display window fnt gc x y text
|
||||||
#ifdef XFT
|
#ifdef XFT
|
||||||
Xft fnt -> do
|
Xft fnts -> do
|
||||||
withXftDraw display window visual colormap $
|
withXftDraw display window visual colormap $
|
||||||
\ft_draw -> withXftColorValue display visual colormap (fromARGB col) $
|
\ft_draw -> withXftColorValue display visual colormap (fromARGB col) $
|
||||||
\ft_color -> xftDrawString ft_draw ft_color fnt x y text
|
#if MIN_VERSION_X11_xft(0, 3, 4)
|
||||||
|
\ft_color -> xftDrawStringFallback ft_draw ft_color (NE.toList fnts) (fi x) (fi y) text
|
||||||
|
#else
|
||||||
|
\ft_color -> xftDrawString ft_draw ft_color (NE.head fnts) x y text
|
||||||
|
#endif
|
||||||
|
|
||||||
-- | Convert 'Pixel' to 'XRenderColor'
|
-- | Convert 'Pixel' to 'XRenderColor'
|
||||||
--
|
--
|
||||||
|
169
XMonad/Actions/UpKeys.hs
Normal file
169
XMonad/Actions/UpKeys.hs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
{-# LANGUAGE BlockArguments #-}
|
||||||
|
{-# LANGUAGE InstanceSigs #-}
|
||||||
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
{- |
|
||||||
|
Module : XMonad.Actions.UpKeys
|
||||||
|
Description : Bind an action to the release of a key
|
||||||
|
Copyright : (c) Tony Zorman, 2024
|
||||||
|
License : BSD-3
|
||||||
|
Maintainer : Tony Zorman <soliditsallgood@mailbox.org>
|
||||||
|
|
||||||
|
A combinator for binding an action to the release of a key. This can be
|
||||||
|
useful for hold-type buttons, where the press of a key engages some
|
||||||
|
functionality, and its release… releases it again.
|
||||||
|
-}
|
||||||
|
module XMonad.Actions.UpKeys
|
||||||
|
( -- * Usage
|
||||||
|
-- $usage
|
||||||
|
useUpKeys,
|
||||||
|
UpKeysConfig (..),
|
||||||
|
ezUpKeys,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Data.Map.Strict (Map)
|
||||||
|
import qualified Data.Map.Strict as Map
|
||||||
|
import XMonad
|
||||||
|
import XMonad.Prelude
|
||||||
|
import XMonad.Util.EZConfig (mkKeymap)
|
||||||
|
import qualified XMonad.Util.ExtensibleConf as XC
|
||||||
|
|
||||||
|
{- $usage
|
||||||
|
You can use this module with the following in your @xmonad.hs@:
|
||||||
|
|
||||||
|
> import XMonad.Actions.UpKeys
|
||||||
|
|
||||||
|
Next, define the keys and actions you want to have happen on the release
|
||||||
|
of a key:
|
||||||
|
|
||||||
|
> myUpKeys = ezUpKeys $
|
||||||
|
> [ ("M-z", myAction)
|
||||||
|
> , ("M-a", myAction2)
|
||||||
|
> ]
|
||||||
|
|
||||||
|
All that's left is to plug this definition into the 'useUpKeys'
|
||||||
|
combinator that this module provides:
|
||||||
|
|
||||||
|
> main :: IO ()
|
||||||
|
> main = xmonad
|
||||||
|
> . useUpKeys (def{ grabKeys = True, upKeys = myUpKeys })
|
||||||
|
> $ myConfig
|
||||||
|
|
||||||
|
Note the presence of @'grabKeys' = True@; this is for situations where
|
||||||
|
you don't have any of these keys bound to do something upon pressing
|
||||||
|
them; i.e., you use them solely for their release actions. If you want
|
||||||
|
something to happen in both cases, remove that part (@'grabKeys' =
|
||||||
|
False@ is the default) and bind the keys to actions as you normally
|
||||||
|
would.
|
||||||
|
|
||||||
|
==== __Examples__
|
||||||
|
|
||||||
|
As an extended example, consider the case where you want all of your
|
||||||
|
docks (e.g., status bar) to "pop up" when you press the super key, and
|
||||||
|
then vanish again once that keys is released.
|
||||||
|
|
||||||
|
Since docks are not generally part of XMonad's window-set—otherwise, we
|
||||||
|
would have to manage them—we first need a way to access and manipulate
|
||||||
|
all docks.
|
||||||
|
|
||||||
|
> onAllDocks :: (Display -> Window -> IO ()) -> X ()
|
||||||
|
> onAllDocks act = withDisplay \dpy -> do
|
||||||
|
> rootw <- asks theRoot
|
||||||
|
> (_, _, wins) <- io $ queryTree dpy rootw
|
||||||
|
> traverse_ (io . act dpy) =<< filterM (runQuery checkDock) wins
|
||||||
|
|
||||||
|
This is also the place where one could filter for just status bar,
|
||||||
|
trayer, and so on.
|
||||||
|
|
||||||
|
Now we have to decide what kinds of keys we want to watch out for. Since
|
||||||
|
you most likely use left super as your modifier key, this is a little
|
||||||
|
bit more complicated than for other keys, as you will most likely see
|
||||||
|
the key both as a @KeyMask@, as well as a @KeySym@. One could think a
|
||||||
|
bit and probably come up with an elegant solution for this—or one could
|
||||||
|
grab all possible key combinations by brute-force!
|
||||||
|
|
||||||
|
> dockKeys :: X () -> [((KeyMask, KeySym), X ())]
|
||||||
|
> dockKeys act = map (actKey . foldr1 (.|.)) . combinations $ keyMasks
|
||||||
|
> where
|
||||||
|
> actKey :: KeyMask -> ((KeyMask, KeySym), X ())
|
||||||
|
> actKey mask = ((mask, xK_Super_L), act)
|
||||||
|
>
|
||||||
|
> keyMasks :: [KeyMask]
|
||||||
|
> keyMasks = [ noModMask, shiftMask, lockMask, controlMask, mod1Mask, mod2Mask, mod3Mask, mod4Mask, mod5Mask ]
|
||||||
|
>
|
||||||
|
> -- Return all combinations of a sequence of values.
|
||||||
|
> combinations :: [a] -> [[a]]
|
||||||
|
> combinations xs = concat [combs i xs | i <- [1 .. length xs]]
|
||||||
|
> where
|
||||||
|
> combs 0 _ = [[]]
|
||||||
|
> combs _ [] = []
|
||||||
|
> combs n (x:xs) = map (x:) (combs (n-1) xs) <> combs n xs
|
||||||
|
|
||||||
|
Given some action, like lowering or raising the window, we generate all
|
||||||
|
possible combinations of modifiers that may be pressed with the super
|
||||||
|
key. This is a good time to say that this is just for demonstrative
|
||||||
|
purposes, btw—please don't actually do this.
|
||||||
|
|
||||||
|
All that's left is to plug everything into the machinery of this module,
|
||||||
|
and we're done!
|
||||||
|
|
||||||
|
> import qualified Data.Map.Strict as Map
|
||||||
|
>
|
||||||
|
> main :: IO ()
|
||||||
|
> main = xmonad
|
||||||
|
> . … -- other combinators
|
||||||
|
> . useUpKeys (def { upKeys = Map.fromList $ dockKeys (onAllDocks lowerWindow) })
|
||||||
|
> $ myConfig `additionalKeys` dockKeys (onAllDocks raiseWindow)
|
||||||
|
>
|
||||||
|
> myConfig = …
|
||||||
|
-}
|
||||||
|
|
||||||
|
data UpKeysConfig = UpKeysConfig
|
||||||
|
{ -- | Whether to grab all keys that are not already grabbed.
|
||||||
|
grabKeys :: !Bool
|
||||||
|
-- | The keys themselves.
|
||||||
|
, upKeys :: !(Map (KeyMask, KeySym) (X ()))
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | The default 'UpKeysConfig'; keys are not grabbed, and no upkeys are
|
||||||
|
-- specified.
|
||||||
|
instance Default UpKeysConfig where
|
||||||
|
def :: UpKeysConfig
|
||||||
|
def = UpKeysConfig { grabKeys = False, upKeys = mempty }
|
||||||
|
|
||||||
|
instance Semigroup UpKeysConfig where
|
||||||
|
(<>) :: UpKeysConfig -> UpKeysConfig -> UpKeysConfig
|
||||||
|
UpKeysConfig g u <> UpKeysConfig g' u' = UpKeysConfig (g && g') (u <> u')
|
||||||
|
|
||||||
|
-- | Bind actions to keys upon their release.
|
||||||
|
useUpKeys :: UpKeysConfig -> (XConfig l -> XConfig l)
|
||||||
|
useUpKeys upKeysConf = flip XC.once upKeysConf \conf -> conf
|
||||||
|
{ handleEventHook = handleEventHook conf <> (\e -> handleKeyUp e $> All True)
|
||||||
|
, startupHook = startupHook conf <> when (grabKeys upKeysConf) grabUpKeys
|
||||||
|
}
|
||||||
|
where
|
||||||
|
grabUpKeys :: X ()
|
||||||
|
grabUpKeys = do
|
||||||
|
XConf{ display = dpy, theRoot = rootw } <- ask
|
||||||
|
realKeys <- maybe mempty upKeys <$> XC.ask @X @UpKeysConfig
|
||||||
|
let grab :: (KeyMask, KeyCode) -> X ()
|
||||||
|
grab (km, kc) = io $ grabKey dpy kc km rootw True grabModeAsync grabModeAsync
|
||||||
|
traverse_ grab =<< mkGrabs (Map.keys realKeys)
|
||||||
|
|
||||||
|
-- | Parse the given EZConfig-style keys into the internal keymap
|
||||||
|
-- representation.
|
||||||
|
--
|
||||||
|
-- This is just 'mkKeymap' with a better name.
|
||||||
|
ezUpKeys :: XConfig l -> [(String, X ())] -> Map (KeyMask, KeySym) (X ())
|
||||||
|
ezUpKeys = mkKeymap
|
||||||
|
|
||||||
|
-- | A handler for key-up events.
|
||||||
|
handleKeyUp :: Event -> X ()
|
||||||
|
handleKeyUp KeyEvent{ ev_event_type, ev_state, ev_keycode }
|
||||||
|
| ev_event_type == keyRelease = withDisplay \dpy -> do
|
||||||
|
s <- io $ keycodeToKeysym dpy ev_keycode 0
|
||||||
|
cln <- cleanMask ev_state
|
||||||
|
ks <- maybe mempty upKeys <$> XC.ask @X @UpKeysConfig
|
||||||
|
userCodeDef () $ whenJust (ks Map.!? (cln, s)) id
|
||||||
|
handleKeyUp _ = pure ()
|
@ -27,7 +27,7 @@ import qualified XMonad.StackSet as W
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- To make the focus update on mouse movement within an unfocused window, add the
|
-- To make the focus update on mouse movement within an unfocused window, add the
|
||||||
-- following to your @~\/.xmonad\/xmonad.hs@:
|
-- following to your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.UpdateFocus
|
-- > import XMonad.Actions.UpdateFocus
|
||||||
-- > xmonad $ def {
|
-- > xmonad $ def {
|
||||||
|
@ -28,11 +28,10 @@ import XMonad
|
|||||||
import XMonad.Prelude
|
import XMonad.Prelude
|
||||||
import XMonad.StackSet (member, peek, screenDetail, current)
|
import XMonad.StackSet (member, peek, screenDetail, current)
|
||||||
|
|
||||||
import Control.Exception (SomeException, try)
|
|
||||||
import Control.Arrow ((&&&), (***))
|
import Control.Arrow ((&&&), (***))
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad
|
-- > import XMonad
|
||||||
-- > import XMonad.Actions.UpdatePointer
|
-- > import XMonad.Actions.UpdatePointer
|
||||||
@ -73,10 +72,9 @@ updatePointer refPos ratio = do
|
|||||||
let defaultRect = screenRect $ screenDetail $ current ws
|
let defaultRect = screenRect $ screenDetail $ current ws
|
||||||
rect <- case peek ws of
|
rect <- case peek ws of
|
||||||
Nothing -> return defaultRect
|
Nothing -> return defaultRect
|
||||||
Just w -> do tryAttributes <- io $ try $ getWindowAttributes dpy w
|
Just w -> maybe defaultRect windowAttributesToRectangle
|
||||||
return $ case tryAttributes of
|
<$> safeGetWindowAttributes w
|
||||||
Left (_ :: SomeException) -> defaultRect
|
|
||||||
Right attributes -> windowAttributesToRectangle attributes
|
|
||||||
root <- asks theRoot
|
root <- asks theRoot
|
||||||
mouseIsMoving <- asks mouseFocused
|
mouseIsMoving <- asks mouseFocused
|
||||||
(_sameRoot,_,currentWindow,rootX,rootY,_,_,_) <- io $ queryPointer dpy root
|
(_sameRoot,_,currentWindow,rootX,rootY,_,_,_) <- io $ queryPointer dpy root
|
||||||
|
@ -28,7 +28,7 @@ import XMonad
|
|||||||
import XMonad.StackSet as W
|
import XMonad.StackSet as W
|
||||||
|
|
||||||
{- $usage
|
{- $usage
|
||||||
You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
You can use this module with the following in your @xmonad.hs@:
|
||||||
|
|
||||||
> import XMonad.Actions.Warp
|
> import XMonad.Actions.Warp
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ Note that warping to a particular screen may change the focus.
|
|||||||
-}
|
-}
|
||||||
|
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
|
|
||||||
data Corner = UpperLeft | UpperRight | LowerLeft | LowerRight
|
data Corner = UpperLeft | UpperRight | LowerLeft | LowerRight
|
||||||
@ -91,11 +91,9 @@ warp w x y = withDisplay $ \d -> io $ warpPointer d none w 0 0 0 0 x y
|
|||||||
-- | Warp the pointer to a given position relative to the currently
|
-- | Warp the pointer to a given position relative to the currently
|
||||||
-- focused window. Top left = (0,0), bottom right = (1,1).
|
-- focused window. Top left = (0,0), bottom right = (1,1).
|
||||||
warpToWindow :: Rational -> Rational -> X ()
|
warpToWindow :: Rational -> Rational -> X ()
|
||||||
warpToWindow h v =
|
warpToWindow h v = withDisplay $ \d -> withFocused $ \w ->
|
||||||
withDisplay $ \d ->
|
withWindowAttributes d w $ \wa ->
|
||||||
withFocused $ \w -> do
|
warp w (fraction h (wa_width wa)) (fraction v (wa_height wa))
|
||||||
wa <- io $ getWindowAttributes d w
|
|
||||||
warp w (fraction h (wa_width wa)) (fraction v (wa_height wa))
|
|
||||||
|
|
||||||
-- | Warp the pointer to the given position (top left = (0,0), bottom
|
-- | Warp the pointer to the given position (top left = (0,0), bottom
|
||||||
-- right = (1,1)) on the given screen.
|
-- right = (1,1)) on the given screen.
|
||||||
|
@ -22,6 +22,7 @@ module XMonad.Actions.WindowBringer (
|
|||||||
WindowBringerConfig(..),
|
WindowBringerConfig(..),
|
||||||
gotoMenu, gotoMenuConfig, gotoMenu', gotoMenuArgs, gotoMenuArgs',
|
gotoMenu, gotoMenuConfig, gotoMenu', gotoMenuArgs, gotoMenuArgs',
|
||||||
bringMenu, bringMenuConfig, bringMenu', bringMenuArgs, bringMenuArgs',
|
bringMenu, bringMenuConfig, bringMenu', bringMenuArgs, bringMenuArgs',
|
||||||
|
copyMenu, copyMenuConfig, copyMenu', copyMenuArgs, copyMenuArgs',
|
||||||
windowMap, windowAppMap, windowMap', bringWindow, actionMenu
|
windowMap, windowAppMap, windowMap', bringWindow, actionMenu
|
||||||
) where
|
) where
|
||||||
|
|
||||||
@ -33,10 +34,11 @@ import XMonad
|
|||||||
import qualified XMonad as X
|
import qualified XMonad as X
|
||||||
import XMonad.Util.Dmenu (menuMapArgs)
|
import XMonad.Util.Dmenu (menuMapArgs)
|
||||||
import XMonad.Util.NamedWindows (getName, getNameWMClass)
|
import XMonad.Util.NamedWindows (getName, getNameWMClass)
|
||||||
|
import XMonad.Actions.CopyWindow (copyWindow)
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- Import the module into your @~\/.xmonad\/xmonad.hs@:
|
-- Import the module into your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.WindowBringer
|
-- > import XMonad.Actions.WindowBringer
|
||||||
--
|
--
|
||||||
@ -44,9 +46,10 @@ import XMonad.Util.NamedWindows (getName, getNameWMClass)
|
|||||||
--
|
--
|
||||||
-- > , ((modm .|. shiftMask, xK_g ), gotoMenu)
|
-- > , ((modm .|. shiftMask, xK_g ), gotoMenu)
|
||||||
-- > , ((modm .|. shiftMask, xK_b ), bringMenu)
|
-- > , ((modm .|. shiftMask, xK_b ), bringMenu)
|
||||||
|
-- > , ((modm .|. shiftMask, xK_y ), copyMenu)
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
data WindowBringerConfig = WindowBringerConfig
|
data WindowBringerConfig = WindowBringerConfig
|
||||||
{ menuCommand :: String -- ^ The shell command that will handle window selection
|
{ menuCommand :: String -- ^ The shell command that will handle window selection
|
||||||
@ -90,6 +93,37 @@ gotoMenu' cmd = gotoMenuConfig def { menuArgs = [], menuCommand = cmd }
|
|||||||
gotoMenuArgs' :: String -> [String] -> X ()
|
gotoMenuArgs' :: String -> [String] -> X ()
|
||||||
gotoMenuArgs' cmd args = gotoMenuConfig def { menuCommand = cmd, menuArgs = args }
|
gotoMenuArgs' cmd args = gotoMenuConfig def { menuCommand = cmd, menuArgs = args }
|
||||||
|
|
||||||
|
-- | Pops open a dmenu with window titles. Choose one, and it will be copied into your current workspace.
|
||||||
|
copyMenu :: X ()
|
||||||
|
copyMenu = copyMenuArgs def
|
||||||
|
|
||||||
|
-- | Pops open a dmenu with window titles. Choose one, and it will be
|
||||||
|
-- copied into your current workspace. This version
|
||||||
|
-- accepts a configuration object.
|
||||||
|
copyMenuConfig :: WindowBringerConfig -> X ()
|
||||||
|
copyMenuConfig wbConfig = actionMenu wbConfig copyBringWindow
|
||||||
|
|
||||||
|
-- | Pops open a dmenu with window titles. Choose one, and it will be
|
||||||
|
-- copied into your current workspace. This version
|
||||||
|
-- takes a list of arguments to pass to dmenu.
|
||||||
|
copyMenuArgs :: [String] -> X ()
|
||||||
|
copyMenuArgs args = copyMenuConfig def { menuArgs = args }
|
||||||
|
|
||||||
|
-- | Pops open an application with window titles given over stdin. Choose one,
|
||||||
|
-- and it will be copied into your current workspace.
|
||||||
|
copyMenu' :: String -> X ()
|
||||||
|
copyMenu' cmd = copyMenuConfig def { menuArgs = [], menuCommand = cmd }
|
||||||
|
|
||||||
|
-- | Pops open an application with window titles given over stdin. Choose one,
|
||||||
|
-- and it will be copied into your current
|
||||||
|
-- workspace. This version allows arguments to the chooser to be specified.
|
||||||
|
copyMenuArgs' :: String -> [String] -> X ()
|
||||||
|
copyMenuArgs' cmd args = copyMenuConfig def { menuArgs = args, menuCommand = cmd }
|
||||||
|
|
||||||
|
-- | Brings a copy of the specified window into the current workspace.
|
||||||
|
copyBringWindow :: Window -> X.WindowSet -> X.WindowSet
|
||||||
|
copyBringWindow w ws = copyWindow w (W.currentTag ws) ws
|
||||||
|
|
||||||
-- | Pops open a dmenu with window titles. Choose one, and it will be
|
-- | Pops open a dmenu with window titles. Choose one, and it will be
|
||||||
-- dragged, kicking and screaming, into your current workspace.
|
-- dragged, kicking and screaming, into your current workspace.
|
||||||
bringMenu :: X ()
|
bringMenu :: X ()
|
||||||
@ -159,7 +193,7 @@ decorateName ws w = do
|
|||||||
return $ name ++ " [" ++ W.tag ws ++ "]"
|
return $ name ++ " [" ++ W.tag ws ++ "]"
|
||||||
|
|
||||||
-- | Returns the window name as will be listed in dmenu. This will
|
-- | Returns the window name as will be listed in dmenu. This will
|
||||||
-- return the executable name of the window along with it's workspace
|
-- return the executable name of the window along with its workspace
|
||||||
-- ID.
|
-- ID.
|
||||||
decorateAppName :: X.WindowSpace -> Window -> X String
|
decorateAppName :: X.WindowSpace -> Window -> X String
|
||||||
decorateAppName ws w = do
|
decorateAppName ws w = do
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
|
||||||
{- |
|
{- |
|
||||||
Module : XMonad.Actions.WindowGo
|
Module : XMonad.Actions.WindowGo
|
||||||
Description : Operations for raising (traveling to) windows.
|
Description : Operations for raising (traveling to) windows.
|
||||||
@ -46,9 +48,11 @@ import XMonad.Operations (windows)
|
|||||||
import XMonad.Prompt.Shell (getBrowser, getEditor)
|
import XMonad.Prompt.Shell (getBrowser, getEditor)
|
||||||
import qualified XMonad.StackSet as W (peek, swapMaster, focusWindow, workspaces, StackSet, Workspace, integrate', tag, stack)
|
import qualified XMonad.StackSet as W (peek, swapMaster, focusWindow, workspaces, StackSet, Workspace, integrate', tag, stack)
|
||||||
import XMonad.Util.Run (safeSpawnProg)
|
import XMonad.Util.Run (safeSpawnProg)
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
|
|
||||||
{- $usage
|
{- $usage
|
||||||
|
|
||||||
Import the module into your @~\/.xmonad\/xmonad.hs@:
|
Import the module into your @xmonad.hs@:
|
||||||
|
|
||||||
> import XMonad.Actions.WindowGo
|
> import XMonad.Actions.WindowGo
|
||||||
|
|
||||||
@ -64,7 +68,8 @@ appropriate one, or cover your bases by using instead something like:
|
|||||||
> (className =? "Firefox" <||> className =? "Firefox-bin")
|
> (className =? "Firefox" <||> className =? "Firefox-bin")
|
||||||
|
|
||||||
For detailed instructions on editing your key bindings, see
|
For detailed instructions on editing your key bindings, see
|
||||||
"XMonad.Doc.Extending#Editing_key_bindings". -}
|
<https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
-}
|
||||||
|
|
||||||
-- | Get the list of workspaces sorted by their tag
|
-- | Get the list of workspaces sorted by their tag
|
||||||
workspacesSorted :: Ord i => W.StackSet i l a s sd -> [W.Workspace i l a]
|
workspacesSorted :: Ord i => W.StackSet i l a s sd -> [W.Workspace i l a]
|
||||||
@ -87,7 +92,10 @@ ifWindows qry f el = withWindowSet $ \wins -> do
|
|||||||
-- | The same as ifWindows, but applies a ManageHook to the first match
|
-- | The same as ifWindows, but applies a ManageHook to the first match
|
||||||
-- instead and discards the other matches
|
-- instead and discards the other matches
|
||||||
ifWindow :: Query Bool -> ManageHook -> X () -> X ()
|
ifWindow :: Query Bool -> ManageHook -> X () -> X ()
|
||||||
ifWindow qry mh = ifWindows qry (windows . appEndo <=< runQuery mh . head)
|
ifWindow qry mh = ifWindows qry (windows . appEndo <=< runQuery mh . NE.head . notEmpty)
|
||||||
|
-- ifWindows guarantees that the list given to the function is
|
||||||
|
-- non-empty. This should really use Data.List.NonEmpty, but, alas,
|
||||||
|
-- that would be a breaking change.
|
||||||
|
|
||||||
{- | 'action' is an executable to be run via 'safeSpawnProg' (of "XMonad.Util.Run") if the Window cannot be found.
|
{- | 'action' is an executable to be run via 'safeSpawnProg' (of "XMonad.Util.Run") if the Window cannot be found.
|
||||||
Presumably this executable is the same one that you were looking for.
|
Presumably this executable is the same one that you were looking for.
|
||||||
@ -158,9 +166,12 @@ raiseNextMaybeCustomFocus :: (Window -> WindowSet -> WindowSet) -> X() -> Query
|
|||||||
raiseNextMaybeCustomFocus focusFn f qry = flip (ifWindows qry) f $ \ws -> do
|
raiseNextMaybeCustomFocus focusFn f qry = flip (ifWindows qry) f $ \ws -> do
|
||||||
foc <- withWindowSet $ return . W.peek
|
foc <- withWindowSet $ return . W.peek
|
||||||
case foc of
|
case foc of
|
||||||
Just w | w `elem` ws -> let (_:y:_) = dropWhile (/=w) $ cycle ws -- cannot fail to match
|
Just w | w `elem` ws ->
|
||||||
in windows $ focusFn y
|
let (notEmpty -> _ :| (notEmpty -> y :| _)) = dropWhile (/=w) $ cycle ws
|
||||||
_ -> windows . focusFn . head $ ws
|
-- cannot fail to match
|
||||||
|
in windows $ focusFn y
|
||||||
|
_ -> windows . focusFn . NE.head . notEmpty $ ws
|
||||||
|
-- ws is non-empty by ifWindows's definition.
|
||||||
|
|
||||||
-- | Given a function which gets us a String, we try to raise a window with that classname,
|
-- | Given a function which gets us a String, we try to raise a window with that classname,
|
||||||
-- or we then interpret that String as a executable name.
|
-- or we then interpret that String as a executable name.
|
||||||
|
@ -34,7 +34,7 @@ import XMonad.Prelude (fi)
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.WindowMenu
|
-- > import XMonad.Actions.WindowMenu
|
||||||
--
|
--
|
||||||
@ -51,9 +51,9 @@ colorizer _ isFg = do
|
|||||||
else (nBC, fBC)
|
else (nBC, fBC)
|
||||||
|
|
||||||
windowMenu :: X ()
|
windowMenu :: X ()
|
||||||
windowMenu = withFocused $ \w -> do
|
windowMenu = withFocused $ \w -> withDisplay $ \d -> withWindowAttributes d w $ \wa -> do
|
||||||
tags <- asks (workspaces . config)
|
tags <- asks (workspaces . config)
|
||||||
Rectangle x y wh ht <- getSize w
|
let Rectangle x y wh ht = getSize wa
|
||||||
Rectangle sx sy swh sht <- gets $ screenRect . W.screenDetail . W.current . windowset
|
Rectangle sx sy swh sht <- gets $ screenRect . W.screenDetail . W.current . windowset
|
||||||
let originFractX = (fi x - fi sx + fi wh / 2) / fi swh
|
let originFractX = (fi x - fi sx + fi wh / 2) / fi swh
|
||||||
originFractY = (fi y - fi sy + fi ht / 2) / fi sht
|
originFractY = (fi y - fi sy + fi ht / 2) / fi sht
|
||||||
@ -69,12 +69,10 @@ windowMenu = withFocused $ \w -> do
|
|||||||
| tag <- tags ]
|
| tag <- tags ]
|
||||||
runSelectedAction gsConfig actions
|
runSelectedAction gsConfig actions
|
||||||
|
|
||||||
getSize :: Window -> X Rectangle
|
getSize :: WindowAttributes -> Rectangle
|
||||||
getSize w = do
|
getSize wa =
|
||||||
d <- asks display
|
|
||||||
wa <- io $ getWindowAttributes d w
|
|
||||||
let x = fi $ wa_x wa
|
let x = fi $ wa_x wa
|
||||||
y = fi $ wa_y wa
|
y = fi $ wa_y wa
|
||||||
wh = fi $ wa_width wa
|
wh = fi $ wa_width wa
|
||||||
ht = fi $ wa_height wa
|
ht = fi $ wa_height wa
|
||||||
return (Rectangle x y wh ht)
|
in Rectangle x y wh ht
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
{-# LANGUAGE TupleSections #-} -- I didn't want this, it's hlint's "suggestion" and it's apparently non-negotiable
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.WindowNavigation
|
-- Module : XMonad.Actions.WindowNavigation
|
||||||
-- Description : Experimental rewrite of "XMonad.Layout.WindowNavigation".
|
-- Description : Experimental rewrite of "XMonad.Layout.WindowNavigation".
|
||||||
-- Copyright : (c) 2007 David Roundy <droundy@darcs.net>,
|
-- Copyright : (c) 2007 David Roundy <droundy@darcs.net>,
|
||||||
-- Devin Mullins <me@twifkak.com>
|
-- Devin Mullins <me@twifkak.com>
|
||||||
-- Maintainer : Devin Mullins <me@twifkak.com>
|
-- Maintainer : Devin Mullins <me@twifkak.com>,
|
||||||
|
-- Platon Pronko <platon7pronko@gmail.com>
|
||||||
-- License : BSD3-style (see LICENSE)
|
-- License : BSD3-style (see LICENSE)
|
||||||
-- Stability : unstable
|
-- Stability : unstable
|
||||||
-- Portability : unportable
|
-- Portability : unportable
|
||||||
@ -37,17 +39,19 @@ module XMonad.Actions.WindowNavigation (
|
|||||||
withWindowNavigationKeys,
|
withWindowNavigationKeys,
|
||||||
WNAction(..),
|
WNAction(..),
|
||||||
go, swap,
|
go, swap,
|
||||||
|
goPure, swapPure,
|
||||||
Direction2D(..), WNState,
|
Direction2D(..), WNState,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import XMonad
|
import XMonad hiding (state)
|
||||||
import XMonad.Prelude (catMaybes, fromMaybe, listToMaybe, sortOn)
|
import XMonad.Prelude (catMaybes, fromMaybe, sortOn)
|
||||||
import XMonad.Util.Types (Direction2D(..))
|
import XMonad.Util.Types (Direction2D(..))
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
|
|
||||||
import Control.Arrow (second)
|
import Control.Arrow (second)
|
||||||
import Data.IORef
|
import Data.IORef
|
||||||
import Data.Map (Map())
|
import Data.Map (Map())
|
||||||
|
import Data.List (partition, find)
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
|
||||||
@ -101,33 +105,66 @@ withWindowNavigation (u,l,d,r) conf@XConfig{modMask=modm} =
|
|||||||
|
|
||||||
withWindowNavigationKeys :: [((KeyMask, KeySym), WNAction)] -> XConfig l -> IO (XConfig l)
|
withWindowNavigationKeys :: [((KeyMask, KeySym), WNAction)] -> XConfig l -> IO (XConfig l)
|
||||||
withWindowNavigationKeys wnKeys conf = do
|
withWindowNavigationKeys wnKeys conf = do
|
||||||
posRef <- newIORef M.empty
|
stateRef <- newIORef M.empty
|
||||||
return conf { keys = \cnf -> M.fromList (map (second (fromWNAction posRef)) wnKeys)
|
return conf { keys = \cnf -> M.fromList (map (second (fromWNAction stateRef)) wnKeys)
|
||||||
`M.union` keys conf cnf,
|
`M.union` keys conf cnf,
|
||||||
logHook = logHook conf >> trackMovement posRef }
|
logHook = logHook conf >> trackMovement stateRef }
|
||||||
where fromWNAction posRef (WNGo dir) = go posRef dir
|
where fromWNAction stateRef (WNGo dir) = go stateRef dir
|
||||||
fromWNAction posRef (WNSwap dir) = swap posRef dir
|
fromWNAction stateRef (WNSwap dir) = swap stateRef dir
|
||||||
|
|
||||||
data WNAction = WNGo Direction2D | WNSwap Direction2D
|
data WNAction = WNGo Direction2D | WNSwap Direction2D
|
||||||
|
|
||||||
type WNState = Map WorkspaceId Point
|
type WNState = Map WorkspaceId Point
|
||||||
|
|
||||||
-- go:
|
-- | Focus window in the given direction.
|
||||||
-- 1. get current position, verifying it matches the current window
|
|
||||||
-- 2. get target windowrect
|
|
||||||
-- 3. focus window
|
|
||||||
-- 4. set new position
|
|
||||||
go :: IORef WNState -> Direction2D -> X ()
|
go :: IORef WNState -> Direction2D -> X ()
|
||||||
go = withTargetWindow W.focusWindow
|
go stateRef dir = runPureAction stateRef (goPure dir)
|
||||||
|
|
||||||
|
-- | Swap current window with the window in the given direction.
|
||||||
|
-- Note: doesn't work with floating windows (don't think it makes much sense to swap floating windows).
|
||||||
swap :: IORef WNState -> Direction2D -> X ()
|
swap :: IORef WNState -> Direction2D -> X ()
|
||||||
swap = withTargetWindow swapWithFocused
|
swap stateRef dir = runPureAction stateRef (swapPure dir)
|
||||||
|
|
||||||
|
type WindowRectFn x = (Window -> x (Maybe Rectangle))
|
||||||
|
-- | (state, oldWindowSet, mappedWindows, windowRect)
|
||||||
|
type WNInput x = (WNState, WindowSet, S.Set Window, WindowRectFn x)
|
||||||
|
type WNOutput = (WNState, WindowSet)
|
||||||
|
|
||||||
|
-- | Run the pure action inside X monad.
|
||||||
|
runPureAction :: IORef WNState -> (WNInput X -> X WNOutput) -> X ()
|
||||||
|
runPureAction stateRef action = do
|
||||||
|
oldState <- io (readIORef stateRef)
|
||||||
|
oldWindowSet <- gets windowset
|
||||||
|
mappedWindows <- gets mapped
|
||||||
|
(newState, newWindowSet) <- action (oldState, oldWindowSet, mappedWindows, windowRectX)
|
||||||
|
windows (const newWindowSet)
|
||||||
|
io $ writeIORef stateRef newState
|
||||||
|
|
||||||
|
-- | Version of `go` not dependent on X monad (needed for testing).
|
||||||
|
goPure :: Monad x => Direction2D -> WNInput x -> x WNOutput
|
||||||
|
goPure dir input@(oldState, oldWindowSet, mappedWindows, _) =
|
||||||
|
if length (filter (`S.member` mappedWindows) $ W.integrate' $ W.stack $ W.workspace $ W.current oldWindowSet) == 1
|
||||||
|
then
|
||||||
|
-- Handle the special case of Full layout, when there's only one mapped window on a screen.
|
||||||
|
return ( oldState
|
||||||
|
, case dir of
|
||||||
|
U -> W.focusUp oldWindowSet
|
||||||
|
L -> W.focusDown oldWindowSet
|
||||||
|
D -> W.focusDown oldWindowSet
|
||||||
|
R -> W.focusUp oldWindowSet
|
||||||
|
)
|
||||||
|
else
|
||||||
|
withTargetWindow W.focusWindow dir input
|
||||||
|
|
||||||
|
-- | Version of `swap` not dependent on X monad (needed for testing).
|
||||||
|
swapPure :: Monad x => Direction2D -> WNInput x -> x WNOutput
|
||||||
|
swapPure = withTargetWindow swapWithFocused
|
||||||
where swapWithFocused targetWin winSet =
|
where swapWithFocused targetWin winSet =
|
||||||
case W.peek winSet of
|
case W.peek winSet of
|
||||||
Just currentWin -> W.focusWindow currentWin $
|
Just currentWin -> W.focusWindow currentWin $
|
||||||
mapWindows (swapWin currentWin targetWin) winSet
|
mapWindows (swapWin currentWin targetWin) winSet
|
||||||
Nothing -> winSet
|
Nothing -> winSet
|
||||||
mapWindows f ss = W.mapWorkspace (mapWindows' f) ss
|
mapWindows f = W.mapWorkspace (mapWindows' f)
|
||||||
mapWindows' f ws@W.Workspace{ W.stack = s } = ws { W.stack = mapWindows'' f <$> s }
|
mapWindows' f ws@W.Workspace{ W.stack = s } = ws { W.stack = mapWindows'' f <$> s }
|
||||||
mapWindows'' f (W.Stack focused up down) = W.Stack (f focused) (map f up) (map f down)
|
mapWindows'' f (W.Stack focused up down) = W.Stack (f focused) (map f up) (map f down)
|
||||||
swapWin win1 win2 win
|
swapWin win1 win2 win
|
||||||
@ -135,87 +172,249 @@ swap = withTargetWindow swapWithFocused
|
|||||||
| win == win2 = win1
|
| win == win2 = win1
|
||||||
| otherwise = win
|
| otherwise = win
|
||||||
|
|
||||||
withTargetWindow :: (Window -> WindowSet -> WindowSet) -> IORef WNState -> Direction2D -> X ()
|
-- | Select a target window in the given direction and modify the WindowSet.
|
||||||
withTargetWindow adj posRef dir = fromCurrentPoint posRef $ \win pos -> do
|
-- 1. Get current position, verifying it matches the current window (exit if no focused window).
|
||||||
targets <- filter ((/= win) . fst) <$> navigableTargets pos dir
|
-- 2. Get the target window.
|
||||||
whenJust (listToMaybe targets) $ \(targetWin, targetRect) -> do
|
-- 3. Execute an action on the target window and windowset.
|
||||||
windows (adj targetWin)
|
-- 4. Set the new position.
|
||||||
setPosition posRef pos targetRect
|
withTargetWindow :: Monad x => (Window -> WindowSet -> WindowSet) -> Direction2D -> WNInput x -> x WNOutput
|
||||||
|
withTargetWindow adj dir input@(oldState, oldWindowSet, _, _) = do
|
||||||
|
whenJust' (getCurrentWindow input) (oldState, oldWindowSet) $ \(win, winRect, pos) -> do
|
||||||
|
targetMaybe <- find ((/= win) . fst) <$> navigableTargets input dir winRect pos
|
||||||
|
whenJust' (pure targetMaybe) (oldState, oldWindowSet) $ \(targetWin, newPos) ->
|
||||||
|
let newWindowSet = adj targetWin oldWindowSet
|
||||||
|
in return (modifyState newWindowSet newPos oldState, newWindowSet)
|
||||||
|
|
||||||
|
-- | Update position on outside changes in windows.
|
||||||
trackMovement :: IORef WNState -> X ()
|
trackMovement :: IORef WNState -> X ()
|
||||||
trackMovement posRef = fromCurrentPoint posRef $ \win pos ->
|
trackMovement stateRef = do
|
||||||
windowRect win >>= flip whenJust (setPosition posRef pos . snd)
|
oldState <- io (readIORef stateRef)
|
||||||
|
oldWindowSet <- gets windowset
|
||||||
|
mappedWindows <- gets mapped
|
||||||
|
whenJust' (getCurrentWindow (oldState, oldWindowSet, mappedWindows, windowRectX)) () $ \(_, _, pos) -> do
|
||||||
|
io $ writeIORef stateRef $ modifyState oldWindowSet pos oldState
|
||||||
|
|
||||||
fromCurrentPoint :: IORef WNState -> (Window -> Point -> X ()) -> X ()
|
-- | Get focused window and current position.
|
||||||
fromCurrentPoint posRef f = withFocused $ \win ->
|
getCurrentWindow :: Monad x => WNInput x -> x (Maybe (Window, Rectangle, Point))
|
||||||
currentPosition posRef >>= f win
|
getCurrentWindow input@(_, oldWindowSet, _, _) =
|
||||||
|
whenJust' (pure $ W.peek oldWindowSet) Nothing $ \window -> do
|
||||||
|
(pos, rect) <- currentPosition input
|
||||||
|
return $ Just (window, rect, pos)
|
||||||
|
|
||||||
-- Gets the current position from the IORef passed in, or if nothing (say, from
|
-- | Gets the current position from the state passed in, or if nothing
|
||||||
-- a restart), derives the current position from the current window. Also,
|
-- (say, from a restart), derives the current position from the current window.
|
||||||
-- verifies that the position is congruent with the current window (say, if you
|
-- Also, verifies that the position is congruent with the current window
|
||||||
-- used mod-j/k or mouse or something).
|
-- (say, if you moved focus using mouse or something).
|
||||||
currentPosition :: IORef WNState -> X Point
|
-- Returns the window rectangle for convenience, since we'll need it later anyway.
|
||||||
currentPosition posRef = do
|
currentPosition :: Monad x => WNInput x -> x (Point, Rectangle)
|
||||||
root <- asks theRoot
|
currentPosition (state, oldWindowSet, _, windowRect) = do
|
||||||
currentWindow <- gets (W.peek . windowset)
|
currentRect <- fromMaybe (Rectangle 0 0 0 0) <$> maybe (pure Nothing) windowRect (W.peek oldWindowSet)
|
||||||
currentRect <- maybe (Rectangle 0 0 0 0) snd <$> windowRect (fromMaybe root currentWindow)
|
let posMaybe = M.lookup (W.currentTag oldWindowSet) state
|
||||||
|
middleOf (Rectangle x y w h) = Point (midPoint x w) (midPoint y h)
|
||||||
|
return $ case posMaybe of
|
||||||
|
Nothing -> (middleOf currentRect, currentRect)
|
||||||
|
Just pos -> (centerPosition currentRect pos, currentRect)
|
||||||
|
|
||||||
wsid <- gets (W.currentTag . windowset)
|
-- | Inserts new position into the state.
|
||||||
mp <- M.lookup wsid <$> io (readIORef posRef)
|
modifyState :: WindowSet -> Point -> WNState -> WNState
|
||||||
|
modifyState oldWindowSet =
|
||||||
|
M.insert (W.currentTag oldWindowSet)
|
||||||
|
|
||||||
return $ maybe (middleOf currentRect) (`inside` currentRect) mp
|
-- | "Jumps" the current position into the middle of target rectangle.
|
||||||
|
-- (keeps the position as-is if it is already inside the target rectangle)
|
||||||
where middleOf (Rectangle x y w h) = Point (midPoint x w) (midPoint y h)
|
centerPosition :: Rectangle -> Point -> Point
|
||||||
|
centerPosition r@(Rectangle rx ry rw rh) pos@(Point x y) = do
|
||||||
setPosition :: IORef WNState -> Point -> Rectangle -> X ()
|
if pointWithin x y r
|
||||||
setPosition posRef oldPos newRect = do
|
then pos
|
||||||
wsid <- gets (W.currentTag . windowset)
|
else Point (midPoint rx rw) (midPoint ry rh)
|
||||||
io $ modifyIORef posRef $ M.insert wsid (oldPos `inside` newRect)
|
|
||||||
|
|
||||||
inside :: Point -> Rectangle -> Point
|
|
||||||
Point x y `inside` Rectangle rx ry rw rh =
|
|
||||||
Point (x `within` (rx, rw)) (y `within` (ry, rh))
|
|
||||||
where pos `within` (lower, dim) = if pos >= lower && pos < lower + fromIntegral dim
|
|
||||||
then pos
|
|
||||||
else midPoint lower dim
|
|
||||||
|
|
||||||
midPoint :: Position -> Dimension -> Position
|
midPoint :: Position -> Dimension -> Position
|
||||||
midPoint pos dim = pos + fromIntegral dim `div` 2
|
midPoint pos dim = pos + fromIntegral dim `div` 2
|
||||||
|
|
||||||
navigableTargets :: Point -> Direction2D -> X [(Window, Rectangle)]
|
-- | Make a list of target windows we can navigate to,
|
||||||
navigableTargets point dir = navigable dir point <$> windowRects
|
-- sorted by desirability of navigation.
|
||||||
|
navigableTargets :: Monad x => WNInput x -> Direction2D -> Rectangle -> Point -> x [(Window, Point)]
|
||||||
|
navigableTargets input@(_, oldWindowSet, _, _) dir currentRect currentPos = do
|
||||||
|
allScreensWindowsAndRectangles <- mapSnd (rectTransform dir) <$> windowRects input
|
||||||
|
let
|
||||||
|
screenWindows = S.fromList $ W.integrate' $ W.stack $ W.workspace $ W.current oldWindowSet
|
||||||
|
(thisScreenWindowsAndRectangles, otherScreensWindowsAndRectangles) = partition (\(w, _) -> S.member w screenWindows) allScreensWindowsAndRectangles
|
||||||
|
|
||||||
-- Filters and sorts the windows in terms of what is closest from the Point in
|
pos = pointTransform dir currentPos
|
||||||
-- the Direction2D.
|
wr = rectTransform dir currentRect
|
||||||
navigable :: Direction2D -> Point -> [(Window, Rectangle)] -> [(Window, Rectangle)]
|
|
||||||
navigable d pt = sortby d . filter (inr d pt . snd)
|
|
||||||
|
|
||||||
-- Produces a list of normal-state windows, on any screen. Rectangles are
|
rectInside r = (rect_p1 r >= rect_p1 wr && rect_p1 r < rect_p2 wr && rect_p2 r > rect_p1 wr && rect_p2 r <= rect_p2 wr) &&
|
||||||
-- adjusted based on screen position relative to the current screen, because I'm
|
((rect_o1 r >= rect_o1 wr && rect_o1 r < rect_o2 wr && rect_o2 r > rect_o1 wr && rect_o2 r <= rect_o2 wr) ||
|
||||||
-- bad like that.
|
(rect_o1 r <= rect_o1 wr && rect_o2 r >= rect_o2 wr)) -- include windows that fully overlaps current on the orthogonal axis
|
||||||
windowRects :: X [(Window, Rectangle)]
|
sortByP2 = sortOn (rect_p2 . snd)
|
||||||
windowRects = fmap catMaybes . mapM windowRect . S.toList =<< gets mapped
|
posBeforeEdge r = point_p pos < rect_p2 r
|
||||||
|
|
||||||
windowRect :: Window -> X (Maybe (Window, Rectangle))
|
rectOverlapsEdge r = rect_p1 r <= rect_p2 wr && rect_p2 r > rect_p2 wr &&
|
||||||
windowRect win = withDisplay $ \dpy -> do
|
rect_o1 r < rect_o2 wr && rect_o2 r > rect_o1 wr
|
||||||
|
rectOverlapsOneEdge r = rectOverlapsEdge r && rect_p1 r > rect_p1 wr
|
||||||
|
rectOverlapsBothEdges r = rectOverlapsEdge r &&
|
||||||
|
rect_o1 r > rect_o1 wr && rect_o2 r < rect_o2 wr && point_o pos >= rect_o1 r && point_o pos < rect_o2 r
|
||||||
|
distanceToRectEdge r = max (max 0 (rect_o1 r - point_o pos)) (max 0 (point_o pos + 1 - rect_o2 r))
|
||||||
|
distanceToRectCenter r =
|
||||||
|
let distance = (rect_o1 r + rect_o2 r) `div` 2 - point_o pos
|
||||||
|
in if distance <= 0
|
||||||
|
then distance + 1
|
||||||
|
else distance
|
||||||
|
sortByPosDistance = sortOn ((\r -> (rect_p1 r, distanceToRectEdge r, distanceToRectCenter r)) . snd)
|
||||||
|
|
||||||
|
rectOutside r = rect_p1 r < rect_p1 wr && rect_p2 r > rect_p2 wr &&
|
||||||
|
rect_o1 r < rect_o1 wr && rect_o2 r > rect_o2 wr
|
||||||
|
sortByLength = sortOn (rect_psize . snd)
|
||||||
|
|
||||||
|
rectAfterEdge r = rect_p1 r > rect_p2 wr
|
||||||
|
|
||||||
|
-- Modified from David Roundy and Devin Mullins original implementation of WindowNavigation:
|
||||||
|
inr r = point_p pos < rect_p2 r && point_o pos >= rect_o1 r && point_o pos < rect_o2 r
|
||||||
|
|
||||||
|
clamp v v1 v2 | v < v1 = v1
|
||||||
|
| v >= v2 = v2 - 1
|
||||||
|
| otherwise = v
|
||||||
|
dragPos r = DirPoint (max (point_p pos) (rect_p1 r)) (clamp (point_o pos) (rect_o1 r) (rect_o2 r))
|
||||||
|
|
||||||
|
return $ mapSnd (inversePointTransform dir) $ concat
|
||||||
|
[
|
||||||
|
-- First, navigate to windows that are fully inside current window
|
||||||
|
-- and have higher coordinate bigger than current position.
|
||||||
|
-- ┌──────────────────┐
|
||||||
|
-- │ current │ (all examples assume direction=R)
|
||||||
|
-- │ ┌──────────┐ │
|
||||||
|
-- │ ──┼─► inside │ │
|
||||||
|
-- │ └──────────┘ │
|
||||||
|
-- └──────────────────┘
|
||||||
|
-- Also include windows fully overlapping current on the orthogonal axis:
|
||||||
|
-- ┌──────────────┐
|
||||||
|
-- │ overlapping │
|
||||||
|
-- ┌───────────┤ ├────┐
|
||||||
|
-- │ current ──┼─► │ │
|
||||||
|
-- └───────────┤ ├────┘
|
||||||
|
-- └──────────────┘
|
||||||
|
mapSnd dragPos $ sortByP2 $ filterSnd posBeforeEdge $ filterSnd rectInside thisScreenWindowsAndRectangles
|
||||||
|
|
||||||
|
-- Then navigate to windows that touch or overlap the edge of current window in the chosen direction.
|
||||||
|
-- ┌──────────────┬─────────────┐ ┌───────────┐ ┌─────────────┐
|
||||||
|
-- │ current │ adjacent │ │ current │ │ current │
|
||||||
|
-- │ ──┼─► │ │ ┌───┴───────────────┐ │ ┌───┴─────────────┐
|
||||||
|
-- │ │ │ │ ──┼─► │ overlapping │ │ ──┼─► │
|
||||||
|
-- │ ├─────────────┘ │ └───┬───────────────┘ └─────────┤ overlapping │
|
||||||
|
-- │ │ │ │ │ │
|
||||||
|
-- └──────────────┘ └───────────┘ └─────────────────┘
|
||||||
|
, mapSnd dragPos $ sortByPosDistance $ filterSnd rectOverlapsOneEdge thisScreenWindowsAndRectangles
|
||||||
|
|
||||||
|
-- Windows fully overlapping current window "in the middle" on the parallel axis are also included,
|
||||||
|
-- if position is inside them:
|
||||||
|
-- ┌───────────┐
|
||||||
|
-- │ current │
|
||||||
|
-- ┌───┤-----------├────────────────┐
|
||||||
|
-- │ │ * ──┼─► overlapping │
|
||||||
|
-- └───┤-----------├────────────────┘
|
||||||
|
-- └───────────┘
|
||||||
|
, mapSnd (\_ -> DirPoint (rect_p2 wr) (point_o pos)) $ sortByPosDistance $ filterSnd rectOverlapsBothEdges thisScreenWindowsAndRectangles
|
||||||
|
|
||||||
|
-- Then navigate to windows that fully encompass the current window.
|
||||||
|
-- ┌─────────────────────┐
|
||||||
|
-- │ outer │
|
||||||
|
-- │ ┌─────────────┐ │
|
||||||
|
-- │ │ current ──┼─► │
|
||||||
|
-- │ └─────────────┘ │
|
||||||
|
-- └─────────────────────┘
|
||||||
|
, mapSnd (\_ -> DirPoint (rect_p2 wr) (point_o pos)) $ sortByLength $ filterSnd rectOutside thisScreenWindowsAndRectangles
|
||||||
|
|
||||||
|
-- Then navigate to windows that are fully after current window in the chosen direction.
|
||||||
|
-- ┌──────────────┐
|
||||||
|
-- │ current │ ┌────────────────┐
|
||||||
|
-- │ │ │ │
|
||||||
|
-- │ ──┼──┼─► not adjacent │
|
||||||
|
-- │ │ │ │
|
||||||
|
-- │ │ └────────────────┘
|
||||||
|
-- └──────────────┘
|
||||||
|
, mapSnd dragPos $ sortByPosDistance $ filterSnd rectAfterEdge thisScreenWindowsAndRectangles
|
||||||
|
|
||||||
|
-- Cast a ray from the current position, jump to the first window (on another screen) that intersects this ray.
|
||||||
|
, mapSnd dragPos $ sortByPosDistance $ filterSnd inr otherScreensWindowsAndRectangles
|
||||||
|
|
||||||
|
-- If everything else fails, then navigate to the window that is fully inside current window,
|
||||||
|
-- but is before the current position.
|
||||||
|
-- This can happen when we are at the last window on a screen, and attempt to navigate even further.
|
||||||
|
-- In this case it seems okay to jump to the remaining inner windows, since we don't have any other choice anyway,
|
||||||
|
-- and user is probably not so fully aware of the precise position anyway.
|
||||||
|
, mapSnd (\r -> DirPoint (rect_p2 r - 1) (clamp (point_o pos) (rect_o1 r) (rect_o2 r))) $
|
||||||
|
sortByP2 $ filterSnd (not . posBeforeEdge) $ filterSnd rectInside thisScreenWindowsAndRectangles
|
||||||
|
]
|
||||||
|
|
||||||
|
-- Structs for direction-independent space - equivalent to rotating points and rectangles such that
|
||||||
|
-- navigation direction points to the right.
|
||||||
|
-- Allows us to abstract over direction in the navigation functions.
|
||||||
|
data DirPoint = DirPoint
|
||||||
|
{ point_p :: Position -- coordinate parallel to the direction
|
||||||
|
, point_o :: Position -- coordinate orthogonal to the direction
|
||||||
|
}
|
||||||
|
data DirRectangle = DirRectangle
|
||||||
|
{ rect_p1 :: Position -- lower rectangle coordinate parallel to the direction
|
||||||
|
, rect_p2 :: Position -- higher rectangle coordinate parallel to the direction
|
||||||
|
, rect_o1 :: Position -- lower rectangle coordinate orthogonal to the direction
|
||||||
|
, rect_o2 :: Position -- higher rectangle coordinate orthogonal to the direction
|
||||||
|
}
|
||||||
|
{- HLINT ignore "Use camelCase" -}
|
||||||
|
rect_psize :: DirRectangle -> Dimension
|
||||||
|
rect_psize r = fromIntegral (rect_p2 r - rect_p1 r)
|
||||||
|
|
||||||
|
-- | Transform a point from screen space into direction-independent space.
|
||||||
|
pointTransform :: Direction2D -> Point -> DirPoint
|
||||||
|
pointTransform dir (Point x y) = case dir of
|
||||||
|
U -> DirPoint (negate y - 1) x
|
||||||
|
L -> DirPoint (negate x - 1) (negate y - 1)
|
||||||
|
D -> DirPoint y (negate x - 1)
|
||||||
|
R -> DirPoint x y
|
||||||
|
|
||||||
|
-- | Transform a point from direction-independent space back into screen space.
|
||||||
|
inversePointTransform :: Direction2D -> DirPoint -> Point
|
||||||
|
inversePointTransform dir p = case dir of
|
||||||
|
U -> Point (point_o p) (negate $ point_p p + 1)
|
||||||
|
L -> Point (negate $ point_p p + 1) (negate $ point_o p + 1)
|
||||||
|
D -> Point (negate $ point_o p + 1) (point_p p)
|
||||||
|
R -> Point (point_p p) (point_o p)
|
||||||
|
|
||||||
|
-- | Transform a rectangle from screen space into direction-independent space.
|
||||||
|
rectTransform :: Direction2D -> Rectangle -> DirRectangle
|
||||||
|
rectTransform dir (Rectangle x y w h) = case dir of
|
||||||
|
U -> DirRectangle (negate $ y + fromIntegral h) (negate y) x (x + fromIntegral w)
|
||||||
|
L -> DirRectangle (negate $ x + fromIntegral w) (negate x) (negate $ y + fromIntegral h) (negate y)
|
||||||
|
D -> DirRectangle y (y + fromIntegral h) (negate $ x + fromIntegral w) (negate x)
|
||||||
|
R -> DirRectangle x (x + fromIntegral w) y (y + fromIntegral h)
|
||||||
|
|
||||||
|
-- | Produces a list of normal-state windows on all screens, excluding currently focused window.
|
||||||
|
windowRects :: Monad x => WNInput x -> x [(Window, Rectangle)]
|
||||||
|
windowRects (_, oldWindowSet, mappedWindows, windowRect) =
|
||||||
|
let
|
||||||
|
allWindows = filter (\w -> w `notElem` W.peek oldWindowSet) $ S.toList mappedWindows
|
||||||
|
windowRect2 w = fmap (w,) <$> windowRect w
|
||||||
|
in catMaybes <$> mapM windowRect2 allWindows
|
||||||
|
|
||||||
|
windowRectX :: Window -> X (Maybe Rectangle)
|
||||||
|
windowRectX win = withDisplay $ \dpy -> do
|
||||||
(_, x, y, w, h, bw, _) <- io $ getGeometry dpy win
|
(_, x, y, w, h, bw, _) <- io $ getGeometry dpy win
|
||||||
return $ Just (win, Rectangle x y (w + 2 * bw) (h + 2 * bw))
|
return $ Just $ Rectangle x y (w + 2 * bw) (h + 2 * bw)
|
||||||
`catchX` return Nothing
|
`catchX` return Nothing
|
||||||
|
|
||||||
-- Modified from droundy's implementation of WindowNavigation:
|
-- Maybe below functions can be replaced with some standard helper functions?
|
||||||
|
|
||||||
inr :: Direction2D -> Point -> Rectangle -> Bool
|
-- | Execute a monadic action on the contents if Just, otherwise wrap default value and return it.
|
||||||
inr D (Point px py) (Rectangle rx ry w h) = px >= rx && px < rx + fromIntegral w &&
|
whenJust' :: Monad x => x (Maybe a) -> b -> (a -> x b) -> x b
|
||||||
py < ry + fromIntegral h
|
whenJust' monadMaybeValue deflt f = do
|
||||||
inr U (Point px py) (Rectangle rx ry w _) = px >= rx && px < rx + fromIntegral w &&
|
maybeValue <- monadMaybeValue
|
||||||
py > ry
|
case maybeValue of
|
||||||
inr R (Point px py) (Rectangle rx ry _ h) = px < rx &&
|
Nothing -> return deflt
|
||||||
py >= ry && py < ry + fromIntegral h
|
Just value -> f value
|
||||||
inr L (Point px py) (Rectangle rx ry w h) = px > rx + fromIntegral w &&
|
|
||||||
py >= ry && py < ry + fromIntegral h
|
|
||||||
|
|
||||||
sortby :: Direction2D -> [(a,Rectangle)] -> [(a,Rectangle)]
|
-- | Filter a list of tuples on the second tuple member.
|
||||||
sortby D = sortOn (rect_y . snd)
|
filterSnd :: (b -> Bool) -> [(a, b)] -> [(a, b)]
|
||||||
sortby R = sortOn (rect_x . snd)
|
filterSnd f = filter (f . snd)
|
||||||
sortby U = reverse . sortby D
|
|
||||||
sortby L = reverse . sortby R
|
-- | Map a second tuple member in a list of tuples.
|
||||||
|
mapSnd :: (b -> b') -> [(a, b)] -> [(a, b')]
|
||||||
|
mapSnd f = map (second f)
|
||||||
|
@ -24,7 +24,7 @@ import XMonad.StackSet
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.WithAll
|
-- > import XMonad.Actions.WithAll
|
||||||
--
|
--
|
||||||
@ -33,7 +33,7 @@ import XMonad.StackSet
|
|||||||
-- , ((modm .|. shiftMask, xK_t), sinkAll)
|
-- , ((modm .|. shiftMask, xK_t), sinkAll)
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
-- | Un-float all floating windows on the current workspace.
|
-- | Un-float all floating windows on the current workspace.
|
||||||
sinkAll :: X ()
|
sinkAll :: X ()
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Actions.Workscreen
|
-- Module : XMonad.Actions.Workscreen
|
||||||
@ -35,12 +37,13 @@ module XMonad.Actions.Workscreen (
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import XMonad hiding (workspaces)
|
import XMonad hiding (workspaces)
|
||||||
|
import XMonad.Prelude
|
||||||
import qualified XMonad.StackSet as W
|
import qualified XMonad.StackSet as W
|
||||||
import qualified XMonad.Util.ExtensibleState as XS
|
import qualified XMonad.Util.ExtensibleState as XS
|
||||||
import XMonad.Actions.OnScreen
|
import XMonad.Actions.OnScreen
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
-- You can use this module with the following in your @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.Workscreen
|
-- > import XMonad.Actions.Workscreen
|
||||||
-- > myWorkspaces = let myOldWorkspaces = ["adm","work","mail"]
|
-- > myWorkspaces = let myOldWorkspaces = ["adm","work","mail"]
|
||||||
@ -55,7 +58,7 @@ import XMonad.Actions.OnScreen
|
|||||||
-- > , (f, m) <- [(Workscreen.viewWorkscreen, 0), (Workscreen.shiftToWorkscreen, shiftMask)]]
|
-- > , (f, m) <- [(Workscreen.viewWorkscreen, 0), (Workscreen.shiftToWorkscreen, shiftMask)]]
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
|
|
||||||
data Workscreen = Workscreen{workscreenId::Int,workspaces::[WorkspaceId]} deriving (Show)
|
data Workscreen = Workscreen{workscreenId::Int,workspaces::[WorkspaceId]} deriving (Show)
|
||||||
@ -90,7 +93,7 @@ viewWorkscreen wscrId = do (WorkscreenStorage c a) <- XS.get
|
|||||||
let wscr = if wscrId == c
|
let wscr = if wscrId == c
|
||||||
then Workscreen wscrId $ shiftWs (workspaces $ a !! wscrId)
|
then Workscreen wscrId $ shiftWs (workspaces $ a !! wscrId)
|
||||||
else a !! wscrId
|
else a !! wscrId
|
||||||
(x,_:ys) = splitAt wscrId a
|
(x, notEmpty -> _ :| ys) = splitAt wscrId a
|
||||||
newWorkscreenStorage = WorkscreenStorage wscrId (x ++ [wscr] ++ ys)
|
newWorkscreenStorage = WorkscreenStorage wscrId (x ++ [wscr] ++ ys)
|
||||||
windows (viewWorkscreen' wscr)
|
windows (viewWorkscreen' wscr)
|
||||||
XS.put newWorkscreenStorage
|
XS.put newWorkscreenStorage
|
||||||
@ -106,5 +109,6 @@ shiftWs a = drop 1 a ++ take 1 a
|
|||||||
-- @WorkscreenId@.
|
-- @WorkscreenId@.
|
||||||
shiftToWorkscreen :: WorkscreenId -> X ()
|
shiftToWorkscreen :: WorkscreenId -> X ()
|
||||||
shiftToWorkscreen wscrId = do (WorkscreenStorage _ a) <- XS.get
|
shiftToWorkscreen wscrId = do (WorkscreenStorage _ a) <- XS.get
|
||||||
let ws = head . workspaces $ a !! wscrId
|
case workspaces (a !! wscrId) of
|
||||||
windows $ W.shift ws
|
[] -> pure ()
|
||||||
|
(w : _) -> windows $ W.shift w
|
||||||
|
@ -50,7 +50,7 @@ import XMonad.Layout.LayoutModifier(ModifiedLayout(..),
|
|||||||
import XMonad(Message, WorkspaceId, X, XState(windowset),
|
import XMonad(Message, WorkspaceId, X, XState(windowset),
|
||||||
fromMessage, sendMessage, windows, gets)
|
fromMessage, sendMessage, windows, gets)
|
||||||
import XMonad.Util.Stack (reverseS)
|
import XMonad.Util.Stack (reverseS)
|
||||||
import XMonad.Prelude (find, fromJust, guard, liftA2, toList, when, (<=<))
|
import XMonad.Prelude
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
--
|
--
|
||||||
@ -95,10 +95,10 @@ import XMonad.Prelude (find, fromJust, guard, liftA2, toList, when, (<=<))
|
|||||||
|
|
||||||
-- | makeCursors requires a nonempty string, and each sublist must be nonempty
|
-- | makeCursors requires a nonempty string, and each sublist must be nonempty
|
||||||
makeCursors :: [[String]] -> Cursors String
|
makeCursors :: [[String]] -> Cursors String
|
||||||
makeCursors [] = error "Workspace Cursors cannot be empty"
|
makeCursors [] = error "Workspace Cursors cannot be empty"
|
||||||
makeCursors a = concat . reverse <$> foldl addDim x xs
|
makeCursors (a : as) = concat . reverse <$> foldl addDim x xs
|
||||||
where x = end $ map return $ head a
|
where x = end $ map return a
|
||||||
xs = map (map return) $ tail a
|
xs = map (map return) as
|
||||||
-- this could probably be simplified, but this true:
|
-- this could probably be simplified, but this true:
|
||||||
-- toList . makeCursors == map (concat . reverse) . sequence . reverse . map (map (:[]))
|
-- toList . makeCursors == map (concat . reverse) . sequence . reverse . map (map (:[]))
|
||||||
-- the strange order is used because it makes the regular M-1..9
|
-- the strange order is used because it makes the regular M-1..9
|
||||||
@ -212,4 +212,4 @@ instance LayoutModifier WorkspaceCursors a where
|
|||||||
return (arrs,WorkspaceCursors <$> focusTo cws cs)
|
return (arrs,WorkspaceCursors <$> focusTo cws cs)
|
||||||
|
|
||||||
handleMess (WorkspaceCursors cs) m =
|
handleMess (WorkspaceCursors cs) m =
|
||||||
sequenceA $ fmap WorkspaceCursors . ($ cs) . unWrap <$> fromMessage m
|
traverse (fmap WorkspaceCursors . ($ cs) . unWrap) (fromMessage m)
|
||||||
|
@ -51,14 +51,14 @@ import XMonad.Actions.CycleWS (findWorkspace, WSType(..), Direction1D(..), anyWS
|
|||||||
import qualified XMonad.Actions.SwapWorkspaces as Swap
|
import qualified XMonad.Actions.SwapWorkspaces as Swap
|
||||||
import XMonad.Hooks.StatusBar.PP (PP(..))
|
import XMonad.Hooks.StatusBar.PP (PP(..))
|
||||||
import XMonad.Hooks.EwmhDesktops (addEwmhWorkspaceRename)
|
import XMonad.Hooks.EwmhDesktops (addEwmhWorkspaceRename)
|
||||||
import XMonad.Prompt (mkXPrompt, XPConfig)
|
import XMonad.Prompt (mkXPrompt, XPConfig, historyCompletionP)
|
||||||
import XMonad.Prompt.Workspace (Wor(Wor))
|
import XMonad.Prompt.Workspace (Wor(Wor))
|
||||||
import XMonad.Util.WorkspaceCompare (getSortByIndex)
|
import XMonad.Util.WorkspaceCompare (getSortByIndex)
|
||||||
|
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
|
-- You can use this module with the following in your @xmonad.hs@ file:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.WorkspaceNames
|
-- > import XMonad.Actions.WorkspaceNames
|
||||||
--
|
--
|
||||||
@ -88,7 +88,7 @@ import qualified Data.Map as M
|
|||||||
-- > | (i, k) <- zip workspaces [xK_1 ..]]
|
-- > | (i, k) <- zip workspaces [xK_1 ..]]
|
||||||
--
|
--
|
||||||
-- For detailed instructions on editing your key bindings, see
|
-- For detailed instructions on editing your key bindings, see
|
||||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -137,9 +137,11 @@ setCurrentWorkspaceName name = do
|
|||||||
|
|
||||||
-- | Prompt for a new name for the current workspace and set it.
|
-- | Prompt for a new name for the current workspace and set it.
|
||||||
renameWorkspace :: XPConfig -> X ()
|
renameWorkspace :: XPConfig -> X ()
|
||||||
renameWorkspace conf =
|
renameWorkspace conf = do
|
||||||
mkXPrompt pr conf (const (return [])) setCurrentWorkspaceName
|
completion <- historyCompletionP conf (prompt ==)
|
||||||
where pr = Wor "Workspace name: "
|
mkXPrompt (Wor prompt) conf completion setCurrentWorkspaceName
|
||||||
|
where
|
||||||
|
prompt = "Workspace name: "
|
||||||
|
|
||||||
-- | See 'XMonad.Actions.SwapWorkspaces.swapTo'. This is the same with names.
|
-- | See 'XMonad.Actions.SwapWorkspaces.swapTo'. This is the same with names.
|
||||||
swapTo :: Direction1D -> X ()
|
swapTo :: Direction1D -> X ()
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
module XMonad.Config.Arossato
|
module XMonad.Config.Arossato
|
||||||
|
{-# DEPRECATED "This module contains a personal configuration, to be removed from xmonad-contrib. If you use this module, please copy the relevant parts to your configuration or obtain a copy of it on https://xmonad.org/configurations.html and include it as a local module." #-}
|
||||||
( -- * Usage
|
( -- * Usage
|
||||||
-- $usage
|
-- $usage
|
||||||
arossatoConfig
|
arossatoConfig
|
||||||
@ -46,7 +47,7 @@ import XMonad.Util.Themes
|
|||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- The simplest way to use this configuration module is to use an
|
-- The simplest way to use this configuration module is to use an
|
||||||
-- @~\/.xmonad\/xmonad.hs@ like this:
|
-- @xmonad.hs@ like this:
|
||||||
--
|
--
|
||||||
-- > module Main (main) where
|
-- > module Main (main) where
|
||||||
-- >
|
-- >
|
||||||
@ -63,7 +64,7 @@ import XMonad.Util.Themes
|
|||||||
--
|
--
|
||||||
-- You can use this module also as a starting point for writing your
|
-- You can use this module also as a starting point for writing your
|
||||||
-- own configuration module from scratch. Save it as your
|
-- own configuration module from scratch. Save it as your
|
||||||
-- @~\/.xmonad\/xmonad.hs@ and:
|
-- @xmonad.hs@ and:
|
||||||
--
|
--
|
||||||
-- 1. Change the module name from
|
-- 1. Change the module name from
|
||||||
--
|
--
|
||||||
|
@ -27,7 +27,7 @@ import qualified XMonad.StackSet as W
|
|||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- To use this module, start with the following @~\/.xmonad\/xmonad.hs@:
|
-- To use this module, start with the following @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad
|
-- > import XMonad
|
||||||
-- > import XMonad.Config.Azerty
|
-- > import XMonad.Config.Azerty
|
||||||
@ -37,11 +37,11 @@ import qualified Data.Map as M
|
|||||||
-- If you prefer, an azertyKeys function is provided which you can use as so:
|
-- If you prefer, an azertyKeys function is provided which you can use as so:
|
||||||
--
|
--
|
||||||
-- > import qualified Data.Map as M
|
-- > import qualified Data.Map as M
|
||||||
-- > main = xmonad someConfig { keys = \c -> azertyKeys c <+> keys someConfig c }
|
-- > main = xmonad someConfig { keys = \c -> azertyKeys c <> keys someConfig c }
|
||||||
|
|
||||||
azertyConfig = def { keys = azertyKeys <+> keys def }
|
azertyConfig = def { keys = azertyKeys <> keys def }
|
||||||
|
|
||||||
belgianConfig = def { keys = belgianKeys <+> keys def }
|
belgianConfig = def { keys = belgianKeys <> keys def }
|
||||||
|
|
||||||
azertyKeys = azertyKeysTop [0x26,0xe9,0x22,0x27,0x28,0x2d,0xe8,0x5f,0xe7,0xe0]
|
azertyKeys = azertyKeysTop [0x26,0xe9,0x22,0x27,0x28,0x2d,0xe8,0x5f,0xe7,0xe0]
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ import qualified XMonad.StackSet as W
|
|||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- To use this module, start with the following @~\/.xmonad\/xmonad.hs@:
|
-- To use this module, start with the following @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad
|
-- > import XMonad
|
||||||
-- > import XMonad.Config.Bepo
|
-- > import XMonad.Config.Bepo
|
||||||
@ -38,7 +38,7 @@ import qualified Data.Map as M
|
|||||||
-- > import qualified Data.Map as M
|
-- > import qualified Data.Map as M
|
||||||
-- > main = xmonad someConfig { keys = \c -> bepoKeys c `M.union` keys someConfig c }
|
-- > main = xmonad someConfig { keys = \c -> bepoKeys c `M.union` keys someConfig c }
|
||||||
|
|
||||||
bepoConfig = def { keys = bepoKeys <+> keys def }
|
bepoConfig = def { keys = bepoKeys <> keys def }
|
||||||
|
|
||||||
bepoKeys conf@XConfig { modMask = modm } = M.fromList $
|
bepoKeys conf@XConfig { modMask = modm } = M.fromList $
|
||||||
((modm, xK_semicolon), sendMessage (IncMasterN (-1)))
|
((modm, xK_semicolon), sendMessage (IncMasterN (-1)))
|
||||||
|
@ -29,7 +29,7 @@ module XMonad.Config.Bluetile (
|
|||||||
import XMonad
|
import XMonad
|
||||||
|
|
||||||
import XMonad.Layout.BorderResize
|
import XMonad.Layout.BorderResize
|
||||||
import XMonad.Layout.BoringWindows
|
import XMonad.Layout.BoringWindows hiding (Replace)
|
||||||
import XMonad.Layout.ButtonDecoration
|
import XMonad.Layout.ButtonDecoration
|
||||||
import XMonad.Layout.Decoration
|
import XMonad.Layout.Decoration
|
||||||
import XMonad.Layout.DecorationAddons
|
import XMonad.Layout.DecorationAddons
|
||||||
@ -37,7 +37,7 @@ import XMonad.Layout.DraggingVisualizer
|
|||||||
import XMonad.Layout.Maximize
|
import XMonad.Layout.Maximize
|
||||||
import XMonad.Layout.Minimize
|
import XMonad.Layout.Minimize
|
||||||
import XMonad.Layout.MouseResizableTile
|
import XMonad.Layout.MouseResizableTile
|
||||||
import XMonad.Layout.Named
|
import XMonad.Layout.Renamed
|
||||||
import XMonad.Layout.NoBorders
|
import XMonad.Layout.NoBorders
|
||||||
import XMonad.Layout.PositionStoreFloat
|
import XMonad.Layout.PositionStoreFloat
|
||||||
import XMonad.Layout.WindowSwitcherDecoration
|
import XMonad.Layout.WindowSwitcherDecoration
|
||||||
@ -65,7 +65,7 @@ import System.Exit
|
|||||||
import XMonad.Prelude(when)
|
import XMonad.Prelude(when)
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- To use this module, start with the following @~\/.xmonad\/xmonad.hs@:
|
-- To use this module, start with the following @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad
|
-- > import XMonad
|
||||||
-- > import XMonad.Config.Bluetile
|
-- > import XMonad.Config.Bluetile
|
||||||
@ -183,10 +183,10 @@ bluetileManageHook = composeAll
|
|||||||
, isFullscreen --> doFullFloat]
|
, isFullscreen --> doFullFloat]
|
||||||
|
|
||||||
bluetileLayoutHook = avoidStruts $ minimize $ boringWindows $
|
bluetileLayoutHook = avoidStruts $ minimize $ boringWindows $
|
||||||
named "Floating" floating |||
|
renamed [Replace "Floating"] floating |||
|
||||||
named "Tiled1" tiled1 |||
|
renamed [Replace "Tiled1"] tiled1 |||
|
||||||
named "Tiled2" tiled2 |||
|
renamed [Replace "Tiled2"] tiled2 |||
|
||||||
named "Fullscreen" fullscreen
|
renamed [Replace "Fullscreen"] fullscreen
|
||||||
where
|
where
|
||||||
floating = floatingDeco $ maximize $ borderResize positionStoreFloat
|
floating = floatingDeco $ maximize $ borderResize positionStoreFloat
|
||||||
tiled1 = tilingDeco $ maximize mouseResizableTileMirrored
|
tiled1 = tilingDeco $ maximize mouseResizableTileMirrored
|
||||||
@ -194,7 +194,7 @@ bluetileLayoutHook = avoidStruts $ minimize $ boringWindows $
|
|||||||
fullscreen = tilingDeco $ maximize $ smartBorders Full
|
fullscreen = tilingDeco $ maximize $ smartBorders Full
|
||||||
|
|
||||||
tilingDeco l = windowSwitcherDecorationWithButtons shrinkText defaultThemeWithButtons (draggingVisualizer l)
|
tilingDeco l = windowSwitcherDecorationWithButtons shrinkText defaultThemeWithButtons (draggingVisualizer l)
|
||||||
floatingDeco l = buttonDeco shrinkText defaultThemeWithButtons l
|
floatingDeco = buttonDeco shrinkText defaultThemeWithButtons
|
||||||
|
|
||||||
bluetileConfig =
|
bluetileConfig =
|
||||||
docks . ewmhFullscreen . ewmh $
|
docks . ewmhFullscreen . ewmh $
|
||||||
|
@ -24,7 +24,7 @@ module XMonad.Config.Desktop (
|
|||||||
-- specification. Extra xmonad settings unique to specific DE's are
|
-- specification. Extra xmonad settings unique to specific DE's are
|
||||||
-- added by overriding or modifying @desktopConfig@ fields in the
|
-- added by overriding or modifying @desktopConfig@ fields in the
|
||||||
-- same way that the default configuration is customized in
|
-- same way that the default configuration is customized in
|
||||||
-- @~\/.xmonad/xmonad.hs@.
|
-- @xmonad.hs@.
|
||||||
--
|
--
|
||||||
-- For more information about EWMH see:
|
-- For more information about EWMH see:
|
||||||
--
|
--
|
||||||
@ -72,7 +72,7 @@ import qualified Data.Map as M
|
|||||||
-- <http://haskell.org/haskellwiki/Xmonad>
|
-- <http://haskell.org/haskellwiki/Xmonad>
|
||||||
--
|
--
|
||||||
-- To configure xmonad for use with a DE or with DE tools like panels
|
-- To configure xmonad for use with a DE or with DE tools like panels
|
||||||
-- and pagers, in place of @def@ in your @~\/.xmonad/xmonad.hs@,
|
-- and pagers, in place of @def@ in your @xmonad.hs@,
|
||||||
-- use @desktopConfig@ or one of the other desktop configs from the
|
-- use @desktopConfig@ or one of the other desktop configs from the
|
||||||
-- @XMonad.Config@ namespace. The following setup and customization examples
|
-- @XMonad.Config@ namespace. The following setup and customization examples
|
||||||
-- work the same way for the other desktop configs as for @desktopConfig@.
|
-- work the same way for the other desktop configs as for @desktopConfig@.
|
||||||
@ -91,7 +91,7 @@ import qualified Data.Map as M
|
|||||||
|
|
||||||
-- $customizing
|
-- $customizing
|
||||||
-- To customize a desktop config, modify its fields as is illustrated with
|
-- To customize a desktop config, modify its fields as is illustrated with
|
||||||
-- the default configuration @def@ in "XMonad.Doc.Extending#Extending xmonad".
|
-- the default configuration @def@ in <https://xmonad.org/TUTORIAL.html the tutorial>.
|
||||||
|
|
||||||
-- $layouts
|
-- $layouts
|
||||||
-- See also "XMonad.Util.EZConfig" for more options for modifying key bindings.
|
-- See also "XMonad.Util.EZConfig" for more options for modifying key bindings.
|
||||||
@ -106,7 +106,7 @@ import qualified Data.Map as M
|
|||||||
-- > main =
|
-- > main =
|
||||||
-- > xmonad $ desktopConfig {
|
-- > xmonad $ desktopConfig {
|
||||||
-- > -- add manage hooks while still ignoring panels and using default manageHooks
|
-- > -- add manage hooks while still ignoring panels and using default manageHooks
|
||||||
-- > manageHook = myManageHook <+> manageHook desktopConfig
|
-- > manageHook = myManageHook <> manageHook desktopConfig
|
||||||
-- >
|
-- >
|
||||||
-- > -- add a fullscreen tabbed layout that does not avoid covering
|
-- > -- add a fullscreen tabbed layout that does not avoid covering
|
||||||
-- > -- up desktop panels before the desktop layouts
|
-- > -- up desktop panels before the desktop layouts
|
||||||
@ -129,7 +129,7 @@ import qualified Data.Map as M
|
|||||||
-- To add to the logHook while still sending workspace and window information
|
-- To add to the logHook while still sending workspace and window information
|
||||||
-- to DE apps use something like:
|
-- to DE apps use something like:
|
||||||
--
|
--
|
||||||
-- > , logHook = myLogHook <+> logHook desktopConfig
|
-- > , logHook = myLogHook <> logHook desktopConfig
|
||||||
--
|
--
|
||||||
-- Or for more elaborate logHooks you can use @do@:
|
-- Or for more elaborate logHooks you can use @do@:
|
||||||
--
|
--
|
||||||
@ -143,7 +143,7 @@ import qualified Data.Map as M
|
|||||||
-- To customize xmonad's event handling while still having it respond
|
-- To customize xmonad's event handling while still having it respond
|
||||||
-- to EWMH events from pagers, task bars:
|
-- to EWMH events from pagers, task bars:
|
||||||
--
|
--
|
||||||
-- > , handleEventHook = myEventHooks <+> handleEventHook desktopConfig
|
-- > , handleEventHook = myEventHooks <> handleEventHook desktopConfig
|
||||||
--
|
--
|
||||||
-- or 'mconcat' if you write a list event of event hooks
|
-- or 'mconcat' if you write a list event of event hooks
|
||||||
--
|
--
|
||||||
@ -157,7 +157,7 @@ import qualified Data.Map as M
|
|||||||
|
|
||||||
-- $startupHook
|
-- $startupHook
|
||||||
-- To run the desktop startupHook, plus add further actions to be run each
|
-- To run the desktop startupHook, plus add further actions to be run each
|
||||||
-- time xmonad starts or restarts, use '<+>' to combine actions as in the
|
-- time xmonad starts or restarts, use '<>' to combine actions as in the
|
||||||
-- logHook example, or something like:
|
-- logHook example, or something like:
|
||||||
--
|
--
|
||||||
-- > , startupHook = do
|
-- > , startupHook = do
|
||||||
@ -169,9 +169,9 @@ import qualified Data.Map as M
|
|||||||
desktopConfig :: XConfig (ModifiedLayout AvoidStruts
|
desktopConfig :: XConfig (ModifiedLayout AvoidStruts
|
||||||
(Choose Tall (Choose (Mirror Tall) Full)))
|
(Choose Tall (Choose (Mirror Tall) Full)))
|
||||||
desktopConfig = docks $ ewmh def
|
desktopConfig = docks $ ewmh def
|
||||||
{ startupHook = setDefaultCursor xC_left_ptr <+> startupHook def
|
{ startupHook = setDefaultCursor xC_left_ptr <> startupHook def
|
||||||
, layoutHook = desktopLayoutModifiers $ layoutHook def
|
, layoutHook = desktopLayoutModifiers $ layoutHook def
|
||||||
, keys = desktopKeys <+> keys def }
|
, keys = desktopKeys <> keys def }
|
||||||
|
|
||||||
desktopKeys :: XConfig l -> M.Map (KeyMask, KeySym) (X ())
|
desktopKeys :: XConfig l -> M.Map (KeyMask, KeySym) (X ())
|
||||||
desktopKeys XConfig{modMask = modm} = M.fromList
|
desktopKeys XConfig{modMask = modm} = M.fromList
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
-- boilerplate {{{
|
-- boilerplate {{{
|
||||||
{-# LANGUAGE ExistentialQuantification, NoMonomorphismRestriction, TypeSynonymInstances #-}
|
{-# LANGUAGE ExistentialQuantification, NoMonomorphismRestriction, TypeSynonymInstances, ViewPatterns, LambdaCase #-}
|
||||||
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-type-defaults #-}
|
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-type-defaults #-}
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
@ -7,7 +7,7 @@
|
|||||||
-- Description : Daniel Wagner's xmonad configuration.
|
-- Description : Daniel Wagner's xmonad configuration.
|
||||||
--
|
--
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
module XMonad.Config.Dmwit where
|
module XMonad.Config.Dmwit {-# DEPRECATED "This module contains a personal configuration, to be removed from xmonad-contrib. If you use this module, please copy the relevant parts to your configuration or obtain a copy of it on https://xmonad.org/configurations.html and include it as a local module." #-} where
|
||||||
|
|
||||||
-- system imports
|
-- system imports
|
||||||
import Control.Monad.Trans
|
import Control.Monad.Trans
|
||||||
@ -34,7 +34,7 @@ import XMonad.Layout.Grid
|
|||||||
import XMonad.Layout.IndependentScreens hiding (withScreen)
|
import XMonad.Layout.IndependentScreens hiding (withScreen)
|
||||||
import XMonad.Layout.Magnifier
|
import XMonad.Layout.Magnifier
|
||||||
import XMonad.Layout.NoBorders
|
import XMonad.Layout.NoBorders
|
||||||
import XMonad.Prelude
|
import XMonad.Prelude hiding (fromList)
|
||||||
import XMonad.Util.Dzen hiding (x, y)
|
import XMonad.Util.Dzen hiding (x, y)
|
||||||
import XMonad.Util.SpawnOnce
|
import XMonad.Util.SpawnOnce
|
||||||
-- }}}
|
-- }}}
|
||||||
@ -78,7 +78,7 @@ modVolume kind n = do
|
|||||||
where
|
where
|
||||||
sign | n > 0 = "+" | otherwise = "-"
|
sign | n > 0 = "+" | otherwise = "-"
|
||||||
ctlKind = map (\c -> if c == ' ' then '-' else c) kind
|
ctlKind = map (\c -> if c == ' ' then '-' else c) kind
|
||||||
parseKind = unwords . map (\(c:cs) -> toUpper c : cs) . words $ kind
|
parseKind = unwords . map (\(notEmpty -> c :| cs) -> toUpper c : cs) . words $ kind
|
||||||
setCommand i = "pactl set-" ++ ctlKind ++ "-volume " ++ i ++ " -- " ++ sign ++ show (abs n) ++ "%"
|
setCommand i = "pactl set-" ++ ctlKind ++ "-volume " ++ i ++ " -- " ++ sign ++ show (abs n) ++ "%"
|
||||||
listCommand = "pactl list " ++ ctlKind ++ "s"
|
listCommand = "pactl list " ++ ctlKind ++ "s"
|
||||||
-- }}}
|
-- }}}
|
||||||
@ -217,13 +217,13 @@ dmwitConfig nScreens = docks $ def {
|
|||||||
keys = keyBindings,
|
keys = keyBindings,
|
||||||
layoutHook = magnifierOff $ avoidStruts (GridRatio 0.9) ||| noBorders Full,
|
layoutHook = magnifierOff $ avoidStruts (GridRatio 0.9) ||| noBorders Full,
|
||||||
manageHook = (title =? "CGoban: Main Window" --> doF sinkFocus)
|
manageHook = (title =? "CGoban: Main Window" --> doF sinkFocus)
|
||||||
<+> (className =? "Wine" <&&> (appName =? "hl2.exe" <||> appName =? "portal2.exe") --> ask >>= viewFullOn {-centerWineOn-} 1 "5")
|
<> (className =? "Wine" <&&> (appName =? "hl2.exe" <||> appName =? "portal2.exe") --> ask >>= viewFullOn {-centerWineOn-} 1 "5")
|
||||||
<+> (className =? "VirtualBox" --> ask >>= viewFullOn 1 "5")
|
<> (className =? "VirtualBox" --> ask >>= viewFullOn 1 "5")
|
||||||
<+> (isFullscreen --> doFullFloat) -- TF2 matches the "isFullscreen" criteria, so its manage hook should appear after (e.g., to the left of a <+> compared to) this one
|
<> (isFullscreen --> doFullFloat) -- TF2 matches the "isFullscreen" criteria, so its manage hook should appear after (e.g., to the left of a <> compared to) this one
|
||||||
<+> (appName =? "huludesktop" --> doRectFloat fullscreen43on169)
|
<> (appName =? "huludesktop" --> doRectFloat fullscreen43on169)
|
||||||
<+> fullscreenMPlayer
|
<> fullscreenMPlayer
|
||||||
<+> floatAll ["Gimp", "Wine"]
|
<> floatAll ["Gimp", "Wine"]
|
||||||
<+> manageSpawn,
|
<> manageSpawn,
|
||||||
logHook = allPPs nScreens,
|
logHook = allPPs nScreens,
|
||||||
startupHook = refresh
|
startupHook = refresh
|
||||||
>> mapM_ (spawnOnce . xmobarCommand) [0 .. nScreens-1]
|
>> mapM_ (spawnOnce . xmobarCommand) [0 .. nScreens-1]
|
||||||
@ -308,7 +308,7 @@ allPPs nScreens = sequence_ [dynamicLogWithPP (pp s) | s <- [0..nScreens-1], pp
|
|||||||
color c = xmobarColor c ""
|
color c = xmobarColor c ""
|
||||||
|
|
||||||
ppFocus s@(S s_) = whenCurrentOn s def {
|
ppFocus s@(S s_) = whenCurrentOn s def {
|
||||||
ppOrder = \(_:_:windowTitle:_) -> [windowTitle],
|
ppOrder = \case{ _:_:windowTitle:_ -> [windowTitle]; _ -> [] },
|
||||||
ppOutput = appendFile (pipeName "focus" s_) . (++ "\n")
|
ppOutput = appendFile (pipeName "focus" s_) . (++ "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +318,7 @@ ppWorkspaces s@(S s_) = marshallPP s def {
|
|||||||
ppHiddenNoWindows = color dark,
|
ppHiddenNoWindows = color dark,
|
||||||
ppUrgent = color "red",
|
ppUrgent = color "red",
|
||||||
ppSep = "",
|
ppSep = "",
|
||||||
ppOrder = \(wss:_layout:_title:_) -> [wss],
|
ppOrder = \case{ wss:_layout:_title:_ -> [wss]; _ -> [] },
|
||||||
ppOutput = appendFile (pipeName "workspaces" s_) . (++"\n")
|
ppOutput = appendFile (pipeName "workspaces" s_) . (++"\n")
|
||||||
}
|
}
|
||||||
-- }}}
|
-- }}}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{-# LANGUAGE PatternGuards #-}
|
{-# LANGUAGE PatternGuards #-}
|
||||||
|
{-# LANGUAGE TupleSections #-}
|
||||||
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-orphans #-}
|
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-orphans #-}
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
@ -8,7 +9,7 @@
|
|||||||
-- License : BSD3-style (see LICENSE)
|
-- License : BSD3-style (see LICENSE)
|
||||||
--
|
--
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
module XMonad.Config.Droundy ( config, mytab ) where
|
module XMonad.Config.Droundy {-# DEPRECATED "This module contains a personal configuration, to be removed from xmonad-contrib. If you use this module, please copy the relevant parts to your configuration or obtain a copy of it on https://xmonad.org/configurations.html and include it as a local module." #-} ( config, mytab ) where
|
||||||
|
|
||||||
import XMonad hiding (keys, config)
|
import XMonad hiding (keys, config)
|
||||||
import qualified XMonad (keys)
|
import qualified XMonad (keys)
|
||||||
@ -20,7 +21,7 @@ import System.Exit ( exitSuccess )
|
|||||||
import XMonad.Layout.Tabbed ( tabbed,
|
import XMonad.Layout.Tabbed ( tabbed,
|
||||||
shrinkText, Shrinker, shrinkIt, CustomShrink(CustomShrink) )
|
shrinkText, Shrinker, shrinkIt, CustomShrink(CustomShrink) )
|
||||||
import XMonad.Layout.Combo ( combineTwo )
|
import XMonad.Layout.Combo ( combineTwo )
|
||||||
import XMonad.Layout.Named ( named )
|
import XMonad.Layout.Renamed ( Rename(Replace), renamed )
|
||||||
import XMonad.Layout.LayoutCombinators
|
import XMonad.Layout.LayoutCombinators
|
||||||
import XMonad.Layout.Square ( Square(Square) )
|
import XMonad.Layout.Square ( Square(Square) )
|
||||||
import XMonad.Layout.WindowNavigation ( Navigate(Move,Swap,Go), Direction2D(U,D,R,L),
|
import XMonad.Layout.WindowNavigation ( Navigate(Move,Swap,Go), Direction2D(U,D,R,L),
|
||||||
@ -114,9 +115,9 @@ keys x = M.fromList $
|
|||||||
]
|
]
|
||||||
|
|
||||||
++
|
++
|
||||||
zip (zip (repeat $ modMask x) [xK_F1..xK_F12]) (map (withNthWorkspace W.greedyView) [0..])
|
zip (map (modMask x,) [xK_F1..xK_F12]) (map (withNthWorkspace W.greedyView) [0..])
|
||||||
++
|
++
|
||||||
zip (zip (repeat (modMask x .|. shiftMask)) [xK_F1..xK_F12]) (map (withNthWorkspace copy) [0..])
|
zip (map (modMask x .|. shiftMask,) [xK_F1..xK_F12]) (map (withNthWorkspace copy) [0..])
|
||||||
|
|
||||||
config = docks $ ewmh def
|
config = docks $ ewmh def
|
||||||
{ borderWidth = 1 -- Width of the window border in pixels.
|
{ borderWidth = 1 -- Width of the window border in pixels.
|
||||||
@ -124,10 +125,10 @@ config = docks $ ewmh def
|
|||||||
, layoutHook = showWName $ workspaceDir "~" $
|
, layoutHook = showWName $ workspaceDir "~" $
|
||||||
boringWindows $ smartBorders $ windowNavigation $
|
boringWindows $ smartBorders $ windowNavigation $
|
||||||
maximizeVertical $ toggleLayouts Full $ avoidStruts $
|
maximizeVertical $ toggleLayouts Full $ avoidStruts $
|
||||||
named "tabbed" mytab |||
|
renamed [Replace "tabbed"] mytab |||
|
||||||
named "xclock" (mytab ****//* combineTwo Square mytab mytab) |||
|
renamed [Replace "xclock"] (mytab ****//* combineTwo Square mytab mytab) |||
|
||||||
named "three" (mytab **//* mytab *//* combineTwo Square mytab mytab) |||
|
renamed [Replace "three"] (mytab **//* mytab *//* combineTwo Square mytab mytab) |||
|
||||||
named "widescreen" ((mytab *||* mytab)
|
renamed [Replace "widescreen"] ((mytab *||* mytab)
|
||||||
****//* combineTwo Square mytab mytab) -- |||
|
****//* combineTwo Square mytab mytab) -- |||
|
||||||
--mosaic 0.25 0.5
|
--mosaic 0.25 0.5
|
||||||
, terminal = "xterm" -- The preferred terminal program.
|
, terminal = "xterm" -- The preferred terminal program.
|
||||||
|
@ -28,10 +28,10 @@ main = do
|
|||||||
-- simple overrides:
|
-- simple overrides:
|
||||||
xmonad $ desktopConfig
|
xmonad $ desktopConfig
|
||||||
{ modMask = mod4Mask -- Use the "Win" key for the mod key
|
{ modMask = mod4Mask -- Use the "Win" key for the mod key
|
||||||
, manageHook = myManageHook <+> manageHook desktopConfig
|
, manageHook = myManageHook <> manageHook desktopConfig
|
||||||
, layoutHook = desktopLayoutModifiers myLayouts
|
, layoutHook = desktopLayoutModifiers myLayouts
|
||||||
, logHook = (dynamicLogString def >>= xmonadPropLog)
|
, logHook = (dynamicLogString def >>= xmonadPropLog)
|
||||||
<+> logHook desktopConfig
|
<> logHook desktopConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
`additionalKeysP` -- Add some extra key bindings:
|
`additionalKeysP` -- Add some extra key bindings:
|
||||||
@ -72,7 +72,7 @@ myManageHook = composeOne
|
|||||||
-- Handle floating windows:
|
-- Handle floating windows:
|
||||||
[ transience -- move transient windows to their parent
|
[ transience -- move transient windows to their parent
|
||||||
, isDialog -?> doCenterFloat
|
, isDialog -?> doCenterFloat
|
||||||
] <+> composeAll
|
] <> composeAll
|
||||||
[ className =? "Pidgin" --> doFloat
|
[ className =? "Pidgin" --> doFloat
|
||||||
, className =? "XCalc" --> doFloat
|
, className =? "XCalc" --> doFloat
|
||||||
, className =? "mpv" --> doFloat
|
, className =? "mpv" --> doFloat
|
||||||
|
@ -32,7 +32,7 @@ import qualified Data.Map as M
|
|||||||
import System.Environment (getEnvironment)
|
import System.Environment (getEnvironment)
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- To use this module, start with the following @~\/.xmonad\/xmonad.hs@:
|
-- To use this module, start with the following @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad
|
-- > import XMonad
|
||||||
-- > import XMonad.Config.Gnome
|
-- > import XMonad.Config.Gnome
|
||||||
@ -43,7 +43,7 @@ import System.Environment (getEnvironment)
|
|||||||
|
|
||||||
gnomeConfig = desktopConfig
|
gnomeConfig = desktopConfig
|
||||||
{ terminal = "gnome-terminal"
|
{ terminal = "gnome-terminal"
|
||||||
, keys = gnomeKeys <+> keys desktopConfig
|
, keys = gnomeKeys <> keys desktopConfig
|
||||||
, startupHook = gnomeRegister >> startupHook desktopConfig }
|
, startupHook = gnomeRegister >> startupHook desktopConfig }
|
||||||
|
|
||||||
gnomeKeys XConfig{modMask = modm} = M.fromList
|
gnomeKeys XConfig{modMask = modm} = M.fromList
|
||||||
|
@ -28,7 +28,7 @@ import XMonad.Config.Desktop
|
|||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- To use this module, start with the following @~\/.xmonad\/xmonad.hs@:
|
-- To use this module, start with the following @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad
|
-- > import XMonad
|
||||||
-- > import XMonad.Config.Kde
|
-- > import XMonad.Config.Kde
|
||||||
@ -42,11 +42,11 @@ import qualified Data.Map as M
|
|||||||
|
|
||||||
kdeConfig = desktopConfig
|
kdeConfig = desktopConfig
|
||||||
{ terminal = "konsole"
|
{ terminal = "konsole"
|
||||||
, keys = kdeKeys <+> keys desktopConfig }
|
, keys = kdeKeys <> keys desktopConfig }
|
||||||
|
|
||||||
kde4Config = desktopConfig
|
kde4Config = desktopConfig
|
||||||
{ terminal = "konsole"
|
{ terminal = "konsole"
|
||||||
, keys = kde4Keys <+> keys desktopConfig }
|
, keys = kde4Keys <> keys desktopConfig }
|
||||||
|
|
||||||
kdeKeys XConfig{modMask = modm} = M.fromList
|
kdeKeys XConfig{modMask = modm} = M.fromList
|
||||||
[ ((modm, xK_p), spawn "dcop kdesktop default popupExecuteCommand")
|
[ ((modm, xK_p), spawn "dcop kdesktop default popupExecuteCommand")
|
||||||
|
@ -27,7 +27,7 @@ import XMonad.Config.Desktop
|
|||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- To use this module, start with the following @~\/.xmonad\/xmonad.hs@:
|
-- To use this module, start with the following @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad
|
-- > import XMonad
|
||||||
-- > import XMonad.Config.LXQt
|
-- > import XMonad.Config.LXQt
|
||||||
@ -38,7 +38,7 @@ import qualified Data.Map as M
|
|||||||
|
|
||||||
lxqtConfig = desktopConfig
|
lxqtConfig = desktopConfig
|
||||||
{ terminal = "qterminal"
|
{ terminal = "qterminal"
|
||||||
, keys = lxqtKeys <+> keys desktopConfig }
|
, keys = lxqtKeys <> keys desktopConfig }
|
||||||
|
|
||||||
lxqtKeys XConfig{modMask = modm} = M.fromList
|
lxqtKeys XConfig{modMask = modm} = M.fromList
|
||||||
[ ((modm, xK_p), spawn "lxqt-runner")
|
[ ((modm, xK_p), spawn "lxqt-runner")
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
|
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
|
||||||
|
-- TODO: Remove when we depend on a version of xmonad that has unGrab.
|
||||||
|
{-# OPTIONS_GHC -Wno-deprecations #-}
|
||||||
|
{-# OPTIONS_GHC -Wno-dodgy-imports #-}
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
-- |
|
-- |
|
||||||
-- Module : XMonad.Config.Mate
|
-- Module : XMonad.Config.Mate
|
||||||
@ -28,18 +30,17 @@ module XMonad.Config.Mate (
|
|||||||
desktopLayoutModifiers
|
desktopLayoutModifiers
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import XMonad
|
import System.Environment (getEnvironment)
|
||||||
import XMonad.Config.Desktop
|
|
||||||
import XMonad.Util.Run (safeSpawn)
|
|
||||||
import XMonad.Util.Ungrab
|
|
||||||
import XMonad.Prelude (toUpper)
|
|
||||||
|
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
|
||||||
import System.Environment (getEnvironment)
|
import XMonad hiding (unGrab)
|
||||||
|
import XMonad.Config.Desktop
|
||||||
|
import XMonad.Prelude (toUpper)
|
||||||
|
import XMonad.Util.Run (safeSpawn)
|
||||||
|
import XMonad.Util.Ungrab (unGrab)
|
||||||
|
|
||||||
-- $usage
|
-- $usage
|
||||||
-- To use this module, start with the following @~\/.xmonad\/xmonad.hs@:
|
-- To use this module, start with the following @xmonad.hs@:
|
||||||
--
|
--
|
||||||
-- > import XMonad
|
-- > import XMonad
|
||||||
-- > import XMonad.Config.Mate
|
-- > import XMonad.Config.Mate
|
||||||
@ -50,7 +51,7 @@ import System.Environment (getEnvironment)
|
|||||||
|
|
||||||
mateConfig = desktopConfig
|
mateConfig = desktopConfig
|
||||||
{ terminal = "mate-terminal"
|
{ terminal = "mate-terminal"
|
||||||
, keys = mateKeys <+> keys desktopConfig
|
, keys = mateKeys <> keys desktopConfig
|
||||||
, startupHook = mateRegister >> startupHook desktopConfig }
|
, startupHook = mateRegister >> startupHook desktopConfig }
|
||||||
|
|
||||||
mateKeys XConfig{modMask = modm} = M.fromList
|
mateKeys XConfig{modMask = modm} = M.fromList
|
||||||
|
@ -15,7 +15,7 @@ ideas:
|
|||||||
"only once" features like avoidStruts, ewmhDesktops
|
"only once" features like avoidStruts, ewmhDesktops
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module XMonad.Config.Monad where
|
module XMonad.Config.Monad {-# DEPRECATED "This module does not work." #-} where
|
||||||
|
|
||||||
import XMonad hiding (terminal, keys)
|
import XMonad hiding (terminal, keys)
|
||||||
import qualified XMonad as X
|
import qualified XMonad as X
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
{-# OPTIONS_HADDOCK hide #-}
|
||||||
{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, KindSignatures, UndecidableInstances #-}
|
{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, KindSignatures, UndecidableInstances #-}
|
||||||
|
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
@ -23,7 +24,7 @@
|
|||||||
--
|
--
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
module XMonad.Config.Prime (
|
module XMonad.Config.Prime {-# DEPRECATED "This module is a perpetual draft and will therefore be removed from xmonad-contrib in the near future." #-} (
|
||||||
-- Note: The identifiers here are listed in the order that makes the most sense
|
-- Note: The identifiers here are listed in the order that makes the most sense
|
||||||
-- for a user, while the definitions below are listed in the order that makes
|
-- for a user, while the definitions below are listed in the order that makes
|
||||||
-- the most sense for a developer.
|
-- the most sense for a developer.
|
||||||
@ -126,7 +127,7 @@ import qualified XMonad as X (xmonad, XConfig(..))
|
|||||||
import XMonad.Util.EZConfig (additionalKeysP, additionalMouseBindings, checkKeymap, removeKeysP, removeMouseBindings)
|
import XMonad.Util.EZConfig (additionalKeysP, additionalMouseBindings, checkKeymap, removeKeysP, removeMouseBindings)
|
||||||
|
|
||||||
-- $start_here
|
-- $start_here
|
||||||
-- To start with, create a @~\/.xmonad\/xmonad.hs@ that looks like this:
|
-- To start with, create a @xmonad.hs@ that looks like this:
|
||||||
--
|
--
|
||||||
-- > {-# LANGUAGE RebindableSyntax #-}
|
-- > {-# LANGUAGE RebindableSyntax #-}
|
||||||
-- > import XMonad.Config.Prime
|
-- > import XMonad.Config.Prime
|
||||||
@ -280,7 +281,7 @@ instance SummableClass (Summable x y) y where
|
|||||||
--
|
--
|
||||||
-- Note that operator precedence mandates the parentheses here.
|
-- Note that operator precedence mandates the parentheses here.
|
||||||
manageHook :: Summable ManageHook ManageHook (XConfig l)
|
manageHook :: Summable ManageHook ManageHook (XConfig l)
|
||||||
manageHook = Summable X.manageHook (\x c -> c { X.manageHook = x }) (<+>)
|
manageHook = Summable X.manageHook (\x c -> c { X.manageHook = x }) (<>)
|
||||||
|
|
||||||
-- | Custom X event handler. Return @All True@ if the default handler should
|
-- | Custom X event handler. Return @All True@ if the default handler should
|
||||||
-- also be run afterwards. Default does nothing. To add an event handler:
|
-- also be run afterwards. Default does nothing. To add an event handler:
|
||||||
@ -289,7 +290,7 @@ manageHook = Summable X.manageHook (\x c -> c { X.manageHook = x }) (<+>)
|
|||||||
-- > ...
|
-- > ...
|
||||||
-- > handleEventHook =+ serverModeEventHook
|
-- > handleEventHook =+ serverModeEventHook
|
||||||
handleEventHook :: Summable (Event -> X All) (Event -> X All) (XConfig l)
|
handleEventHook :: Summable (Event -> X All) (Event -> X All) (XConfig l)
|
||||||
handleEventHook = Summable X.handleEventHook (\x c -> c { X.handleEventHook = x }) (<+>)
|
handleEventHook = Summable X.handleEventHook (\x c -> c { X.handleEventHook = x }) (<>)
|
||||||
|
|
||||||
-- | List of workspaces' names. Default: @map show [1 .. 9 :: Int]@. Adding
|
-- | List of workspaces' names. Default: @map show [1 .. 9 :: Int]@. Adding
|
||||||
-- appends to the end:
|
-- appends to the end:
|
||||||
@ -388,7 +389,7 @@ instance RemovableClass MouseBindings [(ButtonMask, Button)] where
|
|||||||
MouseBindings { mRemove = r } =- sadBindings = return . r sadBindings
|
MouseBindings { mRemove = r } =- sadBindings = return . r sadBindings
|
||||||
|
|
||||||
-- | Mouse button bindings to an 'X' actions on a window. Default: see @`man
|
-- | Mouse button bindings to an 'X' actions on a window. Default: see @`man
|
||||||
-- xmonad`@. To make mod-<scrollwheel> switch workspaces:
|
-- xmonad`@. To make @mod-\<scrollwheel\>@ switch workspaces:
|
||||||
--
|
--
|
||||||
-- > import XMonad.Actions.CycleWS (nextWS, prevWS)
|
-- > import XMonad.Actions.CycleWS (nextWS, prevWS)
|
||||||
-- > ...
|
-- > ...
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user