mirror of
https://github.com/xmonad/xmonad-contrib.git
synced 2025-07-26 09:41:52 -07:00
Compare commits
897 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0fde4c8848 | ||
|
991dc6dfac | ||
|
805de214d8 | ||
|
585558bfb0 | ||
|
a7ccfe61f3 | ||
|
16b9f0f96d | ||
|
2d849cc0b7 | ||
|
8b6f17ba66 | ||
|
e1f4f77346 | ||
|
9824b57d12 | ||
|
a335809767 | ||
|
719c8ecfe9 | ||
|
bc5d4f18e2 | ||
|
5d9a599c9f | ||
|
7a4dc29418 | ||
|
c51353c6c6 | ||
|
046f3c3871 | ||
|
8edc8ab789 | ||
|
5cc26aaa21 | ||
|
d6b4f174d6 | ||
|
0be6780559 | ||
|
f39218ddb5 | ||
|
9b840a1189 | ||
|
c71756095e | ||
|
32028915a3 | ||
|
4560abd1e6 | ||
|
2c01279d1c | ||
|
33ccf910a6 | ||
|
268fc70d0e | ||
|
c0cea57604 | ||
|
0f6403c2e9 | ||
|
c2e36da92c | ||
|
0aeaf93a6e | ||
|
b552b453d5 | ||
|
956d51c225 | ||
|
08b8b285a8 | ||
|
b2d4aa7dad | ||
|
839302533b | ||
|
e5b5ce74b2 | ||
|
6ab136eb56 | ||
|
f666cf4e4e | ||
|
860f80a6d3 | ||
|
79b130b9d6 | ||
|
08ec79eec1 | ||
|
3175f276be | ||
|
fe933c3707 | ||
|
6b9520b03b | ||
|
6358683058 | ||
|
3a72dd5355 | ||
|
3dbdc51158 | ||
|
e0c7e35b3d | ||
|
8e0e8a605d | ||
|
53adf02b38 | ||
|
a5b335469a | ||
|
8defd7f4c8 | ||
|
c2f45b49ff | ||
|
45a07b0e1b | ||
|
8b3ed5224a | ||
|
2cbb342adc | ||
|
74f3b6206f | ||
|
76bab07eb9 | ||
|
6ca1e334eb | ||
|
0e3001681d | ||
|
3f2210206f | ||
|
9151492c6e | ||
|
5e7085661e | ||
|
cfe7533c42 | ||
|
ac6705a144 | ||
|
6ddde878ec | ||
|
2932a8e2f8 | ||
|
7ec3a4e034 | ||
|
586416b0f9 | ||
|
087526dee3 | ||
|
0de958e09c | ||
|
fa3536b40b | ||
|
ab98b40034 | ||
|
50996809aa | ||
|
2a9ccf3002 | ||
|
5085e72217 | ||
|
374293c179 | ||
|
3b4a262658 | ||
|
bb5b64a198 | ||
|
929a6a3f6f | ||
|
f821a2ec0c | ||
|
5bdc5993f9 | ||
|
b331821275 | ||
|
2b1febb40b | ||
|
8ef05975c7 | ||
|
f470f18cf0 | ||
|
9ff7f89664 | ||
|
82f0a7ad57 | ||
|
967cc9a869 | ||
|
30c139e298 | ||
|
3058d1ca22 | ||
|
38c11c1e3c | ||
|
4d1f76f7d8 | ||
|
20fdcbad01 | ||
|
7f49fe3b4d | ||
|
732a3b6e75 | ||
|
948c048832 | ||
|
7a2001cfa2 | ||
|
0c6fdf4e75 | ||
|
b79fbf6975 | ||
|
81339f2044 | ||
|
34af6ebed1 | ||
|
4a63999df6 | ||
|
64e67d5749 | ||
|
d8f4f841a0 | ||
|
04eadeb5ed | ||
|
cadb178819 | ||
|
04713d2e9c | ||
|
0935fd26e0 | ||
|
1cab211bf0 | ||
|
a9ad56be11 | ||
|
07040cbd58 | ||
|
8d557c6954 | ||
|
89ecfe72a1 | ||
|
fa00fad1d9 | ||
|
ee40542cb8 | ||
|
09b1dc1a6e | ||
|
5417969522 | ||
|
b4a13e6b1b | ||
|
3e83068e0a | ||
|
a03d58cf6a | ||
|
b6b6616400 | ||
|
efcc424c98 | ||
|
e4b8b1f6f2 | ||
|
b42303aa6f | ||
|
e1a2bad168 | ||
|
8fab380724 | ||
|
7402a7c250 | ||
|
7c7ff1fabd | ||
|
3e43315def | ||
|
6f0b9e3142 | ||
|
fefebd56b2 | ||
|
78373349c2 | ||
|
91995df559 | ||
|
73e4691ba7 | ||
|
8de415743d | ||
|
c5654c47ba | ||
|
97508ac109 | ||
|
ad23988a99 | ||
|
f754b9f926 | ||
|
4c759ff70c | ||
|
97289ff6ca | ||
|
815d0e397b | ||
|
bbd972012e | ||
|
a3aff3b946 | ||
|
af2183b316 | ||
|
6b16e45166 | ||
|
58e2b803a4 | ||
|
03f055fe0d | ||
|
ad58f0a388 | ||
|
220656aab0 | ||
|
8a0a84f1d5 | ||
|
71e57caa8e | ||
|
28fff7c1a2 | ||
|
da2fb360b8 | ||
|
5d0a3de24d | ||
|
747862c1eb | ||
|
23e3decbb2 | ||
|
bbb093d466 | ||
|
27ae4bd2a4 | ||
|
31b12ebb8a | ||
|
fa82db1130 | ||
|
1351f9a931 | ||
|
159adddb3b | ||
|
361a34d797 | ||
|
5fffe03e06 | ||
|
7d4c3e36c9 | ||
|
640388942b | ||
|
e30269fe96 | ||
|
67c9d7fe90 | ||
|
722967cb12 | ||
|
f732082fdc | ||
|
4ddb3e4915 | ||
|
35197c8907 | ||
|
ea990921e2 | ||
|
51394c6e3e | ||
|
5995d6c117 | ||
|
cfc793e94f | ||
|
dea8d9dced | ||
|
1c6e6c808d | ||
|
4b15ea2ecc | ||
|
c1cb3aaa24 | ||
|
5067164d19 | ||
|
c9ca4ce026 | ||
|
5da9b26142 | ||
|
6d184e859c | ||
|
d521d18dde | ||
|
c89730fc32 | ||
|
8899078b00 | ||
|
b6cd47db29 | ||
|
c3b67ab0df | ||
|
4bc0470e62 | ||
|
f76318ce5f | ||
|
5bc0d9d777 | ||
|
84dcc9b716 | ||
|
a49c4066b9 | ||
|
6036151ca7 | ||
|
aa35d6a2f2 | ||
|
26c4fb0f2d | ||
|
5f58fb5cd1 | ||
|
2f6546a8d6 | ||
|
96640f7aae | ||
|
548595ed34 | ||
|
b7dbc277a7 | ||
|
0af9435d58 | ||
|
402d29b306 | ||
|
9f52af27b9 | ||
|
bd5b969d9b | ||
|
b96899afb6 | ||
|
e9334b5268 | ||
|
8c72f77c8e | ||
|
4489ab2147 | ||
|
9af232489d | ||
|
921ee69064 | ||
|
bb71111b75 | ||
|
5230f038b3 | ||
|
30995c41ff | ||
|
eab9a3a58e | ||
|
13b1de27a9 | ||
|
4a6f21604f | ||
|
280964b9f5 | ||
|
90c7621e1f | ||
|
1ff954b4b6 | ||
|
5b28f5ee54 | ||
|
f71095885f | ||
|
dac3acc5dd | ||
|
465044d5ce | ||
|
e1db71c42c | ||
|
f19b3f6de3 | ||
|
086db3123b | ||
|
7d122b2edf | ||
|
ff42434be3 | ||
|
055c4877a1 | ||
|
86522a27b0 | ||
|
1e2e1273b8 | ||
|
b65b83661b | ||
|
12b30c393c | ||
|
24786c6d04 | ||
|
3db9167da4 | ||
|
f0809e5d1d | ||
|
fd5d8267d2 | ||
|
f4673d611b | ||
|
f6b1e5dd88 | ||
|
56cf96cfa9 | ||
|
1427c9484a | ||
|
229d52ff07 | ||
|
5c73845c68 | ||
|
d2f3a8de74 | ||
|
b6e364ce42 | ||
|
fde7f4f8b0 | ||
|
37fc86c5c4 | ||
|
ff391de17d | ||
|
1fabce659f | ||
|
9b933b1f69 | ||
|
17ef2b95db | ||
|
cef324795e | ||
|
43e35952c8 | ||
|
147e83cbd0 | ||
|
52751f47d0 | ||
|
d1a4820b55 | ||
|
96aae28641 | ||
|
bf0e2c18ea | ||
|
10574d9b61 | ||
|
6384cd04a4 | ||
|
0c11288ea0 | ||
|
8d2d34ed6b | ||
|
5c40a928a1 | ||
|
cbd44dd9f9 | ||
|
410469e124 | ||
|
ea09fc2bf4 | ||
|
bf55122a82 | ||
|
5b171640fb | ||
|
ebe2a6284a | ||
|
2fb435724f | ||
|
7ad38d4063 | ||
|
518e96b384 | ||
|
7894bcec5c | ||
|
25ad725e0c | ||
|
6b014e6025 | ||
|
538089db83 | ||
|
673f727206 | ||
|
b51ccc87b8 | ||
|
f81cb24f0a | ||
|
c5781f225e | ||
|
35a32b22d0 | ||
|
3f8c570347 | ||
|
12b17c4935 | ||
|
41ba7fd0d3 | ||
|
82ecde86fe | ||
|
6946bbc48b | ||
|
2c1c96c3e5 | ||
|
19b2665246 | ||
|
bf5dce592f | ||
|
00e7a5c197 | ||
|
2469269119 | ||
|
6ece010c01 | ||
|
02d0b79289 | ||
|
a622c0808f | ||
|
8cdbb5d422 | ||
|
36d06c1c5d | ||
|
975a99d9dd | ||
|
9c93d90970 | ||
|
644f993fef | ||
|
da59f9f360 | ||
|
57674eb545 | ||
|
0f617114c8 | ||
|
8cbe3ecd48 | ||
|
017f79fd7a | ||
|
322e06eed9 | ||
|
aa6d1eb60b | ||
|
4815c42482 | ||
|
4badaa45ed | ||
|
a99c76cce4 | ||
|
79e06cf76a | ||
|
130e675837 | ||
|
6a23836539 | ||
|
226b385729 | ||
|
1b8c3993e2 | ||
|
c17fc2ed65 | ||
|
5d148b53a3 | ||
|
e214c94f0d | ||
|
41df4b4079 | ||
|
1009284a03 | ||
|
1324baa193 | ||
|
07439cc169 | ||
|
6687a5bc40 | ||
|
b1782da37c | ||
|
f49e7d653a | ||
|
336afc82ca | ||
|
52bb28824d | ||
|
1d7abb102f | ||
|
ad1e858bac | ||
|
83aaf0414b | ||
|
c649d314fa | ||
|
78f3ad26ed | ||
|
827ea89b6b | ||
|
a33de7f73a | ||
|
f5464224e2 | ||
|
563aa08fae | ||
|
a05128359a | ||
|
99ea4c23e8 | ||
|
0ebd3a0534 | ||
|
8aefbf8883 | ||
|
7702fa052b | ||
|
828983060d | ||
|
d7ad486a6e | ||
|
344ace492b | ||
|
72548b9206 | ||
|
c5ccb1a7ef | ||
|
780360abf0 | ||
|
78b6df0e69 | ||
|
a668b0f13a | ||
|
c3c033bb91 | ||
|
0efc99ae13 | ||
|
168cb6a6c3 | ||
|
654fa5045c | ||
|
a1c2d144b3 | ||
|
06fd90a5f8 | ||
|
3e11bae4b1 | ||
|
b9913bd4df | ||
|
8e39d22cec | ||
|
4e1b277784 | ||
|
e32e17aa2d | ||
|
2865268a7a | ||
|
f316d52c1c | ||
|
eea41cbf76 | ||
|
5eb3dbd61b | ||
|
42307c2855 | ||
|
fd20202c23 | ||
|
db8e47e0b4 | ||
|
e2eee301e0 | ||
|
902c2bb17d | ||
|
1033818631 | ||
|
7d5426a183 | ||
|
08059e8fb7 | ||
|
9618a0b616 | ||
|
315d1acaea | ||
|
afb6ef8412 | ||
|
2b6075666c | ||
|
b56804d5f2 | ||
|
a0caca5edc | ||
|
835aeaaffb | ||
|
52f6aa2c4b | ||
|
a18a155a8b | ||
|
a000438526 | ||
|
f127cf55f4 | ||
|
c9a9cabcce | ||
|
3aadb487ed | ||
|
bb205e9205 | ||
|
1726bdb67b | ||
|
619d0819af | ||
|
b49ebdf2e0 | ||
|
5fbfcaada0 | ||
|
cd24f84774 | ||
|
06dafe3772 | ||
|
ec14617123 | ||
|
88b9c80618 | ||
|
37fbf24ba7 | ||
|
04be5fc22e | ||
|
74e55421d3 | ||
|
6d42c540a5 | ||
|
c3fa5138f9 | ||
|
46f24bb27e | ||
|
b6bd6f6d9d | ||
|
bebcb605a8 | ||
|
0d93820160 | ||
|
43d68bd451 | ||
|
3c4f42d2da | ||
|
280c1a8ed5 | ||
|
62e9941d3d | ||
|
ab60361c5b | ||
|
e3a13a57e8 | ||
|
63e31ccd8d | ||
|
21fb9dfc43 | ||
|
d32febd60d | ||
|
d05b934cbf | ||
|
cf9388a918 | ||
|
94f1e943d4 | ||
|
f1bd315448 | ||
|
dbaf0e60ce | ||
|
b0f5c69baf | ||
|
577fd81450 | ||
|
fcbccc1df2 | ||
|
7dbedb17d6 | ||
|
47a0f17230 | ||
|
670124e309 | ||
|
e91b0fef82 | ||
|
cb86dd3c61 | ||
|
d9a7f4388a | ||
|
be963c0e93 | ||
|
91010f6eb9 | ||
|
fc7ea97582 | ||
|
c8de3b92af | ||
|
90974fd820 | ||
|
62d65d3cdd | ||
|
feb9306222 | ||
|
8a0151fe77 | ||
|
690e928a1c | ||
|
3df6d2af01 | ||
|
652dd03319 | ||
|
60bf7a4d42 | ||
|
c826d068bc | ||
|
e38274e05c | ||
|
2e2d344d92 | ||
|
9f6c829c94 | ||
|
2bec3175cc | ||
|
c7e9d914e1 | ||
|
289c7e433a | ||
|
8202594b1d | ||
|
9451d81427 | ||
|
3d6844c65f | ||
|
1b327a059a | ||
|
d49f7a49a2 | ||
|
43592c84d4 | ||
|
9e6e521fbb | ||
|
13f21f4704 | ||
|
101d6e89bd | ||
|
90737d6d03 | ||
|
b24a88a3a3 | ||
|
fc60ccb1fb | ||
|
52717dd5fb | ||
|
d70128418b | ||
|
eb752b15d8 | ||
|
a7bb1a776a | ||
|
ebf9561d76 | ||
|
f2993f5a25 | ||
|
ccebeb675d | ||
|
cdc6c6d39c | ||
|
44c575930a | ||
|
b63eb1c283 | ||
|
c3e5c1a9aa | ||
|
747202a214 | ||
|
39a9c041c5 | ||
|
3876f2fc86 | ||
|
18c5498aeb | ||
|
30d6d7ed4c | ||
|
c37737bf73 | ||
|
c6b4e69f39 | ||
|
5990456cc9 | ||
|
00a6e4839a | ||
|
ee4a3a932d | ||
|
0a2e1f7254 | ||
|
f2cfaa3398 | ||
|
4eec511eb8 | ||
|
db972afbc6 | ||
|
b13447b361 | ||
|
f42989112c | ||
|
3c7ec6343a | ||
|
32ffca599e | ||
|
2eb67ed341 | ||
|
c3c0d38b26 | ||
|
000590fa62 | ||
|
227a3e0aa5 | ||
|
e83081b6ef | ||
|
444bdc09f0 | ||
|
48156cafb8 | ||
|
9cff824a24 | ||
|
a98cc194ad | ||
|
081629d39f | ||
|
627e203bcf | ||
|
94670a72be | ||
|
6d1f34cf18 | ||
|
c59805636d | ||
|
08a165df40 | ||
|
6179ed9dbe | ||
|
2691e3a490 | ||
|
5c7e1194d0 | ||
|
42771e4658 | ||
|
d4d78abc4a | ||
|
55747ecced | ||
|
245dac496b | ||
|
cd5b1a1015 | ||
|
320fe8c537 | ||
|
b8ac9804fc | ||
|
67341e30a2 | ||
|
3e6f0f9f51 | ||
|
8b50ee5c9b | ||
|
14393a2cf9 | ||
|
96e3f00a1f | ||
|
03650d5d3f | ||
|
77b047316c | ||
|
0a44981d0f | ||
|
b3316c2e34 | ||
|
404b3f59b4 | ||
|
d0813f0b3a | ||
|
c39a5cf618 | ||
|
9d520dc880 | ||
|
3213925b6b | ||
|
9fe3444374 | ||
|
e06eafbadd | ||
|
a3e06685ef | ||
|
0313b26cd8 | ||
|
ba94d48464 | ||
|
526336ecec | ||
|
f9a226e75a | ||
|
6c83af1c69 | ||
|
71863c735a | ||
|
d293a053e1 | ||
|
b41a5a50e9 | ||
|
3d553ad5e0 | ||
|
b963c3cf9d | ||
|
78df487e4c | ||
|
63a3282133 | ||
|
2809ed105e | ||
|
ef3697c09c | ||
|
555cd7c596 | ||
|
cff344811f | ||
|
88ddd54084 | ||
|
c66345467e | ||
|
06af11a463 | ||
|
6977d0cdfb | ||
|
9b6c098c9c | ||
|
35e794b1b2 | ||
|
8648ea790a | ||
|
0490e77970 | ||
|
d6cc525500 | ||
|
53903b086f | ||
|
6715c75c50 | ||
|
78d526d1dd | ||
|
5240116f3c | ||
|
89646d75fd | ||
|
5e0ef3777a | ||
|
69c1821818 | ||
|
27f03ad9c5 | ||
|
bd9a79cb80 | ||
|
e956fbf3ce | ||
|
6087bdd40d | ||
|
d3f11d041a | ||
|
0cbee237c7 | ||
|
83b005ee79 | ||
|
048b12995a | ||
|
7ef0faa986 | ||
|
69a2886a8b | ||
|
f24788260d | ||
|
6e44ddb57a | ||
|
2e900cc10b | ||
|
f73ebf1da1 | ||
|
84caa46dab | ||
|
5db2589abf | ||
|
b594d97604 | ||
|
89dbdd4767 | ||
|
0e9c865e72 | ||
|
8afd51517c | ||
|
72b5e662de | ||
|
6381a4687f | ||
|
a74ed5f724 | ||
|
317eb23654 | ||
|
e3558bce93 | ||
|
d9ad93a888 | ||
|
8f7f5f0a56 | ||
|
8694656840 | ||
|
a29b1565d4 | ||
|
e52c20f70c | ||
|
95f68e2ca3 | ||
|
d8cfdaf020 | ||
|
2cfa5ef23a | ||
|
d18bcdc165 | ||
|
78796a24f9 | ||
|
8822d2ff51 | ||
|
ce5aae5403 | ||
|
3e7df4911a | ||
|
ae6c658bc4 | ||
|
1400d167ad | ||
|
6079c61063 | ||
|
aa67fa5352 | ||
|
4c0b5330e7 | ||
|
3c1866979d | ||
|
6b22ce17c7 | ||
|
d13a26b11e | ||
|
9976aa3b3c | ||
|
02c6e0b0d4 | ||
|
e504f40f88 | ||
|
6db6854752 | ||
|
14dc3a7f79 | ||
|
f271d59c34 | ||
|
3dc6b44f86 | ||
|
bda1d3356d | ||
|
683344ed25 | ||
|
5140f5b5d0 | ||
|
cd1c1d1d69 | ||
|
9e3d17c1c0 | ||
|
ffae2dcd2f | ||
|
5d2a6d75a8 | ||
|
e042bcce97 | ||
|
b989655cea | ||
|
087076f136 | ||
|
0996e71c7e | ||
|
d5b64c1e5d | ||
|
73eb28f669 | ||
|
aa67439bfe | ||
|
2cc20b4116 | ||
|
45052b984d | ||
|
b2e16d3bf1 | ||
|
3a7399b56a | ||
|
4670ec002f | ||
|
5fd8ff86e9 | ||
|
d4e15cddd1 | ||
|
4f9dec9760 | ||
|
598a40da9c | ||
|
1a085bec43 | ||
|
a69794892b | ||
|
8e12681925 | ||
|
9c09072843 | ||
|
02baee5f7e | ||
|
00d829803d | ||
|
9c4c417936 | ||
|
52c5bd61cb | ||
|
b63159fd00 | ||
|
bbcc7e576f | ||
|
86ef180162 | ||
|
4de529a923 | ||
|
0c9c330295 | ||
|
795be75a58 | ||
|
b5105381bf | ||
|
c5ff88b87b | ||
|
1e73a97500 | ||
|
551ff2b8ac | ||
|
ebfc068b99 | ||
|
dea887d487 | ||
|
8fe0eabaf8 | ||
|
010cd4ff5f | ||
|
957f4518b9 | ||
|
4b65f9eef0 | ||
|
e88e7ee1f0 | ||
|
c5745b6299 | ||
|
f06ee5e1ff | ||
|
db3e5d85b5 | ||
|
aa3939e66b | ||
|
fe027ce358 | ||
|
3661471377 | ||
|
33ad9e526d | ||
|
e2fa1ce8a1 | ||
|
f94984c141 | ||
|
c2dd9b0b7a | ||
|
610fb0e200 | ||
|
53b0edae28 | ||
|
58feba91d9 | ||
|
6e162280b9 | ||
|
1c97ca63c3 | ||
|
2ad0c04447 | ||
|
40b939f7a6 | ||
|
ab04e29e42 | ||
|
51bc4f8c2f | ||
|
07717e5dea | ||
|
e61a408e9d | ||
|
c60d8fc29d | ||
|
15dbfeff32 | ||
|
dcf5fad5f9 | ||
|
4a6bbb63b4 | ||
|
8059d4c38c | ||
|
c7afc2904b | ||
|
04ccc4d972 | ||
|
3dc49721b6 | ||
|
4d6209e17a | ||
|
abfeff5db7 | ||
|
5869af1c56 | ||
|
ba247afd0a | ||
|
e3632cb0b4 | ||
|
9fa69a5603 | ||
|
5c8ff36bcb | ||
|
65036457cc | ||
|
8c39850dc0 | ||
|
5f3edb110e | ||
|
8ba646aec6 | ||
|
22fcf69e83 | ||
|
a861a8f954 | ||
|
24d2f26302 | ||
|
aa3f93afea | ||
|
faf89612aa | ||
|
6c60740f7e | ||
|
4aa6f22899 | ||
|
290d58dd9a | ||
|
89603c4a36 | ||
|
b200d0c735 | ||
|
1d0eaddc25 | ||
|
1fdfb4b8d0 | ||
|
ff0ab6f977 | ||
|
b2446cd633 | ||
|
365988774f | ||
|
08b54edbf9 | ||
|
28e29fa238 | ||
|
0c642c3e9a | ||
|
2149f0a07b | ||
|
14faa12f69 | ||
|
d95dc14970 | ||
|
f8105131e1 | ||
|
57936bf6d9 | ||
|
c201a9e33e | ||
|
dea927d887 | ||
|
0eed434b0e | ||
|
04625d0d04 | ||
|
8b2bd3ae5c | ||
|
252e6e4d00 | ||
|
f7ed56fc6b | ||
|
5880e01ad2 | ||
|
5521b432dd | ||
|
c48ddb08af | ||
|
5aca3fb542 | ||
|
423d70593d | ||
|
53a59d6592 | ||
|
dcf1f3e694 | ||
|
1e3448fc53 | ||
|
eacd20ad00 | ||
|
5493ff190d | ||
|
a3afd6219d | ||
|
c4f64bc7d6 | ||
|
eb38b064a7 | ||
|
fca0929876 | ||
|
f1fc219732 | ||
|
5860864e45 | ||
|
d6244a1069 | ||
|
1a3d58143e | ||
|
28b3e34fd7 | ||
|
52f8c82504 | ||
|
3825c56e1d | ||
|
37a3d08d73 | ||
|
d07f0a9ee4 | ||
|
7bf8544f1c | ||
|
dc2b96d575 | ||
|
d8e496d3f0 | ||
|
a58c1a6071 | ||
|
bcd4dde298 | ||
|
c3bb1cb2e7 | ||
|
30d45f8993 | ||
|
273ae32438 | ||
|
22aebcb26d | ||
|
e8da66e575 | ||
|
0b26ddf489 | ||
|
53b57eba14 | ||
|
f3024e6779 | ||
|
f98095d33e | ||
|
809ba2a8c0 | ||
|
a8d41df92b | ||
|
7100e7db4f | ||
|
6040fe6c8f | ||
|
b87e801872 | ||
|
48e6bb5529 | ||
|
3c7d58b836 | ||
|
6eae27390d | ||
|
b43fdcb57f | ||
|
599c0f8613 | ||
|
6da250003f | ||
|
60101b9a70 | ||
|
5f1fc602ab | ||
|
303f0c24bc | ||
|
8df80e7805 | ||
|
36dba39c44 | ||
|
8847b3b2f6 | ||
|
1b061245af | ||
|
94662bffc6 | ||
|
23102a6d5c | ||
|
728f9bc270 | ||
|
d9856b955a | ||
|
603fc4ccb7 | ||
|
65e7153874 | ||
|
6a0dc1685c | ||
|
41e8708eb5 | ||
|
18979de5f6 | ||
|
5ffb4e7f52 | ||
|
52a3800b96 | ||
|
dd89eae446 | ||
|
6b68ec5c00 | ||
|
db80842e65 | ||
|
4e4856c722 | ||
|
b5eb418fa4 | ||
|
04b32ae021 | ||
|
9c6d1e696f | ||
|
dad911913c | ||
|
b3f5a09673 | ||
|
4ad6ecf892 | ||
|
fcd296e87a | ||
|
fddd5ea1fe | ||
|
c3cee11ad6 | ||
|
22e6d4b017 | ||
|
fcced8991a | ||
|
bfb52a5025 | ||
|
c53436f3a6 | ||
|
66c1977c29 | ||
|
a18395a13c | ||
|
ca69574cfe | ||
|
faa252e309 | ||
|
71fec4f61a | ||
|
0301d6e21b | ||
|
0cfe3a288e | ||
|
84c63abda6 | ||
|
e12511c658 | ||
|
52890f6007 | ||
|
51bc32ea75 | ||
|
cce7f50372 | ||
|
ba9b108a68 | ||
|
e12c047870 | ||
|
2ce876c330 | ||
|
3faa9b2c38 | ||
|
6720eefce7 | ||
|
cf789504e8 | ||
|
deaaf6b177 | ||
|
60b35ff431 | ||
|
cd404eb644 | ||
|
d9f43c78d6 | ||
|
b9603a0e10 | ||
|
09a5fa111c | ||
|
41a2db5563 | ||
|
1706160b14 | ||
|
207d5962e2 | ||
|
c7e02726bf | ||
|
b0d6e0f942 | ||
|
a774168415 | ||
|
17da8bc8ee | ||
|
c2c0585d7e | ||
|
7a6d24712f | ||
|
13260cae58 | ||
|
07f4ce8029 | ||
|
298cdd6114 | ||
|
36356dd8f5 | ||
|
dd905d2603 | ||
|
dda242a459 | ||
|
204524328d | ||
|
81a980823e | ||
|
677e64dcf6 | ||
|
c5c3fec26c | ||
|
59fbcdfba9 | ||
|
778e32305f | ||
|
5334130bf7 | ||
|
aca76956ba | ||
|
6dcc36c904 | ||
|
705494eb4c | ||
|
6b9fb096d6 | ||
|
02278e5bbb | ||
|
4dcc78b59e | ||
|
e7c92bc628 | ||
|
dba402aba4 | ||
|
8ea584cdb9 | ||
|
6ea4ee8fbd | ||
|
f1c7b09656 | ||
|
13e5429dc2 | ||
|
8ec1efd472 | ||
|
337ca60f76 | ||
|
62d161ca4e | ||
|
913183463a | ||
|
92fe5f34ff | ||
|
8c309f87b8 | ||
|
f6f925c823 | ||
|
203e63b055 | ||
|
e814f748b5 | ||
|
ed32ccd080 | ||
|
b1f28e64d7 | ||
|
a5c5c52745 | ||
|
2314dd628a | ||
|
95cd118c9a | ||
|
53481b2269 | ||
|
c1c7c30532 | ||
|
617099badd | ||
|
b05f5f12cf | ||
|
270ca2da23 |
16
.github/ISSUE_TEMPLATE.md
vendored
16
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,12 +1,16 @@
|
||||
### Problem Description
|
||||
|
||||
Describe the problem you are having, what you expect to happen
|
||||
instead, and how to reproduce the problem.
|
||||
Describe the problem you are having and what you expect to happen
|
||||
instead.
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
Give detailed step-by-step instructions on how to reproduce the problem.
|
||||
|
||||
### Configuration File
|
||||
|
||||
Please include the smallest configuration file that reproduces the
|
||||
problem you are experiencing:
|
||||
Please include the smallest _full_ configuration file that reproduces
|
||||
the problem you are experiencing:
|
||||
|
||||
```haskell
|
||||
module Main (main) where
|
||||
@@ -21,4 +25,6 @@ main = xmonad def
|
||||
|
||||
- [ ] I've read [CONTRIBUTING.md](https://github.com/xmonad/xmonad/blob/master/CONTRIBUTING.md)
|
||||
|
||||
- [ ] I tested my configuration with [xmonad-testing](https://github.com/xmonad/xmonad-testing)
|
||||
- I tested my configuration
|
||||
- [ ] With `xmonad` version XXX (commit XXX if using git)
|
||||
- [ ] With `xmonad-contrib` version XXX (commit XXX if using git)
|
||||
|
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -7,6 +7,7 @@ behind them.
|
||||
|
||||
- [ ] I've read [CONTRIBUTING.md](https://github.com/xmonad/xmonad/blob/master/CONTRIBUTING.md)
|
||||
|
||||
- [ ] I tested my changes with [xmonad-testing](https://github.com/xmonad/xmonad-testing)
|
||||
- [ ] I've considered how to best test these changes (property, unit,
|
||||
manually, ...) and concluded: XXX
|
||||
|
||||
- [ ] I updated the `CHANGES.md` file
|
||||
|
115
.github/workflows/haskell-ci-hackage.patch
vendored
Normal file
115
.github/workflows/haskell-ci-hackage.patch
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
Piggy-back on the haskell-ci workflow for automatic releases to Hackage.
|
||||
|
||||
This extends the workflow with two additional triggers:
|
||||
|
||||
* When a release is created on GitHub, a candidate release is uploaded to
|
||||
Hackage and docs are submitted for it as Hackage can't build them itself
|
||||
(https://github.com/haskell/hackage-server/issues/925).
|
||||
|
||||
* To make a final release, the workflow can be triggered manually by entering
|
||||
the correct version number matching the version in the cabal file. This is
|
||||
here because promoting the candidate on Hackage discards the uploaded docs
|
||||
(https://github.com/haskell/hackage-server/issues/70).
|
||||
|
||||
The automation uses a special Hackage user: https://hackage.haskell.org/user/xmonad
|
||||
and each repo (X11, xmonad, xmonad-contrib) has its own HACKAGE_API_KEY token
|
||||
set in GitHub repository secrets.
|
||||
|
||||
--- .github/workflows/haskell-ci.yml.orig
|
||||
+++ .github/workflows/haskell-ci.yml
|
||||
@@ -14,8 +14,17 @@
|
||||
#
|
||||
name: Haskell-CI
|
||||
on:
|
||||
- - push
|
||||
- - pull_request
|
||||
+ push:
|
||||
+ pull_request:
|
||||
+ release:
|
||||
+ types:
|
||||
+ - published
|
||||
+ workflow_dispatch:
|
||||
+ inputs:
|
||||
+ version:
|
||||
+ # releases to Hackage are final and cannot be reverted, thus require
|
||||
+ # manual entry of version as a poor man's mistake avoidance
|
||||
+ description: version (must match version in cabal file)
|
||||
jobs:
|
||||
linux:
|
||||
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
||||
@@ -28,6 +37,7 @@
|
||||
include:
|
||||
- compiler: ghc-9.0.1
|
||||
allow-failure: false
|
||||
+ upload: true
|
||||
- compiler: ghc-8.10.4
|
||||
allow-failure: false
|
||||
- compiler: ghc-8.8.4
|
||||
@@ -171,8 +181,66 @@
|
||||
${CABAL} -vnormal check
|
||||
- name: haddock
|
||||
run: |
|
||||
- $CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
|
||||
+ $CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH --haddock-for-hackage --builddir $GITHUB_WORKSPACE/haddock all
|
||||
- name: unconstrained build
|
||||
run: |
|
||||
rm -f cabal.project.local
|
||||
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
|
||||
+ - name: upload artifacts (sdist)
|
||||
+ if: matrix.upload
|
||||
+ uses: actions/upload-artifact@v2
|
||||
+ with:
|
||||
+ path: ${{ github.workspace }}/sdist/*.tar.gz
|
||||
+ - name: upload artifacts (haddock)
|
||||
+ if: matrix.upload
|
||||
+ uses: actions/upload-artifact@v2
|
||||
+ with:
|
||||
+ path: ${{ github.workspace }}/haddock/*-docs.tar.gz
|
||||
+ - name: hackage upload (candidate)
|
||||
+ if: matrix.upload && github.event_name == 'release'
|
||||
+ run: |
|
||||
+ set -ex
|
||||
+ PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||
+ curl \
|
||||
+ --silent --show-error --fail \
|
||||
+ --header "Accept: text/plain" \
|
||||
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||
+ --form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
||||
+ https://hackage.haskell.org/packages/candidates/
|
||||
+ curl \
|
||||
+ --silent --show-error --fail \
|
||||
+ -X PUT \
|
||||
+ --header "Accept: text/plain" \
|
||||
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||
+ --header "Content-Type: application/x-tar" \
|
||||
+ --header "Content-Encoding: gzip" \
|
||||
+ --data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
||||
+ https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/candidate/docs
|
||||
+ env:
|
||||
+ HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||
+ PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||
+ PACKAGE_VERSION: ${{ github.event.release.tag_name }}
|
||||
+ - name: hackage upload (release)
|
||||
+ if: matrix.upload && github.event_name == 'workflow_dispatch'
|
||||
+ run: |
|
||||
+ set -ex
|
||||
+ PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||
+ curl \
|
||||
+ --silent --show-error --fail \
|
||||
+ --header "Accept: text/plain" \
|
||||
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||
+ --form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
||||
+ https://hackage.haskell.org/packages/
|
||||
+ curl \
|
||||
+ --silent --show-error --fail \
|
||||
+ -X PUT \
|
||||
+ --header "Accept: text/plain" \
|
||||
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||
+ --header "Content-Type: application/x-tar" \
|
||||
+ --header "Content-Encoding: gzip" \
|
||||
+ --data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
||||
+ https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/docs
|
||||
+ env:
|
||||
+ HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||
+ PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||
+ PACKAGE_VERSION: ${{ github.event.inputs.version }}
|
268
.github/workflows/haskell-ci.yml
vendored
Normal file
268
.github/workflows/haskell-ci.yml
vendored
Normal file
@@ -0,0 +1,268 @@
|
||||
# This GitHub workflow config has been generated by a script via
|
||||
#
|
||||
# haskell-ci 'github' 'cabal.project'
|
||||
#
|
||||
# To regenerate the script (for example after adjusting tested-with) run
|
||||
#
|
||||
# haskell-ci regenerate
|
||||
#
|
||||
# For more information, see https://github.com/haskell-CI/haskell-ci
|
||||
#
|
||||
# version: 0.12
|
||||
#
|
||||
# REGENDATA ("0.12",["github","cabal.project"])
|
||||
#
|
||||
name: Haskell-CI
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
# releases to Hackage are final and cannot be reverted, thus require
|
||||
# manual entry of version as a poor man's mistake avoidance
|
||||
description: version (must match version in cabal file)
|
||||
jobs:
|
||||
linux:
|
||||
name: Haskell-CI - Linux - ${{ matrix.compiler }}
|
||||
runs-on: ubuntu-18.04
|
||||
container:
|
||||
image: buildpack-deps:bionic
|
||||
continue-on-error: ${{ matrix.allow-failure }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- compiler: ghc-9.0.1
|
||||
allow-failure: false
|
||||
upload: true
|
||||
- compiler: ghc-8.10.4
|
||||
allow-failure: false
|
||||
- compiler: ghc-8.8.4
|
||||
allow-failure: false
|
||||
- compiler: ghc-8.6.5
|
||||
allow-failure: false
|
||||
- compiler: ghc-8.4.4
|
||||
allow-failure: false
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: apt
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common
|
||||
apt-add-repository -y 'ppa:hvr/ghc'
|
||||
apt-get update
|
||||
apt-get install -y $CC cabal-install-3.4 libx11-dev libxext-dev libxft-dev libxinerama-dev libxrandr-dev libxss-dev
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
- name: Set PATH and environment variables
|
||||
run: |
|
||||
echo "$HOME/.cabal/bin" >> $GITHUB_PATH
|
||||
echo "LANG=C.UTF-8" >> $GITHUB_ENV
|
||||
echo "CABAL_DIR=$HOME/.cabal" >> $GITHUB_ENV
|
||||
echo "CABAL_CONFIG=$HOME/.cabal/config" >> $GITHUB_ENV
|
||||
HCDIR=$(echo "/opt/$CC" | sed 's/-/\//')
|
||||
HCNAME=ghc
|
||||
HC=$HCDIR/bin/$HCNAME
|
||||
echo "HC=$HC" >> $GITHUB_ENV
|
||||
echo "HCPKG=$HCDIR/bin/$HCNAME-pkg" >> $GITHUB_ENV
|
||||
echo "HADDOCK=$HCDIR/bin/haddock" >> $GITHUB_ENV
|
||||
echo "CABAL=/opt/cabal/3.4/bin/cabal -vnormal+nowrap" >> $GITHUB_ENV
|
||||
HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')
|
||||
echo "HCNUMVER=$HCNUMVER" >> $GITHUB_ENV
|
||||
echo "ARG_TESTS=--enable-tests" >> $GITHUB_ENV
|
||||
echo "ARG_BENCH=--enable-benchmarks" >> $GITHUB_ENV
|
||||
echo "HEADHACKAGE=false" >> $GITHUB_ENV
|
||||
echo "ARG_COMPILER=--$HCNAME --with-compiler=$HC" >> $GITHUB_ENV
|
||||
echo "GHCJSARITH=0" >> $GITHUB_ENV
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
- name: env
|
||||
run: |
|
||||
env
|
||||
- name: write cabal config
|
||||
run: |
|
||||
mkdir -p $CABAL_DIR
|
||||
cat >> $CABAL_CONFIG <<EOF
|
||||
remote-build-reporting: anonymous
|
||||
write-ghc-environment-files: never
|
||||
remote-repo-cache: $CABAL_DIR/packages
|
||||
logs-dir: $CABAL_DIR/logs
|
||||
world-file: $CABAL_DIR/world
|
||||
extra-prog-path: $CABAL_DIR/bin
|
||||
symlink-bindir: $CABAL_DIR/bin
|
||||
installdir: $CABAL_DIR/bin
|
||||
build-summary: $CABAL_DIR/logs/build.log
|
||||
store-dir: $CABAL_DIR/store
|
||||
install-dirs user
|
||||
prefix: $CABAL_DIR
|
||||
repository hackage.haskell.org
|
||||
url: http://hackage.haskell.org/
|
||||
EOF
|
||||
cat $CABAL_CONFIG
|
||||
- name: versions
|
||||
run: |
|
||||
$HC --version || true
|
||||
$HC --print-project-git-commit-id || true
|
||||
$CABAL --version || true
|
||||
- name: update cabal index
|
||||
run: |
|
||||
$CABAL v2-update -v
|
||||
- name: cache (tools)
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
key: ${{ runner.os }}-${{ matrix.compiler }}-tools-6c71d9d3
|
||||
path: ~/.haskell-ci-tools
|
||||
- name: install cabal-plan
|
||||
run: |
|
||||
mkdir -p $HOME/.cabal/bin
|
||||
curl -sL https://github.com/haskell-hvr/cabal-plan/releases/download/v0.6.2.0/cabal-plan-0.6.2.0-x86_64-linux.xz > cabal-plan.xz
|
||||
echo 'de73600b1836d3f55e32d80385acc055fd97f60eaa0ab68a755302685f5d81bc cabal-plan.xz' | sha256sum -c -
|
||||
xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan
|
||||
rm -f cabal-plan.xz
|
||||
chmod a+x $HOME/.cabal/bin/cabal-plan
|
||||
cabal-plan --version
|
||||
- name: 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
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: source
|
||||
- name: initial cabal.project for sdist
|
||||
run: |
|
||||
touch cabal.project
|
||||
echo "packages: $GITHUB_WORKSPACE/source/." >> cabal.project
|
||||
cat cabal.project
|
||||
- name: sdist
|
||||
run: |
|
||||
mkdir -p sdist
|
||||
$CABAL sdist all --output-dir $GITHUB_WORKSPACE/sdist
|
||||
- name: unpack
|
||||
run: |
|
||||
mkdir -p unpacked
|
||||
find sdist -maxdepth 1 -type f -name '*.tar.gz' -exec tar -C $GITHUB_WORKSPACE/unpacked -xzvf {} \;
|
||||
- name: generate cabal.project
|
||||
run: |
|
||||
PKGDIR_xmonad_contrib="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/xmonad-contrib-[0-9.]*')"
|
||||
echo "PKGDIR_xmonad_contrib=${PKGDIR_xmonad_contrib}" >> $GITHUB_ENV
|
||||
touch cabal.project
|
||||
touch cabal.project.local
|
||||
echo "packages: ${PKGDIR_xmonad_contrib}" >> cabal.project
|
||||
echo "package xmonad-contrib" >> cabal.project
|
||||
echo " ghc-options: -Werror=missing-methods" >> cabal.project
|
||||
cat >> cabal.project <<EOF
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/xmonad/xmonad
|
||||
branch: master
|
||||
|
||||
optimization: False
|
||||
|
||||
package xmonad-contrib
|
||||
flags: +pedantic
|
||||
ghc-options: -j
|
||||
EOF
|
||||
$HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: $_ installed\n" unless /^(xmonad-contrib)$/; }' >> cabal.project.local
|
||||
cat cabal.project
|
||||
cat cabal.project.local
|
||||
- name: dump install plan
|
||||
run: |
|
||||
$CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all
|
||||
cabal-plan
|
||||
- name: cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
|
||||
path: ~/.cabal/store
|
||||
restore-keys: ${{ runner.os }}-${{ matrix.compiler }}-
|
||||
- name: install dependencies
|
||||
run: |
|
||||
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --dependencies-only -j2 all
|
||||
$CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dependencies-only -j2 all
|
||||
- name: build w/o tests
|
||||
run: |
|
||||
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
|
||||
- name: build
|
||||
run: |
|
||||
$CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --write-ghc-environment-files=always
|
||||
- name: tests
|
||||
run: |
|
||||
$CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct
|
||||
- name: hlint
|
||||
run: |
|
||||
if [ $((HCNUMVER >= 90000)) -ne 0 ] ; then (cd ${PKGDIR_xmonad_contrib} && hlint -h ${GITHUB_WORKSPACE}/source/.hlint.yaml .) ; fi
|
||||
- name: cabal check
|
||||
run: |
|
||||
cd ${PKGDIR_xmonad_contrib} || false
|
||||
${CABAL} -vnormal check
|
||||
- name: haddock
|
||||
run: |
|
||||
$CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH --haddock-for-hackage --builddir $GITHUB_WORKSPACE/haddock all
|
||||
- name: unconstrained build
|
||||
run: |
|
||||
rm -f cabal.project.local
|
||||
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
|
||||
- name: upload artifacts (sdist)
|
||||
if: matrix.upload
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: ${{ github.workspace }}/sdist/*.tar.gz
|
||||
- name: upload artifacts (haddock)
|
||||
if: matrix.upload
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: ${{ github.workspace }}/haddock/*-docs.tar.gz
|
||||
- name: hackage upload (candidate)
|
||||
if: matrix.upload && github.event_name == 'release'
|
||||
run: |
|
||||
set -ex
|
||||
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||
curl \
|
||||
--silent --show-error --fail \
|
||||
--header "Accept: text/plain" \
|
||||
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||
--form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
||||
https://hackage.haskell.org/packages/candidates/
|
||||
curl \
|
||||
--silent --show-error --fail \
|
||||
-X PUT \
|
||||
--header "Accept: text/plain" \
|
||||
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||
--header "Content-Type: application/x-tar" \
|
||||
--header "Content-Encoding: gzip" \
|
||||
--data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
||||
https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/candidate/docs
|
||||
env:
|
||||
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||
PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||
PACKAGE_VERSION: ${{ github.event.release.tag_name }}
|
||||
- name: hackage upload (release)
|
||||
if: matrix.upload && github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
set -ex
|
||||
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
|
||||
curl \
|
||||
--silent --show-error --fail \
|
||||
--header "Accept: text/plain" \
|
||||
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||
--form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
|
||||
https://hackage.haskell.org/packages/
|
||||
curl \
|
||||
--silent --show-error --fail \
|
||||
-X PUT \
|
||||
--header "Accept: text/plain" \
|
||||
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
|
||||
--header "Content-Type: application/x-tar" \
|
||||
--header "Content-Encoding: gzip" \
|
||||
--data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
|
||||
https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/docs
|
||||
env:
|
||||
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
|
||||
PACKAGE_NAME: ${{ github.event.repository.name }}
|
||||
PACKAGE_VERSION: ${{ github.event.inputs.version }}
|
28
.github/workflows/nix.yml
vendored
Normal file
28
.github/workflows/nix.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Nix
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Nix Flake - Linux
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v13
|
||||
with:
|
||||
install_url: https://nixos-nix-install-tests.cachix.org/serve/i6laym9jw3wg9mw6ncyrk6gjx4l34vvx/install
|
||||
install_options: '--tarball-url-prefix https://nixos-nix-install-tests.cachix.org/serve'
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Clone project
|
||||
uses: actions/checkout@v2
|
||||
- name: Build
|
||||
# "nix build" builds with full optimization and includes a profiling
|
||||
# build, so just the build of xmonad-contrib itself takes 3 minutes.
|
||||
# As a workaround, we invoke cabal manually here.
|
||||
run: nix develop -c cabal v2-build -O0 -j
|
42
.github/workflows/packdeps.yml
vendored
Normal file
42
.github/workflows/packdeps.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Packdeps
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Run every Saturday
|
||||
- cron: '0 3 * * 6'
|
||||
|
||||
jobs:
|
||||
packdeps:
|
||||
name: Packdeps
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Clone project
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Haskell
|
||||
uses: haskell/actions/setup@v1
|
||||
with:
|
||||
# packdeps doesn't build with newer as of 2021-10
|
||||
ghc-version: '8.8'
|
||||
- name: Install packdeps
|
||||
run: |
|
||||
set -ex
|
||||
echo "$HOME/.cabal/bin" >> $GITHUB_PATH
|
||||
cabal install packdeps
|
||||
- name: Check package bounds (all)
|
||||
continue-on-error: true
|
||||
run: |
|
||||
set -ex
|
||||
packdeps \
|
||||
--exclude X11 \
|
||||
--exclude xmonad \
|
||||
*.cabal
|
||||
- name: Check package bounds (preferred)
|
||||
run: |
|
||||
set -ex
|
||||
packdeps \
|
||||
--preferred \
|
||||
--exclude X11 \
|
||||
--exclude xmonad \
|
||||
*.cabal
|
113
.github/workflows/stack.yml
vendored
Normal file
113
.github/workflows/stack.yml
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
name: Stack
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Stack CI - Linux - ${{ matrix.resolver }} - ${{ matrix.yaml }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# XXX: temporarily disabled until xmonad 0.17 release
|
||||
# - resolver: lts-12
|
||||
# ghc: 8.4.4
|
||||
# yaml: stack.yaml
|
||||
- resolver: lts-12
|
||||
ghc: 8.4.4
|
||||
yaml: stack-master.yaml
|
||||
- resolver: lts-14
|
||||
ghc: 8.6.5
|
||||
yaml: stack-master.yaml
|
||||
- resolver: lts-16
|
||||
ghc: 8.8.4
|
||||
yaml: stack-master.yaml
|
||||
# - resolver: lts-17
|
||||
# ghc: 8.10.4
|
||||
# yaml: stack.yaml
|
||||
- resolver: lts-17
|
||||
ghc: 8.10.4
|
||||
yaml: stack-master.yaml
|
||||
- resolver: lts-18
|
||||
ghc: 8.10.7
|
||||
yaml: stack-master.yaml
|
||||
|
||||
steps:
|
||||
- name: Clone project
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Prepare apt sources
|
||||
run: |
|
||||
set -ex
|
||||
sudo add-apt-repository -y ppa:hvr/ghc
|
||||
sudo apt update -y
|
||||
|
||||
- name: Install C dependencies
|
||||
run: |
|
||||
set -ex
|
||||
sudo apt install -y \
|
||||
libx11-dev \
|
||||
libxext-dev \
|
||||
libxft-dev \
|
||||
libxinerama-dev \
|
||||
libxrandr-dev \
|
||||
libxss-dev \
|
||||
#
|
||||
|
||||
- name: Install GHC
|
||||
# use system ghc (if available) in stack, don't waste GH Actions cache space
|
||||
continue-on-error: true
|
||||
run: |
|
||||
set -ex
|
||||
sudo apt install -y ghc-${{ matrix.ghc }}
|
||||
echo /opt/ghc/${{ matrix.ghc }}/bin >> $GITHUB_PATH
|
||||
|
||||
- name: Refresh caches once a month
|
||||
id: cache-date
|
||||
# GHA writes caches on the first miss and then never updates them again;
|
||||
# force updating the cache at least once a month
|
||||
run: |
|
||||
echo "::set-output name=date::$(date +%Y-%m)"
|
||||
|
||||
- name: Cache Haskell package metadata
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.stack/pantry
|
||||
key: stack-pantry-${{ runner.os }}-${{ steps.cache-date.outputs.date }}
|
||||
restore-keys: |
|
||||
stack-pantry-${{ runner.os }}-
|
||||
|
||||
- name: Cache Haskell dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.stack/*
|
||||
!~/.stack/pantry
|
||||
!~/.stack/programs
|
||||
key: stack-${{ runner.os }}-${{ matrix.resolver }}-${{ steps.cache-date.outputs.date }}-${{ hashFiles(matrix.yaml) }}-${{ hashFiles('*.cabal') }}
|
||||
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 }}-
|
||||
stack-${{ runner.os }}-${{ matrix.resolver }}-
|
||||
|
||||
- name: Update hackage index
|
||||
# always update index to prevent the shared ~/.stack/pantry cache from being empty
|
||||
run: |
|
||||
set -ex
|
||||
stack update
|
||||
|
||||
- name: Build and test
|
||||
run: |
|
||||
set -ex
|
||||
|
||||
# workaround for stack/pantry caching of github archives
|
||||
sed -e "s/@{today}/@{$(date -u --iso-8601=seconds)}/" -i ${{ matrix.yaml }}
|
||||
|
||||
stack test \
|
||||
--fast --no-terminal \
|
||||
--stack-yaml=${{ matrix.yaml }} \
|
||||
--resolver=${{ matrix.resolver }} --system-ghc \
|
||||
--flag=xmonad-contrib:pedantic
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -24,3 +24,6 @@ tags
|
||||
# stack artifacts
|
||||
/.stack-work/
|
||||
/cabal.project.local
|
||||
|
||||
stack.yaml.lock
|
||||
|
||||
|
2
.hlint.yaml
Normal file
2
.hlint.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
# Ignore these warnings.
|
||||
- ignore: {name: "Evaluate"}
|
56
.mailmap
56
.mailmap
@@ -13,90 +13,96 @@ Bogdan Sinitsyn <bogdan.sinitsyn@gmail.com>
|
||||
Brandon S Allbery KF8NH <allbery.b@gmail.com>
|
||||
Brandon S Allbery KF8NH <allbery.b@gmail.com> <allbery@ece.cmu.edu>
|
||||
Brent Yorgey <byorgey@gmail.com> <byorgey@cis.upenn.edu>
|
||||
Bruce Forte <fuererb@student.ethz.ch>
|
||||
Carlos Lopez-Camey <c.lopez@kmels.net>
|
||||
Carsten Otto <xmonad@c-otto.de>
|
||||
Cesar Crusius <ccrusius@google.com> <ccrusius@ccrusius-glaptop.roam.corp.google.com>
|
||||
Christian Dietrich <stettberger@dokucode.de>
|
||||
Christian Wills <cwills.dev@gmail.com>
|
||||
Daniel Neri <daniel.neri@sigicom.com> <daniel.neri@sigicom.se>
|
||||
Daniel Schoepe <daniel.schoepe@googlemail.com> <asgaroth_@gmx.de>
|
||||
Daniel Schoepe <daniel.schoepe@googlemail.com> <daniel.schoepe@gmail.com>
|
||||
Daniel Schoepe <daniel.schoepe@gmail.com> <asgaroth_@gmx.de>
|
||||
Daniel Schoepe <daniel.schoepe@gmail.com> <daniel.schoepe@googlemail.com>
|
||||
Daniel Wagner <me@dmwit.com> <daniel@wagner-home.com>
|
||||
Dave Harrison <dave@nullcube.com>
|
||||
Dave Macias <davama@gmail.com>
|
||||
David Glasser <glasser@mit.edu>
|
||||
David McLean <gopsychonauts@gmail.com>
|
||||
Devin Mullins <devin.mullins@gmail.com> <devinmullins@gmail.com>
|
||||
Devin Mullins <devin.mullins@gmail.com> <me@twifkak.com>
|
||||
Dominik Bruhn <dominik@dbruhn.de>
|
||||
Don Stewart <dons00@gmail.com> <dons@cse.unsw.edu.au>
|
||||
Don Stewart <dons00@gmail.com> <dons@galois.com>
|
||||
Edward Z. Yang <ezyang@cs.stanford.edu>
|
||||
Evgeny Kurnevsky <kurnevsky@gmail.com>
|
||||
Gwern Branwen <gwern@gwern.net>
|
||||
Gwern Branwen <gwern@gwern.net> <gwern0@gmail.com>
|
||||
Henrique Abreu <hgabreu@gmail.com>
|
||||
Ilya Portnov <portnov84@rambler.ru>
|
||||
intrigeri <intrigeri@boum.org>
|
||||
Ivan Brennen <ivan.brennan@gmail.com>
|
||||
Ivan Brennen <ivan.brennan@gmail.com> <ivanbrennan@users.noreply.github.com>
|
||||
Ivan Miljenovic <Ivan.Miljenovic@gmail.com>
|
||||
Jan-David Quesel <quesel@informatik.uni-oldenburg.de>
|
||||
Jens Petersen <juhp@community.haskell.org> <petersen@haskell.org>
|
||||
Jeremy Apthorp <nornagon@gmail.com>
|
||||
Joachim Breitner <mail@joachim-breitner.de>
|
||||
Joachim Fasting <joachim.fasting@gmail.com>
|
||||
Joel Suovaniemi <joel.suovaniemi@iki.fi>
|
||||
Joan Milev <joantmilev@gmail.com> <51526053+exorcist365@users.noreply.github.com>
|
||||
Joe Thornber <joe.thornber@gmail.com>
|
||||
Joel Suovaniemi <joel.suovaniemi@iki.fi>
|
||||
Johann Giwer <johanngiwer@web.de>
|
||||
Jussi Maki <joamaki@gmail.com>
|
||||
Konstantin Sobolev <konstantin.sobolev@gmail.com>
|
||||
L. S. Leary <LSLeary@users.noreply.github.com>
|
||||
Lanny Ripple <lan3ny@gmail.com>
|
||||
Lei Chen <linxray@gmail.com>
|
||||
Leon Kowarschick <lkowarschick@gmail.com>
|
||||
Leon Kowarschick <lkowarschick@gmail.com> <5300871+elkowar@users.noreply.github.com>
|
||||
Leonardo Serra <leoserra@minaslivre.org>
|
||||
Luc Duzan <lduzan@linagora.com>
|
||||
Luc Duzan <lduzan@linagora.com> <stroky.l@gmail.com>
|
||||
Luis Cabellos <zhen.sydow@gmail.com>
|
||||
Lukas Mai <l.mai@web.de>
|
||||
Mario Pastorelli <pastorelli.mario@gmail.com>
|
||||
Mathias Stearn <redbeard0531@gmail.com>
|
||||
Matt Brown <deadguysfrom@gmail.com>
|
||||
Matthew Hague <matthewhague@zoho.com>
|
||||
Michael G. Sloan <mgsloan@gmail.com>
|
||||
Nathaniel Filardo <nwfilardo@gmail.com>
|
||||
Nelson Elhage <nelhage@mit.edu>
|
||||
Nicolas Dudebout <nicolas.dudebout@gatech.edu>
|
||||
Nicolas Pouillard <nicolas.pouillard@gmail.com>
|
||||
Nils Schweinsberg <mail@n-sch.de>
|
||||
Norbert Zeh <nzeh@cs.dal.ca>
|
||||
Peter J. Jones <pjones@devalot.com>
|
||||
Peter J. Jones <pjones@devalot.com> <pjones@pmade.com>
|
||||
Peter Olson <polson2@hawk.iit.edu>
|
||||
Quentin Moser <moserq@gmail.com>
|
||||
Quentin Moser <quentin.moser@unifr.ch>
|
||||
Quentin Moser <moserq@gmail.com> <quentin.moser@unifr.ch>
|
||||
Rickard Gustafsson <acura@allyourbase.se>
|
||||
Robert Marlow <bobstopper@bobturf.org>
|
||||
Robert Marlow <bobstopper@bobturf.org> <robreim@bobturf.org>
|
||||
Rohan Jain <crodjer@gmail.com>
|
||||
Sibi Prabakaran <sibi@psibi.in> <psibi2000@gmail.com>
|
||||
Sean Escriva <sean.escriva@gmail.com>
|
||||
Sean McEligot <seanmce33@gmail.com>
|
||||
Sibi Prabakaran <sibi@psibi.in>
|
||||
Spencer Janssen <spencerjanssen@gmail.com> <sjanssen@cse.unl.edu>
|
||||
Tomohiro Matsuyama <matsuyama3@ariel-networks.com>
|
||||
Timothy Hobbs <tim.thelion@gmail.com>
|
||||
Tom Rauchenwald <its.sec@gmx.net>
|
||||
Tom Smeets <tom.tsmeets@gmail.com> <Tom.TSmeets@Gmail.com>
|
||||
Tomas Janousek <tomi@nomi.cz>
|
||||
Tomohiro Matsuyama <matsuyama3@ariel-networks.com>
|
||||
Tony Morris <haskell@tmorris.net>
|
||||
Valery V. Vorotyntsev <valery.vv@gmail.com>
|
||||
Will Farrington <wcfarrington@gmail.com>
|
||||
Wirt Wolff <wirtwolff@gmail.com>
|
||||
Yaakov Nemoy <loupgaroublond@gmail.com>
|
||||
Yclept Nemo <orbisvicis@gmail.com> <pscjtwjdjtAhnbjm/dpn>
|
||||
Yecine Megdiche <yecine.megdiche@gmail.com> <megdiche@in.tum.de>
|
||||
|
||||
brian <brian@lorf.org>
|
||||
cardboard42 <cardboard42@gmail.com>
|
||||
daedalusinfinity <daedalusinfinity@gmail.com>
|
||||
hexago.nl <xmonad-contrib@hexago.nl>
|
||||
intrigeri <intrigeri@boum.org>
|
||||
jakob <jakob@pipefour.org>
|
||||
kedals0 <kedals0@gmail.com>
|
||||
lithis <xmonad@selg.hethrael.org>
|
||||
lithis <xmonad@selg.hethrael.org> <xmonad@s001.hethrael.com>
|
||||
longpoke <longpoke@gmail.com>
|
||||
md143rbh7f <md143rbh7f@gmail.com>
|
||||
perlkat <perlkat@katspace.org>
|
||||
rupa <rupa@lrrr.us> <rupa@lrrr.us>
|
||||
timthelion <tim.thelion@gmail.com>
|
||||
|
||||
# for core only
|
||||
Neil Mitchell <http://www.cs.york.ac.uk/~ndm/>, Neil Mitchell
|
||||
Nick Burlett <nickburlett@mac.com>
|
||||
Sam Hughes <hughes@rpi.edu>
|
||||
Shae Erisson <shae@ScannedInAvian.com>
|
||||
Conrad Irwin <conrad.irwin@gmail.com>
|
||||
sam-barr <mail@samf.bar> <samfbarr@outlook.com>
|
||||
slotThe <soliditsallgood@mailbox.org> <50166980+slotThe@users.noreply.github.com>
|
||||
slotThe <soliditsallgood@mailbox.org> <soliditsallgood@tuta.io>
|
||||
spoonm <spoonm@spoonm.org>
|
||||
|
104
.travis.yml
104
.travis.yml
@@ -1,104 +0,0 @@
|
||||
# This file has been generated -- see https://github.com/hvr/multi-ghc-travis
|
||||
language: c
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cabsnap
|
||||
- $HOME/.cabal/packages
|
||||
|
||||
before_cache:
|
||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
|
||||
- rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- env: GHCVER=8.4.3 CABALVER=2.2
|
||||
compiler: ": #GHC 8.4.3"
|
||||
addons: { apt: { packages: [cabal-install-2.2, ghc-8.4.3, libxrandr-dev]
|
||||
, sources: [hvr-ghc]
|
||||
} }
|
||||
- env: GHCVER=8.2.2 CABALVER=2.0
|
||||
compiler: ": #GHC 8.2.2"
|
||||
addons: { apt: { packages: [cabal-install-2.0, ghc-8.2.2, libxrandr-dev]
|
||||
, sources: [hvr-ghc]
|
||||
} }
|
||||
- env: GHCVER=8.0.1 CABALVER=1.24
|
||||
compiler: ": #GHC 8.0.1"
|
||||
addons: { apt: { packages: [cabal-install-1.24, ghc-8.0.1, libxrandr-dev]
|
||||
, sources: [hvr-ghc]
|
||||
} }
|
||||
- env: GHCVER=7.10.3 CABALVER=1.22
|
||||
compiler: ": #GHC 7.10.3"
|
||||
addons: { apt: { packages: [cabal-install-1.22, ghc-7.10.3, libxrandr-dev]
|
||||
, sources: [hvr-ghc]
|
||||
} }
|
||||
- env: GHCVER=7.8.4 CABALVER=1.18
|
||||
compiler: ": #GHC 7.8.4"
|
||||
addons: { apt: { packages: [cabal-install-1.18, ghc-7.8.4, libxrandr-dev]
|
||||
, sources: [hvr-ghc]
|
||||
} }
|
||||
|
||||
before_install:
|
||||
- unset CC
|
||||
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
||||
|
||||
install:
|
||||
- cabal --version
|
||||
- echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
|
||||
- if [ -f $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz ];
|
||||
then
|
||||
zcat $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz >
|
||||
$HOME/.cabal/packages/hackage.haskell.org/00-index.tar;
|
||||
fi
|
||||
- travis_retry cabal update -v
|
||||
|
||||
# build xmonad from HEAD
|
||||
- git clone https://github.com/xmonad/xmonad.git
|
||||
- cabal install xmonad/
|
||||
|
||||
- sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
|
||||
- cabal install --only-dependencies --enable-tests --enable-benchmarks --dry -v > installplan.txt
|
||||
- sed -i -e '1,/^Resolving /d' installplan.txt; cat installplan.txt
|
||||
|
||||
# check whether current requested install-plan matches cached package-db snapshot
|
||||
- if diff -u $HOME/.cabsnap/installplan.txt installplan.txt;
|
||||
then
|
||||
echo "cabal build-cache HIT";
|
||||
rm -rfv .ghc;
|
||||
cp -a $HOME/.cabsnap/ghc $HOME/.ghc;
|
||||
cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/;
|
||||
else
|
||||
echo "cabal build-cache MISS";
|
||||
rm -rf $HOME/.cabsnap;
|
||||
mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin;
|
||||
fi
|
||||
- cabal install --only-dependencies --enable-tests --enable-benchmarks;
|
||||
|
||||
# snapshot package-db on cache miss
|
||||
- if [ ! -d $HOME/.cabsnap ];
|
||||
then
|
||||
echo "snapshotting package-db to build-cache";
|
||||
mkdir $HOME/.cabsnap;
|
||||
cp -a $HOME/.ghc $HOME/.cabsnap/ghc;
|
||||
cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;
|
||||
fi
|
||||
|
||||
# Here starts the actual work to be performed for the package under test;
|
||||
# any command which exits with a non-zero exit code causes the build to fail.
|
||||
script:
|
||||
- if [ -f configure.ac ]; then autoreconf -i; fi
|
||||
- cabal configure --enable-tests --enable-benchmarks -v2 # -v2 provides useful information for debugging
|
||||
- cabal build # this builds all libraries and executables (including tests/benchmarks)
|
||||
- cabal test
|
||||
# - cabal check # complains about -Werror even though it is
|
||||
# hidden behind a manual flag with default false
|
||||
- cabal sdist # tests that a source-distribution can be generated
|
||||
|
||||
# Check that the resulting source distribution can be built & installed.
|
||||
# If there are no other `.tar.gz` files in `dist`, this can be even simpler:
|
||||
# `cabal install --force-reinstalls dist/*-*.tar.gz`
|
||||
- SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&
|
||||
(cd dist && cabal install --force-reinstalls "$SRC_TGZ")
|
||||
|
||||
# EOF
|
945
CHANGES.md
945
CHANGES.md
@@ -1,5 +1,931 @@
|
||||
# Change Log / Release Notes
|
||||
|
||||
## 0.17.0 (October 27, 2021)
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* All modules that export bitmap fonts as their default
|
||||
|
||||
- If xmonad is compiled with XFT support (the default), use an XFT
|
||||
font instead. The previous default expected an X11 misc font
|
||||
(PCF), which is not supported in pango 1.44 anymore and thus some
|
||||
distributions have stopped shipping these.
|
||||
|
||||
This fixes the silent `user error (createFontSet)`; this would
|
||||
break the respective modules.
|
||||
|
||||
* `XMonad.Prompt`
|
||||
|
||||
- Now `mkComplFunFromList` and `mkComplFunFromList'` take an
|
||||
additional `XPConfig` argument, so that they can take into
|
||||
account the given `searchPredicate`.
|
||||
|
||||
- A `complCaseSensitivity` field has been added to `XPConfig`, indicating
|
||||
whether case-sensitivity is desired when performing completion.
|
||||
|
||||
- `historyCompletion` and `historyCompletionP` now both have an `X`
|
||||
constraint (was: `IO`), due to changes in how the xmonad core handles XDG
|
||||
directories.
|
||||
|
||||
- The prompt window now sets a `WM_CLASS` property. This allows
|
||||
other applications, like compositors, to properly match on it.
|
||||
|
||||
* `XMonad.Hooks.EwmhDesktops`
|
||||
|
||||
- It is no longer recommended to use `fullscreenEventHook` directly.
|
||||
Instead, use `ewmhFullscreen` which additionally advertises fullscreen
|
||||
support in `_NET_SUPPORTED` and fixes fullscreening of applications that
|
||||
explicitly check it, e.g. mupdf-gl, sxiv, …
|
||||
|
||||
`XMonad.Layout.Fullscreen.fullscreenSupport` now advertises it as well,
|
||||
and no configuration changes are required in this case.
|
||||
|
||||
- Deprecated `ewmhDesktopsLogHookCustom` and `ewmhDesktopsEventHookCustom`;
|
||||
these are now replaced by a composable `XMonad.Util.ExtensibleConf`-based
|
||||
interface. Users are advised to just use the `ewmh` XConfig combinator
|
||||
and customize behaviour using the provided `addEwmhWorkspaceSort`,
|
||||
`addEwmhWorkspaceRename` functions, or better still, use integrations
|
||||
provided by modules such as `XMonad.Actions.WorkspaceNames`.
|
||||
|
||||
This interface now additionally allows customization of what happens
|
||||
when clients request window activation. This can be used to ignore
|
||||
activation of annoying applications, to mark windows as urgent instead
|
||||
of focusing them, and more. There's also a new `XMonad.Hooks.Focus`
|
||||
module extending the ManageHook EDSL with useful combinators.
|
||||
|
||||
- Ordering of windows that are set to `_NET_CLIENT_LIST` and `_NET_CLIENT_LIST_STACKING`
|
||||
was changed to be closer to the spec. From now these two lists will have
|
||||
differently sorted windows.
|
||||
|
||||
- `_NET_WM_STATE_DEMANDS_ATTENTION` was added to the list of supported
|
||||
hints (as per `_NET_SUPPORTED`). This hint has long been understood by
|
||||
`UrgencyHook`. This enables certain applications (e.g. kitty terminal
|
||||
emulator) that check whether the hint is supported to use it.
|
||||
|
||||
* All modules still exporting a `defaultFoo` constructor
|
||||
|
||||
- All of these were now removed. You can use the re-exported `def` from
|
||||
`Data.Default` instead.
|
||||
|
||||
* `XMonad.Hooks.Script`
|
||||
|
||||
- `execScriptHook` now has an `X` constraint (was: `MonadIO`), due to changes
|
||||
in how the xmonad core handles XDG directories.
|
||||
|
||||
* `XMonad.Actions.WorkspaceNames`
|
||||
|
||||
- The type of `getWorkspaceNames` was changed to fit into the new `ppRename`
|
||||
field of `PP`.
|
||||
|
||||
* `XMonad.Hooks.StatusBar`, `XMonad.Hooks.StatusBar.PP` (previously
|
||||
`XMonad.Hooks.DynamicLog`) and `XMonad.Util.Run`
|
||||
|
||||
- `spawnPipe` no longer uses binary mode handles but defaults to the
|
||||
current locale encoding instead.
|
||||
|
||||
`dynamicLogString`, the output of which usually goes directly into such
|
||||
a handle, no longer encodes its output in UTF-8, but returns a normal
|
||||
`String` of Unicode codepoints instead.
|
||||
|
||||
When these two are used together, everything should continue to work as
|
||||
it always has, but in isolation behaviour might change.
|
||||
|
||||
(To get the old `spawnPipe` behaviour, `spawnPipeWithNoEncoding` can now
|
||||
be used, and `spawnPipeWithUtf8Encoding` was added as well to force
|
||||
UTF-8 regardless of locale. These shouldn't normally be necessary, though.)
|
||||
|
||||
- `xmonadPropLog` and `xmonadPropLog'` now encode the String in UTF-8.
|
||||
Again, no change when used together with `dynamicLogString`, but other
|
||||
uses of these in user configs might need to be adapted.
|
||||
|
||||
* `XMonad.Actions.TopicSpace`
|
||||
|
||||
- Deprecated the `maxTopicHistory` field, as well as the
|
||||
`getLastFocusedTopics` and `setLastFocusedTopic` functions. It is
|
||||
now recommended to directly use `XMonad.Hooks.WorkspaceHistory`
|
||||
instead.
|
||||
|
||||
- Added `TopicItem`, as well as the helper functions `topicNames`,
|
||||
`tiActions`, `tiDirs`, `noAction`, and `inHome` for a more
|
||||
convenient specification of topics.
|
||||
|
||||
* `XMonad.Actions.CycleRecentWS`
|
||||
|
||||
- Changed the signature of `recentWS` to return a `[WorkspaceId]`
|
||||
instead of a `[WindowSet]`, while `cycleWindowSets` and
|
||||
`toggleWindowSets` now take a function `WindowSet ->
|
||||
[WorkspaceId]` instead of one to `[WindowSet]` as their first
|
||||
argument. This fixes the interplay between this module and any
|
||||
layout that stores state.
|
||||
|
||||
* `XMonad.Layout.LayoutCombinators`
|
||||
|
||||
- Moved the alternative `(|||)` function and `JumpToLayout` to the
|
||||
xmonad core. They are re-exported by the module, but do not add any
|
||||
new functionality. `NewSelect` now exists as a deprecated type
|
||||
alias to `Choose`.
|
||||
|
||||
- Removed the `Wrap` and `NextLayoutNoWrap` data constructors.
|
||||
|
||||
- `XMonad.Actions.CycleWS`
|
||||
|
||||
- Deprecated `EmptyWS`, `HiddenWS`, `NonEmptyWS`, `HiddenNonEmptyWS`,
|
||||
`HiddenEmptyWS`, `AnyWS` and `WSTagGroup`.
|
||||
|
||||
- `XMonad.Actions.GridSelect`
|
||||
|
||||
- `colorRangeFromClassName` now uses different hash function,
|
||||
so colors of inactive window tiles will be different (but still inside
|
||||
the provided color range).
|
||||
|
||||
* `XMonad.Actions.Search`
|
||||
|
||||
- Removed outdated `isohunt` search engine.
|
||||
|
||||
- Updated URLs for `codesearch`, `openstreetmap`, and `thesaurus`
|
||||
search engines.
|
||||
|
||||
- Added `github` search engine.
|
||||
|
||||
### New Modules
|
||||
|
||||
* `XMonad.Layout.FixedAspectRatio`
|
||||
|
||||
Layout modifier for user provided per-window aspect ratios.
|
||||
|
||||
* `XMonad.Hooks.TaffybarPagerHints`
|
||||
|
||||
Add a module that exports information about XMonads internal state that is
|
||||
not available through EWMH that is used by the taffybar status bar.
|
||||
|
||||
* `XMonad.Hooks.StatusBar.PP`
|
||||
|
||||
Originally contained inside `XMonad.Hooks.DynamicLog`, this module provides the
|
||||
pretty-printing abstraction and utilities, used primarly with `logHook`.
|
||||
|
||||
Below are changes from `XMonad.Hooks.DynamicLog`, that now are included in
|
||||
this module:
|
||||
|
||||
- Added `shortenLeft` function, like existing `shorten` but shortens by
|
||||
truncating from left instead of right. Useful for showing directories.
|
||||
|
||||
- Added `shorten'` and `shortenLeft'` functions with customizable overflow
|
||||
markers.
|
||||
|
||||
- Added `filterOutWsPP` for filtering out certain workspaces from being
|
||||
displayed.
|
||||
|
||||
- Added `xmobarBorder` for creating borders around strings and
|
||||
`xmobarFont` for selecting an alternative font.
|
||||
|
||||
- Added `ppRename` to `PP`, which makes it possible for extensions like
|
||||
`workspaceNamesPP`, `marshallPP` and/or `clickablePP` (which need to
|
||||
access the original `WorkspaceId`) to compose intuitively.
|
||||
|
||||
- Added `ppPrinters`, `WSPP` and `fallbackPrinters` as a generalization of
|
||||
the `ppCurrent`, `ppVisible`… sextet, which makes it possible for
|
||||
extensions like `copiesPP` (which acts as if there was a
|
||||
`ppHiddenWithCopies`) to compose intuitively.
|
||||
|
||||
* `XMonad.Hooks.StatusBar`
|
||||
|
||||
This module provides a new interface that replaces `XMonad.Hooks.DynamicLog`,
|
||||
by providing composoble status bars. Supports property-based as well
|
||||
as pipe-based status bars.
|
||||
|
||||
* `XMonad.Util.Hacks`
|
||||
|
||||
A collection of hacks and fixes that should be easily acessible to users:
|
||||
|
||||
- `windowedFullscreenFix` fixes fullscreen behaviour of chromium based
|
||||
applications when using windowed fullscreen.
|
||||
|
||||
- `javaHack` helps when dealing with Java applications that might not work
|
||||
well with xmonad.
|
||||
|
||||
- `trayerAboveXmobarEventHook` reliably stacks trayer on top of xmobar and
|
||||
below other windows
|
||||
|
||||
* `XMonad.Util.ActionCycle`
|
||||
|
||||
A module providing a simple way to implement "cycling" `X` actions,
|
||||
useful for things like alternating toggle-style keybindings.
|
||||
|
||||
* `XMonad.Actions.RotateSome`
|
||||
|
||||
Functions for rotating some elements around the stack while keeping others
|
||||
anchored in place. Useful in combination with layouts that dictate window
|
||||
visibility based on stack position, such as `XMonad.Layout.LimitWindows`.
|
||||
|
||||
Export `surfaceNext` and `surfacePrev` actions, which treat the focused window
|
||||
and any hidden windows as a ring that can be rotated through the focused position.
|
||||
|
||||
Export `rotateSome`, a pure function that rotates some elements around a stack
|
||||
while keeping others anchored in place.
|
||||
|
||||
* `XMonad.Actions.Sift`
|
||||
|
||||
Provide `siftUp` and `siftDown` actions, which behave like `swapUp` and `swapDown`
|
||||
but handle the wrapping case by exchanging the windows at either end of the stack
|
||||
instead of rotating the stack.
|
||||
|
||||
* `XMonad.Hooks.DynamicIcons`
|
||||
|
||||
Dynamically augment workspace names logged to a status bar via DynamicLog
|
||||
based on the contents (windows) of the workspace.
|
||||
|
||||
* `XMonad.Hooks.WindowSwallowing`
|
||||
|
||||
HandleEventHooks that implement window swallowing or sublayouting:
|
||||
Hide parent windows like terminals when opening other programs (like image viewers) from within them,
|
||||
restoring them once the child application closes.
|
||||
|
||||
* `XMonad.Actions.TiledWindowDragging`
|
||||
|
||||
An action that allows you to change the position of windows by dragging them around.
|
||||
|
||||
* `XMonad.Layout.ResizableThreeColumns`
|
||||
|
||||
A layout based on `XMonad.Layout.ThreeColumns` but with each slave window's
|
||||
height resizable.
|
||||
|
||||
* `XMonad.Layout.TallMastersCombo`
|
||||
|
||||
A layout combinator that support Shrink, Expand, and IncMasterN just as
|
||||
the `Tall` layout, and also support operations of two master windows:
|
||||
a main master, which is the original master window;
|
||||
a sub master, the first window of the second pane.
|
||||
This combinator can be nested, and has a good support for using
|
||||
`XMonad.Layout.Tabbed` as a sublayout.
|
||||
|
||||
* `XMonad.Actions.PerWindowKeys`
|
||||
|
||||
Create actions that run on a `Query Bool`, usually associated with
|
||||
conditions on a window, basis. Useful for creating bindings that are
|
||||
excluded or exclusive for some windows.
|
||||
|
||||
* `XMonad.Util.DynamicScratchpads`
|
||||
|
||||
Declare any window as a scratchpad on the fly. Once declared, the
|
||||
scratchpad behaves like `XMonad.Util.NamedScratchpad`.
|
||||
|
||||
* `XMonad.Prompt.Zsh`
|
||||
|
||||
A version of `XMonad.Prompt.Shell` that lets you use completions supplied by
|
||||
zsh.
|
||||
|
||||
* `XMonad.Util.ClickableWorkspaces`
|
||||
|
||||
Provides `clickablePP`, which when applied to the `PP` pretty-printer used by
|
||||
`XMonad.Hooks.StatusBar.PP`, will make the workspace tags clickable in XMobar
|
||||
(for switching focus).
|
||||
|
||||
* `XMonad.Layout.VoidBorders`
|
||||
|
||||
Provides a modifier that semi-permanently (requires manual intervention)
|
||||
disables borders for windows from the layout it modifies.
|
||||
|
||||
* `XMonad.Hooks.Focus`
|
||||
|
||||
Extends ManageHook EDSL to work on focused windows and current workspace.
|
||||
|
||||
* `XMonad.Config.LXQt`
|
||||
|
||||
This module provides a config suitable for use with the LXQt desktop
|
||||
environment.
|
||||
|
||||
* `XMonad.Prompt.OrgMode`
|
||||
|
||||
A prompt for interacting with [org-mode](https://orgmode.org/). It
|
||||
can be used to quickly save TODOs, NOTEs, and the like with the
|
||||
additional capability to schedule/deadline a task, or use the
|
||||
primary selection as the contents of the note.
|
||||
|
||||
* `XMonad.Util.ExtensibleConf`
|
||||
|
||||
Extensible and composable configuration for contrib modules. Allows
|
||||
contrib modules to store custom configuration values inside `XConfig`.
|
||||
This lets them create custom hooks, ensure they hook into xmonad core only
|
||||
once, and possibly more.
|
||||
|
||||
* `XMonad.Hooks.Rescreen`
|
||||
|
||||
Custom hooks for screen (xrandr) configuration changes. These can be used
|
||||
to restart/reposition status bars or systrays automatically after xrandr,
|
||||
as well as to actually invoke xrandr or autorandr when an output is
|
||||
(dis)connected.
|
||||
|
||||
* `XMonad.Actions.EasyMotion`
|
||||
|
||||
A new module that allows selection of visible screens using a key chord.
|
||||
Inspired by [vim-easymotion](https://github.com/easymotion/vim-easymotion). See the animation
|
||||
in the vim-easymotion repo to get some idea of the functionality of this
|
||||
EasyMotion module.
|
||||
|
||||
### Bug Fixes and Minor Changes
|
||||
|
||||
* Add support for GHC 9.0.1.
|
||||
|
||||
* `XMonad.Actions.WithAll`
|
||||
|
||||
- Added `killOthers`, which kills all unfocused windows on the
|
||||
current workspace.
|
||||
|
||||
* `XMonad.Prompt.XMonad`
|
||||
|
||||
- Added `xmonadPromptCT`, which allows you to create an XMonad
|
||||
prompt with a custom title.
|
||||
|
||||
* `XMonad.Actions.DynamicWorkspaceGroups`
|
||||
|
||||
- Add support for `XMonad.Actions.TopicSpace` through `viewTopicGroup` and
|
||||
`promptTopicGroupView`.
|
||||
|
||||
* `XMonad.Actions.TreeSelect`
|
||||
|
||||
- Fix swapped green/blue in foreground when using Xft.
|
||||
|
||||
- The spawned tree-select window now sets a `WM_CLASS` property.
|
||||
This allows other applications, like compositors, to properly
|
||||
match on it.
|
||||
|
||||
* `XMonad.Layout.Fullscreen`
|
||||
|
||||
- Add fullscreenSupportBorder which uses smartBorders to remove
|
||||
window borders when the window is fullscreen.
|
||||
|
||||
* `XMonad.Config.Mate`
|
||||
|
||||
- Split out the logout dialog and add a shutdown dialog. The default behavior
|
||||
remains the same but there are now `mateLogout` and `mateShutdown` actions
|
||||
available.
|
||||
|
||||
- Add mod-d keybinding to open the Mate main menu.
|
||||
|
||||
* `XMonad.Actions.DynamicProjects`
|
||||
|
||||
- The `changeProjectDirPrompt` function respects the `complCaseSensitivity` field
|
||||
of `XPConfig` when performing directory completion.
|
||||
|
||||
- `modifyProject` is now exported.
|
||||
|
||||
* `XMonad.Layout.WorkspaceDir`
|
||||
|
||||
- The `changeDir` function respects the `complCaseSensitivity` field of `XPConfig`
|
||||
when performing directory completion.
|
||||
|
||||
- `Chdir` message is exported, so it's now possible to change the
|
||||
directory programmaticaly, not just via a user prompt.
|
||||
|
||||
* `XMonad.Prompt.Directory`
|
||||
|
||||
- Added `directoryMultipleModes'`, like `directoryMultipleModes` with an additional
|
||||
`ComplCaseSensitivity` argument.
|
||||
|
||||
- Directory completions are now sorted.
|
||||
|
||||
- The `Dir` constructor now takes an additional `ComplCaseSensitivity`
|
||||
argument to indicate whether directory completion is case sensitive.
|
||||
|
||||
* `XMonad.Prompt.FuzzyMatch`
|
||||
|
||||
- `fuzzySort` will now accept cases where the input is not a subsequence of
|
||||
every completion.
|
||||
|
||||
* `XMonad.Prompt.Shell`
|
||||
|
||||
- Added `getShellCompl'`, like `getShellCompl` with an additional `ComplCaseSensitivity`
|
||||
argument.
|
||||
|
||||
- Added `compgenDirectories` and `compgenFiles` to get the directory/filename completion
|
||||
matches returned by the compgen shell builtin.
|
||||
|
||||
- Added `safeDirPrompt`, which is like `safePrompt`, but optimized
|
||||
for the use-case of a program that needs a file as an argument.
|
||||
|
||||
* `XMonad.Prompt.Unicode`
|
||||
|
||||
- Reworked internally to call `spawnPipe` (asynchronous) instead of
|
||||
`runProcessWithInput` (synchronous), which fixes `typeUnicodePrompt`.
|
||||
|
||||
- Now respects `searchPredicate` and `sorter` from user-supplied `XPConfig`.
|
||||
|
||||
* `XMonad.Hooks.DynamicLog`
|
||||
|
||||
- Added `xmobarProp`, for property-based alternative to `xmobar`.
|
||||
|
||||
- Add the -dock argument to the dzen spawn arguments
|
||||
|
||||
- The API for this module is frozen: this is now a compatibility wrapper.
|
||||
|
||||
- References for this module are updated to point to `X.H.StatusBar` or
|
||||
`X.H.StatusBar.PP`
|
||||
|
||||
* `XMonad.Layout.BoringWindows`
|
||||
|
||||
- Added boring-aware `swapUp`, `swapDown`, `siftUp`, and `siftDown` functions.
|
||||
|
||||
- Added `markBoringEverywhere` function, to mark the currently
|
||||
focused window boring on all layouts, when using `XMonad.Actions.CopyWindow`.
|
||||
|
||||
* `XMonad.Util.NamedScratchpad`
|
||||
|
||||
- Added two new exported functions to the module:
|
||||
- `customRunNamedScratchpadAction`
|
||||
(provides the option to customize the `X ()` action the scratchpad is launched by)
|
||||
- `spawnHereNamedScratchpadAction`
|
||||
(uses `XMonad.Actions.SpawnOn.spawnHere` to initially start the scratchpad on the workspace it was launched on)
|
||||
|
||||
- Deprecated `namedScratchpadFilterOutWorkspace` and
|
||||
`namedScratchpadFilterOutWorkspacePP`. Use
|
||||
`XMonad.Util.WorkspaceCompare.filterOutWs` respectively
|
||||
`XMonad.Hooks.DynamicLog.filterOutWsPP` instead.
|
||||
|
||||
- Exported the `scratchpadWorkspaceTag`.
|
||||
|
||||
- Added a new logHook `nsHideOnFocusLoss` for hiding scratchpads
|
||||
when they lose focus.
|
||||
|
||||
* `XMonad.Prompt.Window`
|
||||
|
||||
- Added `allApplications` function which maps application executable
|
||||
names to its underlying window.
|
||||
|
||||
- Added a `WithWindow` constructor to `WindowPrompt` to allow executing
|
||||
actions of type `Window -> X ()` on the chosen window.
|
||||
|
||||
* `XMonad.Prompt.WindowBringer`
|
||||
|
||||
- Added `windowAppMap` function which maps application executable
|
||||
names to its underlying window.
|
||||
|
||||
- A new field `windowFilter` was added to the config, which allows the user
|
||||
to provide a function which will decide whether each window should be
|
||||
included in the window bringer menu.
|
||||
|
||||
* `XMonad.Actions.Search`
|
||||
|
||||
- The `hoogle` function now uses the new URL `hoogle.haskell.org`.
|
||||
|
||||
- Added `promptSearchBrowser'` function to only suggest previous searches of
|
||||
the selected search engine (instead of all search engines).
|
||||
|
||||
* `XMonad.Layout.MouseResizableTile`
|
||||
|
||||
- When we calculate dragger widths, we first try to get the border width of
|
||||
the focused window, before failing over to using the initial `borderWidth`.
|
||||
|
||||
* `XMonad.Actions.CycleRecentWS`
|
||||
|
||||
- Added `cycleRecentNonEmptyWS` function which behaves like `cycleRecentWS`
|
||||
but is constrainded to non-empty workspaces.
|
||||
|
||||
- Added `toggleRecentWS` and `toggleRecentNonEmptyWS` functions which toggle
|
||||
between the current and most recent workspace, and continue to toggle back
|
||||
and forth on repeated presses, rather than cycling through other workspaces.
|
||||
|
||||
- Added `recentWS` function which allows the recency list to be filtered with
|
||||
a user-provided predicate.
|
||||
|
||||
* `XMonad.Layout.Hidden`
|
||||
|
||||
- Export `HiddenWindows` type constructor.
|
||||
|
||||
- Export `popHiddenWindow` function restoring a specific window.
|
||||
|
||||
* `XMonad.Hooks.ManageDocks`
|
||||
|
||||
- Export `AvoidStruts` constructor
|
||||
|
||||
- Restored compatibility with pre-0.13 configs by making the startup hook
|
||||
unnecessary for correct functioning (strut cache is initialized on-demand).
|
||||
|
||||
This is a temporary measure, however. The individual hooks are now
|
||||
deprecated in favor of the `docks` combinator, `xmonad --recompile` now
|
||||
reports deprecation warnings, and the hooks will be removed soon.
|
||||
|
||||
- Fixed ignoring of strut updates from override-redirect windows, which is
|
||||
default for xmobar.
|
||||
|
||||
Previously, if one wanted xmobar to reposition itself after xrandr
|
||||
changes and have xmonad handle that repositioning, one would need to
|
||||
configure xmobar with `overrideRedirect = False`, which would disable
|
||||
lowering on start and thus cause other problems. This is no longer
|
||||
necessary.
|
||||
|
||||
* `XMonad.Hooks.ManageHelpers`
|
||||
|
||||
- Export `doSink`
|
||||
|
||||
- Added `doLower` and `doRaise`
|
||||
|
||||
- Added `shiftToSame` and `clientLeader` which allow a hook to be created
|
||||
that shifts a window to the workspace of other windows of the application
|
||||
(using either the `WM_CLIENT_LEADER` or `_NET_WM_PID` property).
|
||||
|
||||
- Added `windowTag`
|
||||
|
||||
- Added `(^?)`, `(~?)` and `($?)` operators as infix versions of `isPrefixOf`, `isInfixOf`
|
||||
and `isSuffixOf` working with `ManageHook`s.
|
||||
|
||||
* `XMonad.Util.EZConfig`
|
||||
|
||||
- Added support for XF86Bluetooth.
|
||||
|
||||
* `XMonad.Util.Loggers`
|
||||
|
||||
- Make `battery` and `loadAvg` distro-independent.
|
||||
|
||||
- Added `logTitleOnScreen`, `logCurrentOnScreen` and `logLayoutOnScreen`
|
||||
as screen-specific variants of `logTitle`, `logCurrent` and `logLayout`.
|
||||
|
||||
- Added `logWhenActive` to have loggers active only when a certain
|
||||
screen is active.
|
||||
|
||||
- Added `logConst` to log a constant `String`, and `logDefault` (infix: `.|`)
|
||||
to combine loggers.
|
||||
|
||||
- Added `logTitles` to log all window titles (focused and unfocused
|
||||
ones) on the focused workspace, as well as `logTitlesOnScreen` as
|
||||
a screen-specific variant thereof.
|
||||
|
||||
* `XMonad.Layout.Minimize`
|
||||
|
||||
- Export `Minimize` type constructor.
|
||||
|
||||
* `XMonad.Actions.WorkspaceNames`
|
||||
|
||||
- Added `workspaceNamesEwmh` which makes workspace names visible to
|
||||
external pagers.
|
||||
|
||||
* `XMonad.Util.PureX`
|
||||
|
||||
- Added `focusWindow` and `focusNth` which don't refresh (and thus
|
||||
possibly flicker) when they happen to be a no-op.
|
||||
|
||||
- Added `shiftWin` as a refresh tracking version of `W.shiftWin`.
|
||||
|
||||
* Several `LayoutClass` instances now have an additional `Typeable`
|
||||
constraint which may break some advanced configs. The upside is that we
|
||||
can now add `Typeable` to `LayoutClass` in `XMonad.Core` and make it
|
||||
possible to introspect the current layout and its modifiers.
|
||||
|
||||
* `XMonad.Actions.TopicSpace`
|
||||
|
||||
- `switchTopic` now correctly updates the last used topics.
|
||||
|
||||
- `setLastFocusedTopic` will now check whether we have exceeded the
|
||||
`maxTopicHistory` and prune the topic history as necessary, as well as
|
||||
cons the given topic onto the list __before__ filtering it.
|
||||
|
||||
- Added `switchNthLastFocusedExclude`, which works like
|
||||
`switchNthLastFocused` but is able to exclude certain topics.
|
||||
|
||||
- Added `switchTopicWith`, which works like `switchTopic`, but one is able
|
||||
to give `setLastFocusedTopic` a custom filtering function as well.
|
||||
|
||||
- Instead of a hand-rolled history, use the one from
|
||||
`XMonad.Hooks.WorkspaceHistory`.
|
||||
|
||||
- Added the screen-aware functions `getLastFocusedTopicsByScreen` and
|
||||
`switchNthLastFocusedByScreen`.
|
||||
|
||||
* `XMonad.Hooks.WorkspaceHistory`
|
||||
|
||||
- Added `workspaceHistoryModify` to modify the workspace history with a pure
|
||||
function.
|
||||
|
||||
- Added `workspaceHistoryHookExclude` for excluding certain
|
||||
workspaces to ever enter the history.
|
||||
|
||||
* `XMonad.Util.DebugWindow`
|
||||
|
||||
- Fixed a bottom in `debugWindow` when used on windows with UTF8 encoded titles.
|
||||
|
||||
* `XMonad.Config.Xfce`
|
||||
|
||||
- Set `terminal` to `xfce4-terminal`.
|
||||
|
||||
* `XMonad.Hooks.WorkspaceCompare`
|
||||
|
||||
- Added `filterOutWs` for workspace filtering.
|
||||
|
||||
* `XMonad.Prompt`
|
||||
|
||||
- Accommodate completion of multiple words even when `alwaysHighlight` is
|
||||
enabled.
|
||||
|
||||
- Made the history respect words that were "completed" by `alwaysHighlight`
|
||||
upon confirmation of the selection by the user.
|
||||
|
||||
- Fixed a crash when focusing a new window while the prompt was up
|
||||
by allowing pointer events to pass through the custom prompt event
|
||||
loop.
|
||||
|
||||
- The prompt now cycles through its suggestions if one hits the ends
|
||||
of the suggestion list and presses `TAB` again.
|
||||
|
||||
- Added `maxComplColumns` field to `XPConfig`, to limit the number of
|
||||
columns in the completion window.
|
||||
|
||||
- Redefine `ComplCaseSensitivity` to a proper sum type as opposed to
|
||||
a `newtype` wrapper around `Bool`.
|
||||
|
||||
* `XMonad.Actions.TreeSelect`
|
||||
|
||||
- Fixed a crash when focusing a new window while the tree select
|
||||
window was up by allowing pointer events to pass through the
|
||||
custom tree select event loop.
|
||||
|
||||
* `XMonad.Layout.NoBorders`
|
||||
|
||||
- Fixed handling of floating window borders in multihead setups that was
|
||||
broken since 0.14.
|
||||
|
||||
- Added `OnlyFloat` constructor to `Ambiguity` to unconditionally
|
||||
remove all borders on floating windows.
|
||||
|
||||
* `XMonad.Hooks.UrgencyHook`
|
||||
|
||||
- It's now possible to clear urgency of selected windows only using the
|
||||
newly exported `clearUrgents'` function. Also, this and `clearUrgents`
|
||||
now clear the `_NET_WM_STATE_DEMANDS_ATTENTION` bit as well.
|
||||
|
||||
- Added a variant of `filterUrgencyHook` that takes a generic `Query Bool`
|
||||
to select which windows should never be marked urgent.
|
||||
|
||||
- Added `askUrgent` and a `doAskUrgent` manage hook helper for marking
|
||||
windows as urgent from inside of xmonad. This can be used as a less
|
||||
intrusive action for windows requesting focus.
|
||||
|
||||
* `XMonad.Hooks.ServerMode`
|
||||
|
||||
- To make it easier to use, the `xmonadctl` client is now included in
|
||||
`scripts/`.
|
||||
|
||||
* `XMonad.Layout.TrackFloating`
|
||||
|
||||
- Fixed a bug that prevented changing focus on inactive workspaces.
|
||||
|
||||
* `XMonad.Layout.Magnifier`
|
||||
|
||||
- Added `magnifierczOff` and `magnifierczOff'` for custom-size
|
||||
magnifiers that start out with magnifying disabled.
|
||||
|
||||
- Added `magnify` as a more general combinator, including the
|
||||
ability to postpone magnifying until there are a certain number of
|
||||
windows on the workspace.
|
||||
|
||||
* `XMonad.Layout.Renamed`
|
||||
|
||||
- Added `KeepWordsLeft` and `KeepWordsRight` for keeping certain number of
|
||||
words in left or right direction in layout description.
|
||||
|
||||
* `XMonad.Hooks.WallpaperSetter`
|
||||
|
||||
- Added `defWPNamesPng`, which works like `defWPNames` but maps
|
||||
`ws-name` to `ws-name.png` instead of `ws-name.jpg`.
|
||||
|
||||
- Added `defWPNamesJpg` as an alias to `defWPNames` and deprecated
|
||||
the latter.
|
||||
|
||||
* `XMonad.Layout.SubLayouts`
|
||||
|
||||
- Floating windows are no longer moved to the end of the window stack.
|
||||
|
||||
* `XMonad.Layout.BinarySpacePartition`
|
||||
|
||||
- Add the ability to increase/decrease the window size by a custom
|
||||
rational number. E.g: `sendMessage $ ExpandTowardsBy L 0.02`
|
||||
|
||||
* `XMonad.Layout.Decoration`
|
||||
|
||||
- The decoration window now sets a `WM_CLASS` property. This allows
|
||||
other applications, like compositors, to properly match on it.
|
||||
|
||||
* `XMonad.Layout.IndependentScreens`
|
||||
|
||||
- Fixed a bug where `marshallPP` always sorted workspace names
|
||||
lexically. This changes the default behaviour of `marshallPP`—the
|
||||
given `ppSort` now operates in the _physical_ workspace names.
|
||||
The documentation of `marshallSort` contains an example of how to
|
||||
get the old behaviour, where `ppSort` operates in virtual names,
|
||||
back.
|
||||
|
||||
- Added `workspacesOn` for filtering workspaces on the current screen.
|
||||
|
||||
- Added `withScreen` to specify names for a given single screen.
|
||||
|
||||
- Added new aliases `PhysicalWindowSpace` and `VirtualWindowSpace`
|
||||
for a `WindowSpace` for easier to read function signatures.
|
||||
|
||||
- Added a few useful utility functions related to simplify using the
|
||||
module; namely `workspaceOnScreen`, `focusWindow'`, `focusScreen`,
|
||||
`nthWorkspace`, and `withWspOnScreen`.
|
||||
|
||||
- Fixed wrong type-signature of `onCurrentScreen`.
|
||||
|
||||
* `XMonad.Actions.CopyWindow`
|
||||
|
||||
- Added `copiesPP` to make a `PP` aware of copies of the focused
|
||||
window.
|
||||
|
||||
- `XMonad.Actions.CycleWS`
|
||||
|
||||
- Added `:&:`, `:|:` and `Not` data constructors to `WSType` to logically
|
||||
combine predicates.
|
||||
|
||||
- Added `hiddenWS`, `emptyWS` and `anyWS` to replace deprecated
|
||||
constructors.
|
||||
|
||||
- Added `ingoringWSs` as a `WSType` predicate to skip workspaces having a
|
||||
tag in a given list.
|
||||
|
||||
- `XMonad.Actions.DynamicWorkspaceOrder`
|
||||
|
||||
- Added `swapWithCurrent` and `swapOrder` to the list of exported names.
|
||||
|
||||
- `XMonad.Actions.Submap`, `XMonad.Util.Ungrab`:
|
||||
|
||||
- Fixed issue with keyboard/pointer staying grabbed when a blocking action
|
||||
like `runProcessWithInput` was invoked.
|
||||
|
||||
- `XMonad.Actions.UpdateFocus`
|
||||
|
||||
- Added `focusUnderPointer`, that updates the focus based on pointer
|
||||
position, an inverse of `X.A.UpdatePointer`, which moves the mouse
|
||||
pointer to match the focused window). Together these can be used to
|
||||
ensure focus stays in sync with mouse.
|
||||
|
||||
- `XMonad.Layout.MultiToggle`
|
||||
|
||||
- Added `isToggleActive` for querying the toggle state of transformers.
|
||||
Useful to show the state in a status bar.
|
||||
|
||||
* `XMonad.Layout.Spacing`
|
||||
|
||||
- Removed deprecations for `spacing`, `spacingWithEdge`,
|
||||
`smartSpacing`, and `smartSpacingWithEdge`.
|
||||
|
||||
* `XMonad.Actions.DynamicWorkspaces`
|
||||
|
||||
- Fixed a system freeze when using `X.A.CopyWindow.copy` in
|
||||
combination with `removeWorkspace`.
|
||||
|
||||
## 0.16
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* `XMonad.Layout.Decoration`
|
||||
- Added `Theme` record fields for controlling decoration border width for active/inactive/urgent windows.
|
||||
* `XMonad.Prompt`
|
||||
|
||||
- Prompt ships a vim-like keymap, see `vimLikeXPKeymap` and
|
||||
`vimLikeXPKeymap'`. A reworked event loop supports new vim-like prompt
|
||||
actions.
|
||||
- Prompt supports dynamic colors. Colors are now specified by the `XPColor`
|
||||
type in `XPState` while `XPConfig` colors remain unchanged for backwards
|
||||
compatibility.
|
||||
- Fixes `showCompletionOnTab`.
|
||||
- The behavior of `moveWord` and `moveWord'` has changed; brought in line
|
||||
with the documentation and now internally consistent. The old keymaps
|
||||
retain the original behavior; see the documentation to do the same your
|
||||
XMonad configuration.
|
||||
* `XMonad.Util.Invisble`
|
||||
- Requires `MonadFail` for `Read` instance
|
||||
|
||||
### New Modules
|
||||
|
||||
* `XMonad.Layout.TwoPanePersistent`
|
||||
|
||||
A layout that is like TwoPane but keeps track of the slave window that is
|
||||
currently beside the master. In TwoPane, the default behavior when the master
|
||||
is focused is to display the next window in the stack on the slave pane. This
|
||||
is a problem when a different slave window is selected without changing the stack
|
||||
order.
|
||||
|
||||
* `XMonad.Util.ExclusiveScratchpads`
|
||||
|
||||
Named scratchpads that can be mutually exclusive: This new module extends the
|
||||
idea of named scratchpads such that you can define "families of scratchpads"
|
||||
that are exclusive on the same screen. It also allows to remove this
|
||||
constraint of being mutually exclusive with another scratchpad.
|
||||
|
||||
* `XMonad.Actions.Prefix`
|
||||
|
||||
A module that allows the user to use an Emacs-style prefix
|
||||
argument (raw or numeric).
|
||||
|
||||
### Bug Fixes and Minor Changes
|
||||
|
||||
* `XMonad.Layout.Tabbed`
|
||||
|
||||
tabbedLeft and tabbedRight will set their tabs' height and width according to decoHeight/decoWidth
|
||||
|
||||
* `XMonad.Prompt`
|
||||
|
||||
Added `sorter` to `XPConfig` used to sort the possible completions by how
|
||||
well they match the search string (example: `XMonad.Prompt.FuzzyMatch`).
|
||||
|
||||
Fixes a potential bug where an error during prompt execution would
|
||||
leave the window open and keep the keyboard grabbed. See issue
|
||||
[#180](https://github.com/xmonad/xmonad-contrib/issues/180).
|
||||
|
||||
Fixes [issue #217](https://github.com/xmonad/xmonad-contrib/issues/217), where
|
||||
using tab to wrap around the completion rows would fail when maxComplRows is
|
||||
restricting the number of rows of output.
|
||||
|
||||
* `XMonad.Prompt.Pass`
|
||||
|
||||
Added 'passOTPPrompt' to support getting OTP type password. This require
|
||||
pass-otp (https://github.com/tadfisher/pass-otp) has been setup in the running
|
||||
machine.
|
||||
|
||||
Added 'passGenerateAndCopyPrompt', which both generates a new password and
|
||||
copies it to the clipboard. These two actions are commonly desirable to
|
||||
take together, e.g. when establishing a new account.
|
||||
|
||||
Made password prompts traverse symlinks when gathering password names for
|
||||
autocomplete.
|
||||
|
||||
* `XMonad.Actions.DynamicProjects`
|
||||
|
||||
Make the input directory read from the prompt in `DynamicProjects`
|
||||
absolute wrt the current directory.
|
||||
|
||||
Before this, the directory set by the prompt was treated like a relative
|
||||
directory. This means that when you switch from a project with directory
|
||||
`foo` into a project with directory `bar`, xmonad actually tries to `cd`
|
||||
into `foo/bar`, instead of `~/bar` as expected.
|
||||
|
||||
* `XMonad.Actions.DynamicWorkspaceOrder`
|
||||
|
||||
Add a version of `withNthWorkspace` that takes a `[WorkspaceId] ->
|
||||
[WorkspaceId]` transformation to apply over the list of workspace tags
|
||||
resulting from the dynamic order.
|
||||
|
||||
* `XMonad.Actions.GroupNavigation`
|
||||
|
||||
Add a utility function `isOnAnyVisibleWS :: Query Bool` to allow easy
|
||||
cycling between all windows on all visible workspaces.
|
||||
|
||||
|
||||
* `XMonad.Hooks.WallpaperSetter`
|
||||
|
||||
Preserve the aspect ratio of wallpapers that xmonad sets. When previous
|
||||
versions would distort images to fit the screen size, it will now find a
|
||||
best fit by cropping instead.
|
||||
|
||||
* `XMonad.Util.Themes`
|
||||
|
||||
Add adwaitaTheme and adwaitaDarkTheme to match their respective
|
||||
GTK themes.
|
||||
|
||||
* 'XMonad.Layout.BinarySpacePartition'
|
||||
|
||||
Add a new `SplitShiftDirectional` message that allows moving windows by
|
||||
splitting its neighbours.
|
||||
|
||||
* `XMonad.Prompt.FuzzyMatch`
|
||||
|
||||
Make fuzzy sort show shorter strings first.
|
||||
|
||||
## 0.15
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* `XMonad.Layout.Groups` & `XMonad.Layout.Groups.Helpers`
|
||||
The layout will no longer perform refreshes inside of its message handling.
|
||||
If you have been relying on it to in your xmonad.hs, you will need to start
|
||||
sending its messages in a manner that properly handles refreshing, e.g. with
|
||||
`sendMessage`.
|
||||
|
||||
### New Modules
|
||||
|
||||
* `XMonad.Util.Purex`
|
||||
|
||||
Unlike the opaque `IO` actions that `X` actions can wrap, regular reads from
|
||||
the `XConf` and modifications to the `XState` are fundamentally pure --
|
||||
contrary to the current treatment of such actions in most xmonad code. Pure
|
||||
modifications to the `WindowSet` can be readily composed, but due to the
|
||||
need for those modifications to be properly handled by `windows`, other pure
|
||||
changes to the `XState` cannot be interleaved with those changes to the
|
||||
`WindowSet` without superfluous refreshes, hence breaking composability.
|
||||
|
||||
This module aims to rectify that situation by drawing attention to it and
|
||||
providing `PureX`: a pure type with the same monadic interface to state as
|
||||
`X`. The `XLike` typeclass enables writing actions generic over the two
|
||||
monads; if pure, existing `X` actions can be generalised with only a change
|
||||
to the type signature. Various other utilities are provided, in particular
|
||||
the `defile` function which is needed by end-users.
|
||||
|
||||
### Bug Fixes and Minor Changes
|
||||
|
||||
* Add support for GHC 8.6.1.
|
||||
|
||||
* `XMonad.Actions.MessageHandling`
|
||||
Refresh-performing functions updated to better reflect the new `sendMessage`.
|
||||
|
||||
## 0.14
|
||||
|
||||
### Breaking Changes
|
||||
@@ -126,6 +1052,12 @@
|
||||
Provides a simple transformer for use with `XMonad.Layout.MultiToggle` to
|
||||
dynamically toggle `XMonad.Layout.TabBarDecoration`.
|
||||
|
||||
* `XMonad.Hooks.RefocusLast`
|
||||
|
||||
Provides hooks and actions that keep track of recently focused windows on a
|
||||
per workspace basis and automatically refocus the last window on loss of the
|
||||
current (if appropriate as determined by user specified criteria).
|
||||
|
||||
* `XMonad.Layout.StateFull`
|
||||
|
||||
Provides `StateFull`: a stateful form of `Full` that does not misbehave when
|
||||
@@ -276,7 +1208,8 @@
|
||||
|
||||
* `XMonad.Hooks.ManageHelpers`
|
||||
|
||||
Make type of ManageHook combinators more general.
|
||||
- Make type of ManageHook combinators more general.
|
||||
- New manage hook `doSink` for sinking windows (as upposed to the `doFloat` manage hook)
|
||||
|
||||
* `XMonad.Prompt`
|
||||
|
||||
@@ -311,6 +1244,8 @@
|
||||
|
||||
- New function `passTypePrompt` which uses `xdotool` to type in a password
|
||||
from the store, bypassing the clipboard.
|
||||
- New function `passEditPrompt` for editing a password from the
|
||||
store.
|
||||
- Now handles password labels with spaces and special characters inside
|
||||
them.
|
||||
|
||||
@@ -372,6 +1307,12 @@
|
||||
* `XMonad.Prompt` now stores its history file in the XMonad cache
|
||||
directory in a file named `prompt-history`.
|
||||
|
||||
* `XMonad.Hooks.ManageDocks` now requires an additional startup hook to be
|
||||
added to configuration in addition to the other 3 hooks, otherwise docks
|
||||
started before xmonad are covered by windows. It's recommended to use the
|
||||
newly introduced `docks` function to add all necessary hooks to xmonad
|
||||
config.
|
||||
|
||||
### New Modules
|
||||
|
||||
* `XMonad.Layout.SortedLayout`
|
||||
@@ -405,7 +1346,7 @@
|
||||
|
||||
### Bug Fixes and Minor Changes
|
||||
|
||||
* `XMonad.Hooks.ManageDocks`,
|
||||
* `XMonad.Hooks.ManageDocks`
|
||||
|
||||
- Fix a very annoying bug where taskbars/docs would be
|
||||
covered by windows.
|
||||
|
5
CONTRIBUTING.md
Normal file
5
CONTRIBUTING.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Contributing to xmonad and xmonad-contrib
|
||||
|
||||
Please refer to XMonad's [CONTRIBUTING][gh:xmonad:contributing].
|
||||
|
||||
[gh:xmonad:contributing]: https://github.com/xmonad/xmonad/blob/master/CONTRIBUTING.md
|
45
LICENSE
45
LICENSE
@@ -1,27 +1,26 @@
|
||||
Copyright (c) The Xmonad Community
|
||||
Copyright (c) The Xmonad Community. All rights reserved.
|
||||
|
||||
All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the author nor the names of his contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
110
README.md
110
README.md
@@ -1,43 +1,97 @@
|
||||
# xmonad-contrib: Third Party Extensions to the xmonad Window Manager
|
||||
<p align="center">
|
||||
<a href="https://xmonad.org/">
|
||||
<img alt="XMonad logo" src="https://xmonad.org/images/logo-wrapped.svg" height=150>
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://hackage.haskell.org/package/xmonad-contrib">
|
||||
<img alt="Hackage" src="https://img.shields.io/hackage/v/xmonad-contrib?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>
|
||||
<a href="https://github.com/xmonad/xmonad-contrib/actions/workflows/stack.yml">
|
||||
<img alt="Stack" src="https://img.shields.io/github/workflow/status/xmonad/xmonad-contrib/Stack?label=Stack&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>
|
||||
<a href="https://github.com/sponsors/xmonad">
|
||||
<img alt="GitHub Sponsors" src="https://img.shields.io/github/sponsors/xmonad?label=GitHub%20Sponsors&logo=githubsponsors">
|
||||
</a>
|
||||
<a href="https://opencollective.com/xmonad">
|
||||
<img alt="Open Collective" src="https://img.shields.io/opencollective/all/xmonad?label=Open%20Collective&logo=opencollective">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
[](https://travis-ci.org/xmonad/xmonad-contrib)
|
||||
[](https://www.codetriage.com/xmonad/xmonad-contrib)
|
||||
# xmonad-contrib
|
||||
|
||||
You need the ghc compiler and xmonad window manager installed in
|
||||
order to use these extensions.
|
||||
**Community-maintained extensions for the [XMonad][web:xmonad] window manager.**
|
||||
|
||||
For installation and configuration instructions, please see the
|
||||
[xmonad website][xmonad], the documents included with the
|
||||
[xmonad source distribution][xmonad-git], and the
|
||||
[online haddock documentation][xmonad-docs].
|
||||
[xmonad core][gh:xmonad] is minimal, stable, yet extensible.
|
||||
[xmonad-contrib][gh:xmonad-contrib] is home to hundreds of additional tiling
|
||||
algorithms and extension modules. The two combined make for a powerful X11
|
||||
window-manager with endless customization possibilities. They are, quite
|
||||
literally, libraries for creating your own window manager.
|
||||
|
||||
## Getting or Updating XMonadContrib
|
||||
## Installation
|
||||
|
||||
* Latest release: <https://hackage.haskell.org/package/xmonad-contrib>
|
||||
For installation and configuration instructions, please see:
|
||||
|
||||
* Git version: <https://github.com/xmonad/xmonad-contrib>
|
||||
* [downloading and installing xmonad][web:download]
|
||||
* [installing latest xmonad snapshot from git][web:install]
|
||||
* [configuring xmonad][web:tutorial]
|
||||
|
||||
(To use git xmonad-contrib you must also use the
|
||||
[git version of xmonad][xmonad-git].)
|
||||
If you run into any trouble, consult our [documentation][web:documentation] or
|
||||
ask the [community][web:community] for help.
|
||||
|
||||
## Contributing
|
||||
|
||||
Haskell code contributed to this repo should live under the
|
||||
appropriate subdivision of the `XMonad` namespace (currently includes
|
||||
`Actions`, `Config`, `Hooks`, `Layout`, `Prompt`, and `Util`). For
|
||||
example, to use the Grid layout, one would import:
|
||||
We welcome all forms of contributions:
|
||||
|
||||
XMonad.Layout.Grid
|
||||
* [bug reports and feature ideas][gh:xmonad-contrib:issues]
|
||||
(also to [xmonad][gh:xmonad:issues])
|
||||
* [bug fixes, new features, new extensions][gh:xmonad-contrib:pulls]
|
||||
(also to [xmonad][gh:xmonad:pulls])
|
||||
* documentation fixes and improvements: [xmonad][gh:xmonad],
|
||||
[xmonad-contrib][gh:xmonad-contrib], [xmonad-web][gh:xmonad-web]
|
||||
* helping others in the [community][web:community]
|
||||
* financial support: [GitHub Sponsors][gh:xmonad:sponsors],
|
||||
[Open Collective][opencollective:xmonad]
|
||||
|
||||
For further details, see the [documentation][developing] for the
|
||||
`XMonad.Doc.Developing` module, XMonad's [CONTRIBUTING.md](https://github.com/xmonad/xmonad/blob/master/CONTRIBUTING.md) and the [xmonad website][xmonad].
|
||||
Please do read the [CONTRIBUTING][gh:xmonad:contributing] document for more
|
||||
information about bug reporting and code contributions. For a brief overview
|
||||
of the architecture and code conventions, see the [documentation for the
|
||||
`XMonad.Doc.Developing` module][doc:developing]. If in doubt, [talk to
|
||||
us][web:community].
|
||||
|
||||
## License
|
||||
|
||||
Code submitted to the contrib repo is licensed under the same license as
|
||||
xmonad itself, with copyright held by the authors.
|
||||
|
||||
[xmonad]: http://xmonad.org
|
||||
[xmonad-git]: https://github.com/xmonad/xmonad
|
||||
[xmonad-docs]: http://hackage.haskell.org/package/xmonad
|
||||
[developing]: http://hackage.haskell.org/package/xmonad-contrib/docs/XMonad-Doc-Developing.html
|
||||
Code submitted to the xmonad-contrib repo is licensed under the same license
|
||||
as xmonad core itself, with copyright held by the authors.
|
||||
|
||||
[doc:developing]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Doc-Developing.html
|
||||
[gh:xmonad-contrib:issues]: https://github.com/xmonad/xmonad-contrib/issues
|
||||
[gh:xmonad-contrib:pulls]: https://github.com/xmonad/xmonad-contrib/pulls
|
||||
[gh:xmonad-contrib]: https://github.com/xmonad/xmonad-contrib
|
||||
[gh:xmonad-web]: https://github.com/xmonad/xmonad-web
|
||||
[gh:xmonad:contributing]: https://github.com/xmonad/xmonad/blob/master/CONTRIBUTING.md
|
||||
[gh:xmonad:issues]: https://github.com/xmonad/xmonad/issues
|
||||
[gh:xmonad:pulls]: https://github.com/xmonad/xmonad/pulls
|
||||
[gh:xmonad:sponsors]: https://github.com/sponsors/xmonad
|
||||
[gh:xmonad]: https://github.com/xmonad/xmonad
|
||||
[opencollective:xmonad]: https://opencollective.com/xmonad
|
||||
[web:community]: https://xmonad.org/community.html
|
||||
[web:documentation]: https://xmonad.org/documentation.html
|
||||
[web:download]: https://xmonad.org/download.html
|
||||
[web:install]: https://xmonad.org/INSTALL.html
|
||||
[web:tutorial]: https://xmonad.org/TUTORIAL.html
|
||||
[web:xmonad]: https://xmonad.org/
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.AfterDrag
|
||||
-- Description : Allows you to add actions dependent on the current mouse drag.
|
||||
-- Copyright : (c) 2014 Anders Engstrom <ankaan@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -19,7 +20,8 @@ module XMonad.Actions.AfterDrag (
|
||||
ifClick') where
|
||||
|
||||
import XMonad
|
||||
import System.Time
|
||||
|
||||
import Data.Time (NominalDiffTime, diffUTCTime, getCurrentTime)
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
@@ -53,7 +55,7 @@ afterDrag task = do drag <- gets dragging
|
||||
-- A drag is considered a click if it is completed within 300 ms.
|
||||
ifClick
|
||||
:: X () -- ^ The action to take if the dragging turned out to be a click.
|
||||
-> X ()
|
||||
-> X ()
|
||||
ifClick action = ifClick' 300 action (return ())
|
||||
|
||||
-- | Take an action if the current dragging is completed within a certain time (in milliseconds.)
|
||||
@@ -61,11 +63,11 @@ ifClick'
|
||||
:: Int -- ^ Maximum time of dragging for it to be considered a click (in milliseconds.)
|
||||
-> X () -- ^ The action to take if the dragging turned out to be a click.
|
||||
-> X () -- ^ The action to take if the dragging turned out to not be a click.
|
||||
-> X ()
|
||||
-> X ()
|
||||
ifClick' ms click drag = do
|
||||
start <- io $ getClockTime
|
||||
afterDrag $ do
|
||||
stop <- io $ getClockTime
|
||||
if diffClockTimes stop start <= noTimeDiff { tdPicosec = fromIntegral ms * 10^(9 :: Integer) }
|
||||
start <- io getCurrentTime
|
||||
afterDrag $ do
|
||||
stop <- io getCurrentTime
|
||||
if diffUTCTime stop start <= (fromIntegral ms / 10^(3 :: Integer) :: NominalDiffTime)
|
||||
then click
|
||||
else drag
|
||||
|
@@ -1,6 +1,7 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.BluetileCommands
|
||||
-- Description : Interface with the [Bluetile](https://hackage.haskell.org/package/bluetile) tiling window manager.
|
||||
-- Copyright : (c) Jan Vornberger 2009
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -24,7 +25,6 @@ module XMonad.Actions.BluetileCommands (
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Layout.LayoutCombinators
|
||||
import System.Exit
|
||||
|
||||
-- $usage
|
||||
@@ -43,7 +43,7 @@ import System.Exit
|
||||
|
||||
workspaceCommands :: Int -> X [(String, X ())]
|
||||
workspaceCommands sid = asks (workspaces . config) >>= \spaces -> return
|
||||
[(("greedyView" ++ show i),
|
||||
[( "greedyView" ++ show i,
|
||||
activateScreen sid >> windows (W.greedyView i))
|
||||
| i <- spaces ]
|
||||
|
||||
@@ -66,7 +66,7 @@ masterAreaCommands sid = [ ("increase master n", activateScreen sid >>
|
||||
]
|
||||
|
||||
quitCommands :: [(String, X ())]
|
||||
quitCommands = [ ("quit bluetile", io (exitWith ExitSuccess))
|
||||
quitCommands = [ ("quit bluetile", io exitSuccess)
|
||||
, ("quit bluetile and start metacity", restart "metacity" False)
|
||||
]
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.Commands
|
||||
-- Description : Run internal xmonad commands using a dmenu menu.
|
||||
-- Copyright : (c) David Glasser 2007
|
||||
-- License : BSD3
|
||||
--
|
||||
@@ -32,7 +33,7 @@ import XMonad.Util.Dmenu (dmenu)
|
||||
|
||||
import qualified Data.Map as M
|
||||
import System.Exit
|
||||
import Data.Maybe
|
||||
import XMonad.Prelude
|
||||
|
||||
-- $usage
|
||||
--
|
||||
@@ -61,18 +62,18 @@ import Data.Maybe
|
||||
-- | Create a 'Data.Map.Map' from @String@s to xmonad actions from a
|
||||
-- list of pairs.
|
||||
commandMap :: [(String, X ())] -> M.Map String (X ())
|
||||
commandMap c = M.fromList c
|
||||
commandMap = M.fromList
|
||||
|
||||
-- | Generate a list of commands to switch to\/send windows to workspaces.
|
||||
workspaceCommands :: X [(String, X ())]
|
||||
workspaceCommands = asks (workspaces . config) >>= \spaces -> return
|
||||
[((m ++ show i), windows $ f i)
|
||||
[( m ++ show i, windows $ f i)
|
||||
| i <- spaces
|
||||
, (f, m) <- [(view, "view"), (shift, "shift")] ]
|
||||
|
||||
-- | Generate a list of commands dealing with multiple screens.
|
||||
screenCommands :: [(String, X ())]
|
||||
screenCommands = [((m ++ show sc), screenWorkspace (fromIntegral sc) >>= flip whenJust (windows . f))
|
||||
screenCommands = [( m ++ show sc, screenWorkspace (fromIntegral sc) >>= flip whenJust (windows . f))
|
||||
| sc <- [0, 1]::[Int] -- TODO: adapt to screen changes
|
||||
, (f, m) <- [(view, "screen"), (shift, "screen-to-")]
|
||||
]
|
||||
@@ -100,7 +101,7 @@ defaultCommands = do
|
||||
, ("swap-down" , windows swapDown )
|
||||
, ("swap-master" , windows swapMaster )
|
||||
, ("sink" , withFocused $ windows . sink )
|
||||
, ("quit-wm" , io $ exitWith ExitSuccess )
|
||||
, ("quit-wm" , io exitSuccess )
|
||||
]
|
||||
|
||||
-- | Given a list of command\/action pairs, prompt the user to choose a
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.ConstrainedResize
|
||||
-- Description : Constrain the aspect ratio of a floating window.
|
||||
-- Copyright : (c) Dougal Stanton
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -44,7 +45,6 @@ import XMonad
|
||||
-- | Resize (floating) window with optional aspect ratio constraints.
|
||||
mouseResizeWindow :: Window -> Bool -> X ()
|
||||
mouseResizeWindow w c = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
sh <- io $ getWMNormalHints d w
|
||||
io $ warpPointer d none w 0 0 0 0 (fromIntegral (wa_width wa)) (fromIntegral (wa_height wa))
|
||||
@@ -53,5 +53,6 @@ mouseResizeWindow w c = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
y = ey - fromIntegral (wa_y wa)
|
||||
sz = if c then (max x y, max x y) else (x,y)
|
||||
io $ resizeWindow d w `uncurry`
|
||||
applySizeHintsContents sh sz)
|
||||
applySizeHintsContents sh sz
|
||||
float w)
|
||||
(float w)
|
||||
|
@@ -1,7 +1,9 @@
|
||||
{-# LANGUAGE PatternGuards #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.CopyWindow
|
||||
-- Description : Duplicate a window on multiple workspaces.
|
||||
-- Copyright : (c) David Roundy <droundy@darcs.net>, Ivan Veselov <veselov@gmail.com>, Lanny Ripple <lan3ny@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -18,17 +20,19 @@ module XMonad.Actions.CopyWindow (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
copy, copyToAll, copyWindow, runOrCopy
|
||||
, killAllOtherCopies, kill1
|
||||
, killAllOtherCopies, kill1, taggedWindows, copiesOfOn
|
||||
-- * Highlight workspaces containing copies in logHook
|
||||
-- $logHook
|
||||
, wsContainingCopies
|
||||
, wsContainingCopies, copiesPP
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude
|
||||
import Control.Arrow ((&&&))
|
||||
import qualified Data.List as L
|
||||
|
||||
import XMonad.Actions.WindowGo
|
||||
import XMonad.Hooks.StatusBar.PP (PP(..), WS(..), isHidden)
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
-- $usage
|
||||
@@ -76,18 +80,24 @@ import qualified XMonad.StackSet as W
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
-- $logHook
|
||||
-- To distinguish workspaces containing copies of the focused window use
|
||||
-- something like:
|
||||
--
|
||||
-- > sampleLogHook h = do
|
||||
-- > copies <- wsContainingCopies
|
||||
-- > let check ws | ws `elem` copies = pad . xmobarColor "red" "black" $ ws
|
||||
-- > | otherwise = pad ws
|
||||
-- > dynamicLogWithPP myPP {ppHidden = check, ppOutput = hPutStrLn h}
|
||||
-- >
|
||||
-- > main = do
|
||||
-- > h <- spawnPipe "xmobar"
|
||||
-- > xmonad def { logHook = sampleLogHook h }
|
||||
-- To distinguish workspaces containing copies of the focused window, use 'copiesPP'.
|
||||
-- 'copiesPP' takes a pretty printer and makes it aware of copies of the focused window.
|
||||
-- It can be applied when creating a 'XMonad.Hooks.StatusBar.StatusBarConfig'.
|
||||
--
|
||||
-- A sample config looks like this:
|
||||
--
|
||||
-- > mySB = statusBarProp "xmobar" (copiesPP (pad . xmobarColor "red" "black") xmobarPP)
|
||||
-- > main = xmonad $ withEasySB mySB defToggleStrutsKey def
|
||||
|
||||
-- | Take a pretty printer and make it aware of copies by using the provided function
|
||||
-- to show hidden workspaces that contain copies of the focused window.
|
||||
copiesPP :: (WorkspaceId -> String) -> PP -> X PP
|
||||
copiesPP wtoS pp = do
|
||||
copies <- wsContainingCopies
|
||||
let check WS{..} = W.tag wsWS `elem` copies
|
||||
let printer = (asks (isHidden <&&> check) >>= guard) $> wtoS
|
||||
return pp{ ppPrinters = printer <|> ppPrinters pp }
|
||||
|
||||
-- | Copy the focused window to a workspace.
|
||||
copy :: (Eq s, Eq i, Eq a) => i -> W.StackSet i l a s sd -> W.StackSet i l a s sd
|
||||
@@ -96,7 +106,7 @@ copy n s | Just w <- W.peek s = copyWindow w n s
|
||||
|
||||
-- | Copy the focused window to all workspaces.
|
||||
copyToAll :: (Eq s, Eq i, Eq a) => W.StackSet i l a s sd -> W.StackSet i l a s sd
|
||||
copyToAll s = foldr copy s $ map W.tag (W.workspaces s)
|
||||
copyToAll s = foldr (copy . W.tag) s (W.workspaces s)
|
||||
|
||||
-- | Copy an arbitrary window to a workspace.
|
||||
copyWindow :: (Eq a, Eq i, Eq s) => a -> i -> W.StackSet i l a s sd -> W.StackSet i l a s sd
|
||||
@@ -142,9 +152,9 @@ killAllOtherCopies = do ss <- gets windowset
|
||||
W.view (W.currentTag ss) .
|
||||
delFromAllButCurrent w
|
||||
where
|
||||
delFromAllButCurrent w ss = foldr ($) ss $
|
||||
map (delWinFromWorkspace w . W.tag) $
|
||||
W.hidden ss ++ map W.workspace (W.visible ss)
|
||||
delFromAllButCurrent w ss = foldr (delWinFromWorkspace w . W.tag)
|
||||
ss
|
||||
(W.hidden ss ++ map W.workspace (W.visible ss))
|
||||
delWinFromWorkspace w wid = viewing wid $ W.modify Nothing (W.filter (/= w))
|
||||
|
||||
viewing wis f ss = W.view (W.currentTag ss) $ f $ W.view wis ss
|
||||
|
@@ -1,6 +1,10 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
{-# LANGUAGE PatternGuards #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.CycleRecentWS
|
||||
-- Description : Cycle through most recently used workspaces.
|
||||
-- Copyright : (c) Michal Janeczek <janeczek@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -19,11 +23,23 @@ module XMonad.Actions.CycleRecentWS (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
cycleRecentWS,
|
||||
cycleWindowSets
|
||||
cycleRecentNonEmptyWS,
|
||||
cycleWindowSets,
|
||||
toggleRecentWS,
|
||||
toggleRecentNonEmptyWS,
|
||||
toggleWindowSets,
|
||||
recentWS,
|
||||
|
||||
#ifdef TESTING
|
||||
unView,
|
||||
#endif
|
||||
) where
|
||||
|
||||
import XMonad hiding (workspaces)
|
||||
import XMonad.StackSet
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
import Control.Arrow ((&&&))
|
||||
import Data.Function (on)
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
|
||||
@@ -47,33 +63,61 @@ cycleRecentWS :: [KeySym] -- ^ A list of modifier keys used when invoking this a
|
||||
-> KeySym -- ^ Key used to switch to previous (more recent) workspace.
|
||||
-- If it's the same as the nextWorkspace key, it is effectively ignored.
|
||||
-> X ()
|
||||
cycleRecentWS = cycleWindowSets options
|
||||
where options w = map (view `flip` w) (recentTags w)
|
||||
recentTags w = map tag $ tail (workspaces w) ++ [head (workspaces w)]
|
||||
cycleRecentWS = cycleWindowSets $ recentWS (const True)
|
||||
|
||||
|
||||
cycref :: [a] -> Int -> a
|
||||
cycref l i = l !! (i `mod` length l)
|
||||
-- | Like 'cycleRecentWS', but restricted to non-empty workspaces.
|
||||
cycleRecentNonEmptyWS :: [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 (less recent) workspace.
|
||||
-> KeySym -- ^ Key used to switch to previous (more recent) workspace.
|
||||
-- If it's the same as the nextWorkspace key, it is effectively ignored.
|
||||
-> X ()
|
||||
cycleRecentNonEmptyWS = cycleWindowSets $ recentWS (not . null . stack)
|
||||
|
||||
-- | Cycle through a finite list of WindowSets with repeated presses of a key, while
|
||||
|
||||
-- | Switch to the most recent workspace. The stack of most recently used workspaces
|
||||
-- is updated, so repeated use toggles between a pair of workspaces.
|
||||
toggleRecentWS :: X ()
|
||||
toggleRecentWS = toggleWindowSets $ recentWS (const True)
|
||||
|
||||
|
||||
-- | Like 'toggleRecentWS', but restricted to non-empty workspaces.
|
||||
toggleRecentNonEmptyWS :: X ()
|
||||
toggleRecentNonEmptyWS = toggleWindowSets $ recentWS (not . null . stack)
|
||||
|
||||
|
||||
-- | Given a predicate @p@ and the current 'WindowSet' @w@, create a
|
||||
-- list of workspaces to choose from. They are ordered by recency and
|
||||
-- have to satisfy @p@.
|
||||
recentWS :: (WindowSpace -> Bool) -- ^ A workspace predicate.
|
||||
-> WindowSet -- ^ The current WindowSet
|
||||
-> [WorkspaceId]
|
||||
recentWS p w = map tag
|
||||
$ filter p
|
||||
$ map workspace (visible w)
|
||||
++ hidden w
|
||||
++ [workspace (current w)]
|
||||
|
||||
-- | Cycle through a finite list of workspaces with repeated presses of a key, while
|
||||
-- a modifier key is held down. For best effects use the same modkey+key combination
|
||||
-- as the one used to invoke this action.
|
||||
cycleWindowSets :: (WindowSet -> [WindowSet]) -- ^ A function used to create a list of WindowSets to choose from
|
||||
-> [KeySym] -- ^ A list of modifier keys used when invoking this action.
|
||||
-- As soon as one of them is released, the final WindowSet is chosen and the action exits.
|
||||
-> KeySym -- ^ Key used to preview next WindowSet from the list of generated options
|
||||
-> KeySym -- ^ Key used to preview previous WindowSet from the list of generated options.
|
||||
-- If it's the same as nextOption key, it is effectively ignored.
|
||||
cycleWindowSets :: (WindowSet -> [WorkspaceId]) -- ^ A function used to create a list of workspaces to choose from
|
||||
-> [KeySym] -- ^ A list of modifier keys used when invoking this action.
|
||||
-- As soon as one of them is released, the final workspace is chosen and the action exits.
|
||||
-> KeySym -- ^ Key used to preview next workspace from the list of generated options
|
||||
-> KeySym -- ^ Key used to preview previous workspace from the list of generated options.
|
||||
-- If it's the same as nextOption key, it is effectively ignored.
|
||||
-> X ()
|
||||
cycleWindowSets genOptions mods keyNext keyPrev = do
|
||||
options <- gets $ genOptions . windowset
|
||||
(options, unView') <- gets $ (genOptions &&& unView) . windowset
|
||||
XConf {theRoot = root, display = d} <- ask
|
||||
let event = 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)
|
||||
let setOption n = do windows $ const $ options `cycref` n
|
||||
let setOption n = do windows $ view (options `cycref` n) . unView'
|
||||
(t, s) <- io event
|
||||
case () of
|
||||
() | t == keyPress && s == keyNext -> setOption (n+1)
|
||||
@@ -83,3 +127,37 @@ cycleWindowSets genOptions mods keyNext keyPrev = do
|
||||
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
|
||||
-- 'view' away from the old one, restore the workspace order of the
|
||||
-- former inside of the latter. This respects any new state that the
|
||||
-- new 'WindowSet' may have accumulated.
|
||||
unView :: forall i l a s sd. (Eq i, Eq s)
|
||||
=> StackSet i l a s sd -> StackSet i l a s sd -> StackSet i l a s sd
|
||||
unView w0 w1 = fixOrderH . fixOrderV . view' (currentTag w0) $ w1
|
||||
where
|
||||
view' = if screen (current w0) == screen (current w1) then greedyView else view
|
||||
fixOrderV w | v : vs <- visible w = w{ visible = insertAt (pfxV (visible w0) vs) v vs }
|
||||
| otherwise = w
|
||||
fixOrderH w | h : hs <- hidden w = w{ hidden = insertAt (pfxH (hidden w0) hs) h hs }
|
||||
| otherwise = w
|
||||
pfxV = commonPrefix `on` fmap (tag . workspace)
|
||||
pfxH = commonPrefix `on` fmap tag
|
||||
|
||||
insertAt :: Int -> x -> [x] -> [x]
|
||||
insertAt n x xs = let (l, r) = splitAt n xs in l ++ [x] ++ r
|
||||
|
||||
commonPrefix :: Eq x => [x] -> [x] -> Int
|
||||
commonPrefix a b = length $ takeWhile id $ zipWith (==) a b
|
||||
|
||||
-- | Given some function that generates a list of workspaces from a
|
||||
-- given 'WindowSet', switch to the first generated workspace.
|
||||
toggleWindowSets :: (WindowSet -> [WorkspaceId]) -> X ()
|
||||
toggleWindowSets genOptions = do
|
||||
options <- gets $ genOptions . windowset
|
||||
case options of
|
||||
[] -> return ()
|
||||
o:_ -> windows (view o)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.CycleSelectedLayouts
|
||||
-- Description : Cycle through the given subset of layouts.
|
||||
-- Copyright : (c) Roman Cheplyaka
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -18,27 +19,21 @@ module XMonad.Actions.CycleSelectedLayouts (
|
||||
cycleThroughLayouts) where
|
||||
|
||||
import XMonad
|
||||
import Data.List (findIndex)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import XMonad.Layout.LayoutCombinators (JumpToLayout(..))
|
||||
import XMonad.Prelude (elemIndex, fromMaybe)
|
||||
import qualified XMonad.StackSet as S
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad hiding ((|||))
|
||||
-- > import XMonad.Layout.LayoutCombinators ((|||))
|
||||
-- > import XMonad
|
||||
-- > import XMonad.Actions.CycleSelectedLayouts
|
||||
--
|
||||
-- > , ((modm, xK_t ), cycleThroughLayouts ["Tall", "Mirror Tall"])
|
||||
--
|
||||
-- Make sure you are using NewSelect from XMonad.Layout.LayoutCombinators,
|
||||
-- rather than the Select defined in xmonad core.
|
||||
|
||||
cycleToNext :: (Eq a) => [a] -> a -> Maybe a
|
||||
cycleToNext lst a = do
|
||||
-- not beautiful but simple and readable
|
||||
ind <- findIndex (a==) lst
|
||||
ind <- elemIndex a lst
|
||||
return $ lst !! if ind == length lst - 1 then 0 else ind+1
|
||||
|
||||
-- | If the current layout is in the list, cycle to the next layout. Otherwise,
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.CycleWS
|
||||
-- Description : Cycle through workspaces.
|
||||
-- Copyright : (c) Joachim Breitner <mail@joachim-breitner.de>,
|
||||
-- Nelson Elhage <nelhage@mit.edu> (`toggleWS' function)
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
@@ -18,13 +19,13 @@
|
||||
--
|
||||
-- Note that this module now subsumes the functionality of the former
|
||||
-- @XMonad.Actions.RotView@. Former users of @rotView@ can simply replace
|
||||
-- @rotView True@ with @moveTo Next NonEmptyWS@, and so on.
|
||||
-- @rotView True@ with @moveTo Next (Not emptyWS)@, and so on.
|
||||
--
|
||||
-- If you want to exactly replicate the action of @rotView@ (cycling
|
||||
-- through workspace in order lexicographically by tag, instead of in
|
||||
-- the order specified in the config), it can be implemented as:
|
||||
--
|
||||
-- > rotView b = do t <- findWorkspace getSortByTag (bToDir b) NonEmptyWS 1
|
||||
-- > rotView b = do t <- findWorkspace getSortByTag (bToDir b) (Not emptyWS) 1
|
||||
-- > windows . greedyView $ t
|
||||
-- > where bToDir True = Next
|
||||
-- > bToDir False = Prev
|
||||
@@ -63,6 +64,11 @@ module XMonad.Actions.CycleWS (
|
||||
|
||||
, Direction1D(..)
|
||||
, WSType(..)
|
||||
, emptyWS
|
||||
, hiddenWS
|
||||
, anyWS
|
||||
, wsTagGroup
|
||||
, ignoringWSs
|
||||
|
||||
, shiftTo
|
||||
, moveTo
|
||||
@@ -78,9 +84,7 @@ module XMonad.Actions.CycleWS (
|
||||
|
||||
) where
|
||||
|
||||
import Data.List ( find, findIndex )
|
||||
import Data.Maybe ( isNothing, isJust )
|
||||
|
||||
import XMonad.Prelude (find, findIndex, isJust, isNothing, liftM2)
|
||||
import XMonad hiding (workspaces)
|
||||
import qualified XMonad.Hooks.WorkspaceHistory as WH
|
||||
import XMonad.StackSet hiding (filter)
|
||||
@@ -112,9 +116,9 @@ import XMonad.Util.WorkspaceCompare
|
||||
-- You can also get fancier with 'moveTo', 'shiftTo', and 'findWorkspace'.
|
||||
-- For example:
|
||||
--
|
||||
-- > , ((modm , xK_f), moveTo Next EmptyWS) -- find a free workspace
|
||||
-- > , ((modm , xK_f), moveTo Next emptyWS) -- find a free workspace
|
||||
-- > , ((modm .|. controlMask, xK_Right), -- a crazy keybinding!
|
||||
-- > do t <- findWorkspace getSortByXineramaRule Next NonEmptyWS 2
|
||||
-- > do t <- findWorkspace getSortByXineramaRule Next (Not emptyWS) 2
|
||||
-- > windows . view $ t )
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
@@ -201,8 +205,7 @@ skipTags wss ids = filter ((`notElem` ids) . tag) wss
|
||||
lastViewedHiddenExcept :: [WorkspaceId] -> X (Maybe WorkspaceId)
|
||||
lastViewedHiddenExcept skips = do
|
||||
hs <- gets $ map tag . flip skipTags skips . hidden . windowset
|
||||
vs <- WH.workspaceHistory
|
||||
return $ choose hs (find (`elem` hs) vs)
|
||||
choose hs . find (`elem` hs) <$> WH.workspaceHistory
|
||||
where choose [] _ = Nothing
|
||||
choose (h:_) Nothing = Just h
|
||||
choose _ vh@(Just _) = vh
|
||||
@@ -213,8 +216,8 @@ switchWorkspace d = wsBy d >>= windows . greedyView
|
||||
shiftBy :: Int -> X ()
|
||||
shiftBy d = wsBy d >>= windows . shift
|
||||
|
||||
wsBy :: Int -> X (WorkspaceId)
|
||||
wsBy = findWorkspace getSortByIndex Next AnyWS
|
||||
wsBy :: Int -> X WorkspaceId
|
||||
wsBy = findWorkspace getSortByIndex Next anyWS
|
||||
|
||||
{- $taketwo
|
||||
|
||||
@@ -223,7 +226,7 @@ through subsets of workspaces.
|
||||
|
||||
For example,
|
||||
|
||||
> moveTo Next EmptyWS
|
||||
> moveTo Next emptyWS
|
||||
|
||||
will move to the first available workspace with no windows, and
|
||||
|
||||
@@ -234,6 +237,13 @@ the letter 'p' in its name. =)
|
||||
|
||||
-}
|
||||
|
||||
{-# DEPRECATED EmptyWS "Use emptyWS instead." #-}
|
||||
{-# DEPRECATED HiddenWS "Use hiddenWS instead." #-}
|
||||
{-# DEPRECATED NonEmptyWS "Use Not emptyWS instead." #-}
|
||||
{-# DEPRECATED HiddenNonEmptyWS "Use hiddenWS :&: Not emptyWS instead." #-}
|
||||
{-# DEPRECATED HiddenEmptyWS "Use hiddenWS :&: emptyWS instead." #-}
|
||||
{-# DEPRECATED AnyWS "Use anyWS instead." #-}
|
||||
{-# DEPRECATED WSTagGroup "Use wsTagGroup instead." #-}
|
||||
-- | What type of workspaces should be included in the cycle?
|
||||
data WSType = EmptyWS -- ^ cycle through empty workspaces
|
||||
| NonEmptyWS -- ^ cycle through non-empty workspaces
|
||||
@@ -248,6 +258,11 @@ data WSType = EmptyWS -- ^ cycle through empty workspaces
|
||||
| WSIs (X (WindowSpace -> Bool))
|
||||
-- ^ cycle through workspaces satisfying
|
||||
-- an arbitrary predicate
|
||||
| WSType :&: WSType -- ^ cycle through workspaces satisfying both
|
||||
-- predicates.
|
||||
| WSType :|: WSType -- ^ cycle through workspaces satisfying one of
|
||||
-- the predicates.
|
||||
| Not WSType -- ^ cycle through workspaces not satisfying the predicate
|
||||
|
||||
-- | Convert a WSType value to a predicate on workspaces.
|
||||
wsTypeToPred :: WSType -> X (WindowSpace -> Bool)
|
||||
@@ -262,10 +277,46 @@ wsTypeToPred HiddenEmptyWS = do ne <- wsTypeToPred EmptyWS
|
||||
hi <- wsTypeToPred HiddenWS
|
||||
return (\w -> hi w && ne w)
|
||||
wsTypeToPred AnyWS = return (const True)
|
||||
wsTypeToPred (WSTagGroup sep) = do cur <- (groupName.workspace.current) `fmap` gets windowset
|
||||
wsTypeToPred (WSTagGroup sep) = do cur <- groupName.workspace.current <$> gets windowset
|
||||
return $ (cur ==).groupName
|
||||
where groupName = takeWhile (/=sep).tag
|
||||
wsTypeToPred (WSIs p) = p
|
||||
wsTypeToPred (WSIs p ) = p
|
||||
wsTypeToPred (p :&: q) = liftM2 (&&) <$> wsTypeToPred p <*> wsTypeToPred q
|
||||
wsTypeToPred (p :|: q) = liftM2 (||) <$> wsTypeToPred p <*> wsTypeToPred q
|
||||
wsTypeToPred (Not p ) = fmap not <$> wsTypeToPred p
|
||||
|
||||
-- | Cycle through empty workspaces
|
||||
emptyWS :: WSType
|
||||
emptyWS = WSIs . return $ isNothing . stack
|
||||
|
||||
-- | Cycle through non-visible workspaces
|
||||
hiddenWS :: WSType
|
||||
hiddenWS = WSIs $ do
|
||||
hs <- gets (map tag . hidden . windowset)
|
||||
return $ (`elem` hs) . tag
|
||||
|
||||
-- | Cycle through all workspaces
|
||||
anyWS :: WSType
|
||||
anyWS = WSIs . return $ const True
|
||||
|
||||
-- | Cycle through workspaces that are not in the given list. This could, for
|
||||
-- example, be used for skipping the workspace reserved for
|
||||
-- "XMonad.Util.NamedScratchpad":
|
||||
--
|
||||
-- > moveTo Next $ hiddenWS :&: Not emptyWS :&: ignoringWSs [scratchpadWorkspaceTag]
|
||||
--
|
||||
ignoringWSs :: [WorkspaceId] -> WSType
|
||||
ignoringWSs ts = WSIs . return $ (`notElem` ts) . tag
|
||||
|
||||
-- | Cycle through workspaces in the same group, the
|
||||
-- group name is all characters up to the first
|
||||
-- separator character or the end of the tag
|
||||
wsTagGroup :: Char -> WSType
|
||||
wsTagGroup sep = WSIs $ do
|
||||
cur <- groupName . workspace . current <$> gets windowset
|
||||
return $ (cur ==) . groupName
|
||||
where groupName = takeWhile (/= sep) . tag
|
||||
|
||||
|
||||
-- | View the next workspace in the given direction that satisfies
|
||||
-- the given condition.
|
||||
@@ -299,7 +350,7 @@ findWorkspace :: X WorkspaceSort -> Direction1D -> WSType -> Int -> X WorkspaceI
|
||||
findWorkspace s dir t n = findWorkspaceGen s (wsTypeToPred t) (maybeNegate dir n)
|
||||
where
|
||||
maybeNegate Next d = d
|
||||
maybeNegate Prev d = (-d)
|
||||
maybeNegate Prev d = -d
|
||||
|
||||
findWorkspaceGen :: X WorkspaceSort -> X (WindowSpace -> Bool) -> Int -> X WorkspaceId
|
||||
findWorkspaceGen _ _ 0 = gets (currentTag . windowset)
|
||||
@@ -309,7 +360,7 @@ findWorkspaceGen sortX wsPredX d = do
|
||||
ws <- gets windowset
|
||||
let cur = workspace (current ws)
|
||||
sorted = sort (workspaces ws)
|
||||
pivoted = let (a,b) = span ((/= (tag cur)) . tag) sorted in b ++ a
|
||||
pivoted = let (a,b) = span ((/= tag cur) . tag) sorted in b ++ a
|
||||
ws' = filter wsPred pivoted
|
||||
mCurIx = findWsIndex cur ws'
|
||||
d' = if d > 0 then d - 1 else d
|
||||
@@ -321,7 +372,7 @@ findWorkspaceGen sortX wsPredX d = do
|
||||
return $ tag next
|
||||
|
||||
findWsIndex :: WindowSpace -> [WindowSpace] -> Maybe Int
|
||||
findWsIndex ws wss = findIndex ((== tag ws) . tag) wss
|
||||
findWsIndex ws = findIndex ((== tag ws) . tag)
|
||||
|
||||
-- | View next screen
|
||||
nextScreen :: X ()
|
||||
@@ -349,7 +400,7 @@ the default screen keybindings:
|
||||
> , (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
|
||||
|
||||
-}
|
||||
screenBy :: Int -> X (ScreenId)
|
||||
screenBy :: Int -> X ScreenId
|
||||
screenBy d = do ws <- gets windowset
|
||||
--let ss = sortBy screen (screens ws)
|
||||
let now = screen (current ws)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
--------------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.CycleWindows
|
||||
-- Description : Cycle windows while maintaining focus in place.
|
||||
-- Copyright : (c) Wirt Wolff <wirtwolff@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -116,7 +117,7 @@ cycleRecentWindows :: [KeySym] -- ^ A list of modifier keys used when invoking t
|
||||
-- If it's the same as the first key, it is effectively ignored.
|
||||
-> X ()
|
||||
cycleRecentWindows = cycleStacks' stacks where
|
||||
stacks s = map (shiftToFocus' `flip` s) (wins s)
|
||||
stacks s = map (`shiftToFocus'` s) (wins s)
|
||||
wins (W.Stack t l r) = t : r ++ reverse l
|
||||
|
||||
|
||||
@@ -205,7 +206,7 @@ rotFocused' :: ([a] -> [a]) -> W.Stack a -> W.Stack a
|
||||
rotFocused' _ s@(W.Stack _ [] []) = s
|
||||
rotFocused' f (W.Stack t [] (r:rs)) = W.Stack t' [] (r:rs') -- Master has focus
|
||||
where (t':rs') = f (t:rs)
|
||||
rotFocused' f s@(W.Stack _ _ _) = rotSlaves' f s -- otherwise
|
||||
rotFocused' f s@W.Stack{} = rotSlaves' f s -- otherwise
|
||||
|
||||
|
||||
-- $unfocused
|
||||
|
@@ -1,7 +1,7 @@
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.CycleWorkspaceByScreen
|
||||
-- Description : Cycle workspaces in a screen-aware fashion.
|
||||
-- Copyright : (c) 2017 Ivan Malison
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -23,14 +23,12 @@ module XMonad.Actions.CycleWorkspaceByScreen (
|
||||
, repeatableAction
|
||||
) where
|
||||
|
||||
import Control.Monad
|
||||
import Data.IORef
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
|
||||
import Graphics.X11.Xlib.Extras
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude
|
||||
import XMonad.Hooks.WorkspaceHistory
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
@@ -51,7 +49,7 @@ repeatableAction mods pressHandler = do
|
||||
return (t, s)
|
||||
handleEvent (t, s)
|
||||
| t == keyRelease && s `elem` mods = return ()
|
||||
| otherwise = (pressHandler t s) >> getNextEvent >>= handleEvent
|
||||
| otherwise = pressHandler t s >> getNextEvent >>= handleEvent
|
||||
|
||||
io $ grabKeyboard d root False grabModeAsync grabModeAsync currentTime
|
||||
getNextEvent >>= handleEvent
|
||||
@@ -83,9 +81,9 @@ cycleWorkspaceOnScreen screenId mods nextKey prevKey = workspaceHistoryTransacti
|
||||
current <- readIORef currentWSIndex
|
||||
modifyIORef
|
||||
currentWSIndex
|
||||
((`mod` (length cycleWorkspaces)) . (+ increment))
|
||||
((`mod` length cycleWorkspaces) . (+ increment))
|
||||
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
|
||||
repeatableAction mods $
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.DeManage
|
||||
-- Description : Cease management of a window without unmapping it.
|
||||
-- Copyright : (c) Spencer Janssen <spencerjanssen@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.DwmPromote
|
||||
-- Description : DWM-like swap function for xmonad.
|
||||
-- Copyright : (c) Miikka Koskinen 2007
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
|
@@ -1,8 +1,7 @@
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.DynamicProjects
|
||||
-- Description : Treat workspaces as individual project areas.
|
||||
-- Copyright : (c) Peter J. Jones
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -39,18 +38,14 @@ module XMonad.Actions.DynamicProjects
|
||||
, lookupProject
|
||||
, currentProject
|
||||
, activateProject
|
||||
, modifyProject
|
||||
) where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
import Control.Applicative ((<|>))
|
||||
import Control.Monad (when, unless)
|
||||
import Data.Char (isSpace)
|
||||
import Data.List (sort, union, stripPrefix)
|
||||
import Data.Map.Strict (Map)
|
||||
import qualified Data.Map.Strict as Map
|
||||
import Data.Maybe (fromMaybe, isNothing)
|
||||
import Data.Monoid ((<>))
|
||||
import System.Directory (setCurrentDirectory, getHomeDirectory)
|
||||
import System.Directory (setCurrentDirectory, getHomeDirectory, makeAbsolute)
|
||||
import XMonad.Prelude
|
||||
import XMonad
|
||||
import XMonad.Actions.DynamicWorkspaces
|
||||
import XMonad.Prompt
|
||||
@@ -130,14 +125,14 @@ data Project = Project
|
||||
{ projectName :: !ProjectName -- ^ Workspace name.
|
||||
, projectDirectory :: !FilePath -- ^ Working directory.
|
||||
, projectStartHook :: !(Maybe (X ())) -- ^ Optional start-up hook.
|
||||
} deriving Typeable
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Internal project state.
|
||||
data ProjectState = ProjectState
|
||||
{ projects :: !ProjectTable
|
||||
, previousProject :: !(Maybe WorkspaceId)
|
||||
} deriving Typeable
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
instance ExtensionClass ProjectState where
|
||||
@@ -145,24 +140,24 @@ instance ExtensionClass ProjectState where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Internal types for working with XPrompt.
|
||||
data ProjectPrompt = ProjectPrompt ProjectMode [ProjectName]
|
||||
data ProjectPrompt = ProjectPrompt XPConfig ProjectMode [ProjectName]
|
||||
data ProjectMode = SwitchMode | ShiftMode | RenameMode | DirMode
|
||||
|
||||
instance XPrompt ProjectPrompt where
|
||||
showXPrompt (ProjectPrompt submode _) =
|
||||
showXPrompt (ProjectPrompt _ submode _) =
|
||||
case submode of
|
||||
SwitchMode -> "Switch or Create Project: "
|
||||
ShiftMode -> "Send Window to Project: "
|
||||
RenameMode -> "New Project Name: "
|
||||
DirMode -> "Change Project Directory: "
|
||||
|
||||
completionFunction (ProjectPrompt RenameMode _) = return . (:[])
|
||||
completionFunction (ProjectPrompt DirMode _) =
|
||||
let xpt = directoryMultipleModes "" (const $ return ())
|
||||
completionFunction (ProjectPrompt _ RenameMode _) = return . (:[])
|
||||
completionFunction (ProjectPrompt c DirMode _) =
|
||||
let xpt = directoryMultipleModes' (complCaseSensitivity c) "" (const $ return ())
|
||||
in completionFunction xpt
|
||||
completionFunction (ProjectPrompt _ ns) = mkComplFunFromList' ns
|
||||
completionFunction (ProjectPrompt c _ ns) = mkComplFunFromList' c ns
|
||||
|
||||
modeAction (ProjectPrompt SwitchMode _) buf auto = do
|
||||
modeAction (ProjectPrompt _ SwitchMode _) buf auto = do
|
||||
let name = if null auto then buf else auto
|
||||
ps <- XS.gets projects
|
||||
|
||||
@@ -171,18 +166,19 @@ instance XPrompt ProjectPrompt where
|
||||
Nothing | null name -> return ()
|
||||
| otherwise -> switchProject (defProject name)
|
||||
|
||||
modeAction (ProjectPrompt ShiftMode _) buf auto = do
|
||||
modeAction (ProjectPrompt _ ShiftMode _) buf auto = do
|
||||
let name = if null auto then buf else auto
|
||||
ps <- XS.gets projects
|
||||
shiftToProject . fromMaybe (defProject name) $ Map.lookup name ps
|
||||
|
||||
modeAction (ProjectPrompt RenameMode _) name _ =
|
||||
modeAction (ProjectPrompt _ RenameMode _) name _ =
|
||||
when (not (null name) && not (all isSpace name)) $ do
|
||||
renameWorkspaceByName name
|
||||
modifyProject (\p -> p { projectName = name })
|
||||
|
||||
modeAction (ProjectPrompt DirMode _) buf auto = do
|
||||
let dir = if null auto then buf else auto
|
||||
modeAction (ProjectPrompt _ DirMode _) buf auto = do
|
||||
let dir' = if null auto then buf else auto
|
||||
dir <- io $ makeAbsolute dir'
|
||||
modifyProject (\p -> p { projectDirectory = dir })
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@@ -230,7 +226,7 @@ dynamicProjectsStartupHook ps = XS.modify go
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Find a project based on its name.
|
||||
lookupProject :: ProjectName -> X (Maybe Project)
|
||||
lookupProject name = Map.lookup name `fmap` XS.gets projects
|
||||
lookupProject name = Map.lookup name <$> XS.gets projects
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- | Fetch the current project (the one being used for the currently
|
||||
@@ -326,11 +322,11 @@ changeProjectDirPrompt = projectPrompt [ DirMode
|
||||
-- | Prompt for a project name.
|
||||
projectPrompt :: [ProjectMode] -> XPConfig -> X ()
|
||||
projectPrompt submodes c = do
|
||||
ws <- map W.tag `fmap` gets (W.workspaces . windowset)
|
||||
ws <- map W.tag <$> gets (W.workspaces . windowset)
|
||||
ps <- XS.gets projects
|
||||
|
||||
let names = sort (Map.keys ps `union` ws)
|
||||
modes = map (\m -> XPT $ ProjectPrompt m names) submodes
|
||||
modes = map (\m -> XPT $ ProjectPrompt c m names) submodes
|
||||
|
||||
mkXPromptWithModes modes c
|
||||
|
||||
|
@@ -1,8 +1,7 @@
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.DynamicWorkspaceGroups
|
||||
-- Description : Dynamically manage workspace groups in multi-head setups.
|
||||
-- Copyright : (c) Brent Yorgey 2009
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
@@ -34,17 +33,22 @@ module XMonad.Actions.DynamicWorkspaceGroups
|
||||
, promptWSGroupForget
|
||||
|
||||
, WSGPrompt
|
||||
-- * TopicSpace Integration
|
||||
-- $topics
|
||||
, viewTopicGroup
|
||||
, promptTopicGroupView
|
||||
) where
|
||||
|
||||
import Data.List (find)
|
||||
import Control.Arrow ((&&&))
|
||||
import qualified Data.Map as M
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude (find, for_)
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import XMonad.Prompt
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
import XMonad.Actions.TopicSpace
|
||||
|
||||
-- $usage
|
||||
-- You can use this module by importing it into your ~\/.xmonad\/xmonad.hs file:
|
||||
@@ -63,14 +67,14 @@ type WSGroup = [(ScreenId,WorkspaceId)]
|
||||
|
||||
type WSGroupId = String
|
||||
|
||||
data WSGroupStorage = WSG { unWSG :: M.Map WSGroupId WSGroup }
|
||||
deriving (Typeable, Read, Show)
|
||||
newtype WSGroupStorage = WSG { unWSG :: M.Map WSGroupId WSGroup }
|
||||
deriving (Read, Show)
|
||||
|
||||
withWSG :: (M.Map WSGroupId WSGroup -> M.Map WSGroupId WSGroup) -> WSGroupStorage -> WSGroupStorage
|
||||
withWSG f = WSG . f . unWSG
|
||||
|
||||
instance ExtensionClass WSGroupStorage where
|
||||
initialValue = WSG $ M.empty
|
||||
initialValue = WSG M.empty
|
||||
extensionType = PersistentExtension
|
||||
|
||||
-- | Add a new workspace group of the given name, mapping to an
|
||||
@@ -85,9 +89,7 @@ addWSGroup :: WSGroupId -> [WorkspaceId] -> X ()
|
||||
addWSGroup name wids = withWindowSet $ \w -> do
|
||||
let wss = map ((W.tag . W.workspace) &&& W.screen) $ W.screens w
|
||||
wmap = mapM (strength . (flip lookup wss &&& id)) wids
|
||||
case wmap of
|
||||
Just ps -> addRawWSGroup name ps
|
||||
Nothing -> return ()
|
||||
for_ wmap (addRawWSGroup name)
|
||||
where strength (ma, b) = ma >>= \a -> return (a,b)
|
||||
|
||||
-- | Give a name to the current workspace group.
|
||||
@@ -103,20 +105,23 @@ forgetWSGroup = XS.modify . withWSG . M.delete
|
||||
|
||||
-- | View the workspace group with the given name.
|
||||
viewWSGroup :: WSGroupId -> X ()
|
||||
viewWSGroup name = do
|
||||
WSG m <- XS.get
|
||||
case M.lookup name m of
|
||||
Just grp -> mapM_ (uncurry viewWS) grp
|
||||
Nothing -> return ()
|
||||
viewWSGroup = viewGroup (windows . W.greedyView)
|
||||
|
||||
-- | View the given workspace on the given screen.
|
||||
viewWS :: ScreenId -> WorkspaceId -> X ()
|
||||
viewWS sid wid = do
|
||||
-- | Internal function for viewing a group.
|
||||
viewGroup :: (WorkspaceId -> X ()) -> WSGroupId -> X ()
|
||||
viewGroup fview name = do
|
||||
WSG m <- XS.get
|
||||
for_ (M.lookup name m) $
|
||||
mapM_ (uncurry (viewWS fview))
|
||||
|
||||
-- | View the given workspace on the given screen, using the provided function.
|
||||
viewWS :: (WorkspaceId -> X ()) -> ScreenId -> WorkspaceId -> X ()
|
||||
viewWS fview sid wid = do
|
||||
mw <- findScreenWS sid
|
||||
case mw of
|
||||
Just w -> do
|
||||
windows $ W.view w
|
||||
windows $ W.greedyView wid
|
||||
fview wid
|
||||
Nothing -> return ()
|
||||
|
||||
-- | Find the workspace which is currently on the given screen.
|
||||
@@ -124,16 +129,20 @@ findScreenWS :: ScreenId -> X (Maybe WorkspaceId)
|
||||
findScreenWS sid = withWindowSet $
|
||||
return . fmap (W.tag . W.workspace) . find ((==sid) . W.screen) . W.screens
|
||||
|
||||
data WSGPrompt = WSGPrompt String
|
||||
newtype WSGPrompt = WSGPrompt String
|
||||
|
||||
instance XPrompt WSGPrompt where
|
||||
showXPrompt (WSGPrompt s) = s
|
||||
|
||||
-- | Prompt for a workspace group to view.
|
||||
promptWSGroupView :: XPConfig -> String -> X ()
|
||||
promptWSGroupView xp s = do
|
||||
promptWSGroupView = promptGroupView viewWSGroup
|
||||
|
||||
-- | Internal function for making a prompt to view a workspace group
|
||||
promptGroupView :: (WSGroupId -> X ()) -> XPConfig -> String -> X ()
|
||||
promptGroupView fview xp s = do
|
||||
gs <- fmap (M.keys . unWSG) XS.get
|
||||
mkXPrompt (WSGPrompt s) xp (mkComplFunFromList' gs) viewWSGroup
|
||||
mkXPrompt (WSGPrompt s) xp (mkComplFunFromList' xp gs) fview
|
||||
|
||||
-- | Prompt for a name for the current workspace group.
|
||||
promptWSGroupAdd :: XPConfig -> String -> X ()
|
||||
@@ -144,4 +153,25 @@ promptWSGroupAdd xp s =
|
||||
promptWSGroupForget :: XPConfig -> String -> X ()
|
||||
promptWSGroupForget xp s = do
|
||||
gs <- fmap (M.keys . unWSG) XS.get
|
||||
mkXPrompt (WSGPrompt s) xp (mkComplFunFromList' gs) forgetWSGroup
|
||||
mkXPrompt (WSGPrompt s) xp (mkComplFunFromList' xp gs) forgetWSGroup
|
||||
|
||||
-- $topics
|
||||
-- You can use this module with "XMonad.Actions.TopicSpace" — just replace
|
||||
-- 'promptWSGroupView' with 'promptTopicGroupView':
|
||||
--
|
||||
-- > , ("M-y n", promptWSGroupAdd myXPConfig "Name this group: ")
|
||||
-- > , ("M-y g", promptTopicGroupView myTopicConfig myXPConfig "Go to group: ")
|
||||
-- > , ("M-y d", promptWSGroupForget myXPConfig "Forget group: ")
|
||||
--
|
||||
-- It's also a good idea to replace 'spawn' with
|
||||
-- 'XMonad.Actions.SpawnOn.spawnOn' or 'XMonad.Actions.SpawnOn.spawnHere' in
|
||||
-- your topic actions, so everything is spawned where it should be.
|
||||
|
||||
-- | Prompt for a workspace group to view, treating the workspaces as topics.
|
||||
promptTopicGroupView :: TopicConfig -> XPConfig -> String -> X ()
|
||||
promptTopicGroupView = promptGroupView . viewTopicGroup
|
||||
|
||||
-- | View the workspace group with the given name, treating the workspaces as
|
||||
-- topics.
|
||||
viewTopicGroup :: TopicConfig -> WSGroupId -> X ()
|
||||
viewTopicGroup = viewGroup . switchTopic
|
||||
|
@@ -1,8 +1,7 @@
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.DynamicWorkspaceOrder
|
||||
-- Description : Remember a dynamically updateable ordering on workspaces.
|
||||
-- Copyright : (c) Brent Yorgey 2009
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
@@ -12,7 +11,7 @@
|
||||
--
|
||||
-- Remember a dynamically updateable ordering on workspaces, together
|
||||
-- with tools for using this ordering with "XMonad.Actions.CycleWS"
|
||||
-- and "XMonad.Hooks.DynamicLog".
|
||||
-- and "XMonad.Hooks.StatusBar.PP".
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
@@ -23,6 +22,8 @@ module XMonad.Actions.DynamicWorkspaceOrder
|
||||
getWsCompareByOrder
|
||||
, getSortByOrder
|
||||
, swapWith
|
||||
, swapWithCurrent
|
||||
, swapOrder
|
||||
, updateName
|
||||
, removeName
|
||||
|
||||
@@ -30,6 +31,7 @@ module XMonad.Actions.DynamicWorkspaceOrder
|
||||
, moveToGreedy
|
||||
, shiftTo
|
||||
|
||||
, withNthWorkspace'
|
||||
, withNthWorkspace
|
||||
|
||||
) where
|
||||
@@ -43,7 +45,7 @@ import XMonad.Actions.CycleWS (findWorkspace, WSType(..), Direction1D(..), doTo)
|
||||
|
||||
import qualified Data.Map as M
|
||||
import qualified Data.Set as S
|
||||
import Data.Maybe (fromJust, fromMaybe)
|
||||
import XMonad.Prelude (fromJust, fromMaybe)
|
||||
import Data.Ord (comparing)
|
||||
|
||||
-- $usage
|
||||
@@ -66,10 +68,10 @@ import Data.Ord (comparing)
|
||||
-- order of workspaces must be updated to use the auxiliary ordering.
|
||||
--
|
||||
-- To change the order in which workspaces are displayed by
|
||||
-- "XMonad.Hooks.DynamicLog", use 'getSortByOrder' in your
|
||||
-- 'XMonad.Hooks.DynamicLog.ppSort' field, for example:
|
||||
-- "XMonad.Hooks.StatusBar.PP", use 'getSortByOrder' in your
|
||||
-- 'XMonad.Hooks.StatusBar.PP.ppSort' field, for example:
|
||||
--
|
||||
-- > ... dynamicLogWithPP $ byorgeyPP {
|
||||
-- > myPP = ... byorgeyPP {
|
||||
-- > ...
|
||||
-- > , ppSort = DO.getSortByOrder
|
||||
-- > ...
|
||||
@@ -88,8 +90,8 @@ import Data.Ord (comparing)
|
||||
-- tweak as desired.
|
||||
|
||||
-- | Extensible state storage for the workspace order.
|
||||
data WSOrderStorage = WSO { unWSO :: Maybe (M.Map WorkspaceId Int) }
|
||||
deriving (Typeable, Read, Show)
|
||||
newtype WSOrderStorage = WSO { unWSO :: Maybe (M.Map WorkspaceId Int) }
|
||||
deriving (Read, Show)
|
||||
|
||||
instance ExtensionClass WSOrderStorage where
|
||||
initialValue = WSO Nothing
|
||||
@@ -183,13 +185,19 @@ moveToGreedy dir t = doTo dir t getSortByOrder (windows . W.greedyView)
|
||||
shiftTo :: Direction1D -> WSType -> X ()
|
||||
shiftTo dir t = doTo dir t getSortByOrder (windows . W.shift)
|
||||
|
||||
-- | Do something with the nth workspace in the dynamic order after
|
||||
-- transforming it. The callback is given the workspace's tag as well
|
||||
-- as the 'WindowSet' of the workspace itself.
|
||||
withNthWorkspace' :: ([WorkspaceId] -> [WorkspaceId]) -> (String -> WindowSet -> WindowSet) -> Int -> X ()
|
||||
withNthWorkspace' tr job wnum = do
|
||||
sort <- getSortByOrder
|
||||
ws <- gets (tr . map W.tag . sort . W.workspaces . windowset)
|
||||
case drop wnum ws of
|
||||
(w:_) -> windows $ job w
|
||||
[] -> return ()
|
||||
|
||||
-- | Do something with the nth workspace in the dynamic order. The
|
||||
-- callback is given the workspace's tag as well as the 'WindowSet'
|
||||
-- of the workspace itself.
|
||||
withNthWorkspace :: (String -> WindowSet -> WindowSet) -> Int -> X ()
|
||||
withNthWorkspace job wnum = do
|
||||
sort <- getSortByOrder
|
||||
ws <- gets (map W.tag . sort . W.workspaces . windowset)
|
||||
case drop wnum ws of
|
||||
(w:_) -> windows $ job w
|
||||
[] -> return ()
|
||||
withNthWorkspace = withNthWorkspace' id
|
||||
|
@@ -1,8 +1,7 @@
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.DynamicWorkspaces
|
||||
-- Description : Provides bindings to add and delete workspaces.
|
||||
-- Copyright : (c) David Roundy <droundy@darcs.net>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -35,14 +34,12 @@ module XMonad.Actions.DynamicWorkspaces (
|
||||
WorkspaceIndex
|
||||
) where
|
||||
|
||||
import XMonad.Prelude (find, isNothing, nub, when)
|
||||
import XMonad hiding (workspaces)
|
||||
import XMonad.StackSet hiding (filter, modify, delete)
|
||||
import XMonad.Prompt.Workspace ( Wor(Wor), workspacePrompt )
|
||||
import XMonad.Prompt ( XPConfig, mkXPrompt )
|
||||
import XMonad.Util.WorkspaceCompare ( getSortByIndex )
|
||||
import Data.List (find)
|
||||
import Data.Maybe (isNothing)
|
||||
import Control.Monad (when)
|
||||
import qualified Data.Map.Strict as Map
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
|
||||
@@ -88,8 +85,8 @@ type WorkspaceIndex = Int
|
||||
|
||||
-- | Internal dynamic project state that stores a mapping between
|
||||
-- workspace indexes and workspace tags.
|
||||
data DynamicWorkspaceState = DynamicWorkspaceState {workspaceIndexMap :: Map.Map WorkspaceIndex WorkspaceTag}
|
||||
deriving (Typeable, Read, Show)
|
||||
newtype DynamicWorkspaceState = DynamicWorkspaceState {workspaceIndexMap :: Map.Map WorkspaceIndex WorkspaceTag}
|
||||
deriving (Read, Show)
|
||||
|
||||
instance ExtensionClass DynamicWorkspaceState where
|
||||
initialValue = DynamicWorkspaceState Map.empty
|
||||
@@ -108,7 +105,7 @@ withWorkspaceIndex job widx = do
|
||||
maybe (return ()) (windows . job) wtag
|
||||
where
|
||||
ilookup :: WorkspaceIndex -> X (Maybe WorkspaceTag)
|
||||
ilookup idx = Map.lookup idx `fmap` XS.gets workspaceIndexMap
|
||||
ilookup idx = Map.lookup idx <$> XS.gets workspaceIndexMap
|
||||
|
||||
|
||||
mkCompl :: [String] -> String -> IO [String]
|
||||
@@ -127,14 +124,14 @@ renameWorkspace conf = workspacePrompt conf renameWorkspaceByName
|
||||
|
||||
renameWorkspaceByName :: String -> X ()
|
||||
renameWorkspaceByName w = do old <- gets (currentTag . windowset)
|
||||
windows $ \s -> let sett wk = wk { tag = w }
|
||||
setscr scr = scr { workspace = sett $ workspace scr }
|
||||
sets q = q { current = setscr $ current q }
|
||||
windows $ \s -> let sett wk = wk { tag = w }
|
||||
setscr scr = scr { workspace = sett $ workspace scr }
|
||||
sets q = q { current = setscr $ current q }
|
||||
in sets $ removeWorkspace' w s
|
||||
updateIndexMap old w
|
||||
where updateIndexMap old new = do
|
||||
updateIndexMap old w
|
||||
where updateIndexMap oldIM newIM = do
|
||||
wmap <- XS.gets workspaceIndexMap
|
||||
XS.modify $ \s -> s {workspaceIndexMap = Map.map (\t -> if t == old then new else t) wmap}
|
||||
XS.modify $ \s -> s {workspaceIndexMap = Map.map (\t -> if t == oldIM then newIM else t) wmap}
|
||||
|
||||
toNthWorkspace :: (String -> X ()) -> Int -> X ()
|
||||
toNthWorkspace job wnum = do sort <- getSortByIndex
|
||||
@@ -241,20 +238,20 @@ isEmpty t = do wsl <- gets $ workspaces . windowset
|
||||
return $ maybe True (isNothing . stack) mws
|
||||
|
||||
addHiddenWorkspace' :: (Workspace i l a -> [Workspace i l a] -> [Workspace i l a]) -> i -> l -> StackSet i l a sid sd -> StackSet i l a sid sd
|
||||
addHiddenWorkspace' add newtag l s@(StackSet { hidden = ws }) = s { hidden = add (Workspace newtag l Nothing) ws }
|
||||
addHiddenWorkspace' add newtag l s@StackSet{ hidden = ws } = s { hidden = add (Workspace newtag l Nothing) ws }
|
||||
|
||||
-- | Remove the hidden workspace with the given tag from the StackSet, if
|
||||
-- it exists. All the windows in that workspace are moved to the current
|
||||
-- workspace.
|
||||
removeWorkspace' :: (Eq i) => i -> StackSet i l a sid sd -> StackSet i l a sid sd
|
||||
removeWorkspace' torem s@(StackSet { current = scr@(Screen { workspace = wc })
|
||||
, hidden = hs })
|
||||
removeWorkspace' :: (Eq i, Eq a) => i -> StackSet i l a sid sd -> StackSet i l a sid sd
|
||||
removeWorkspace' torem s@StackSet{ current = scr@Screen { workspace = wc }
|
||||
, hidden = hs }
|
||||
= let (xs, ys) = break ((== torem) . tag) hs
|
||||
in removeWorkspace'' xs ys
|
||||
where meld Nothing Nothing = Nothing
|
||||
meld x Nothing = x
|
||||
meld Nothing x = x
|
||||
meld (Just x) (Just y) = differentiate (integrate x ++ integrate y)
|
||||
meld (Just x) (Just y) = differentiate . nub $ integrate x ++ integrate y
|
||||
removeWorkspace'' xs (y:ys) = s { current = scr { workspace = wc { stack = meld (stack y) (stack wc) } }
|
||||
, hidden = xs ++ ys }
|
||||
removeWorkspace'' _ _ = s
|
||||
|
388
XMonad/Actions/EasyMotion.hs
Normal file
388
XMonad/Actions/EasyMotion.hs
Normal file
@@ -0,0 +1,388 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE MultiWayIf #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.EasyMotion
|
||||
-- Description : Focus a visible window using a key chord.
|
||||
-- Copyright : (c) Matt Kingston <mattkingston@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : mattkingston@gmail.com
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Provides functionality to use key chords to focus a visible window. Overlays a unique key chord
|
||||
-- (a string) above each visible window and allows the user to select a window by typing that
|
||||
-- chord.
|
||||
-- Inspired by <https://github.com/easymotion/vim-easymotion vim-easymotion>.
|
||||
-- Thanks to <https://github.com/larkery Tom Hinton> for some feature inspiration and window
|
||||
-- sorting code.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.EasyMotion ( -- * Usage
|
||||
-- $usage
|
||||
selectWindow
|
||||
|
||||
-- * Configuration
|
||||
, EasyMotionConfig(..)
|
||||
, ChordKeys(..)
|
||||
, def
|
||||
|
||||
-- * Creating overlays
|
||||
, fullSize
|
||||
, fixedSize
|
||||
, textSize
|
||||
, proportional
|
||||
, bar
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Util.Font (releaseXMF, initXMF, Align(AlignCenter), XMonadFont(..), textExtentsXMF)
|
||||
import XMonad.Util.XUtils (createNewWindow, paintAndWrite, deleteWindow, showWindow)
|
||||
|
||||
import Control.Arrow ((&&&))
|
||||
import qualified Data.Map.Strict as M (Map, elems, map, mapWithKey)
|
||||
|
||||
-- $usage
|
||||
--
|
||||
-- You can use this module's basic functionality with the following in your
|
||||
-- @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.EasyMotion (selectWindow)
|
||||
--
|
||||
-- To customise
|
||||
--
|
||||
-- > import XMonad.Actions.EasyMotion (selectWindow, EasyMotionConfig(..))
|
||||
--
|
||||
-- Then add a keybinding and an action to the 'selectWindow' function.
|
||||
-- In this case @M-f@ to focus the selected window:
|
||||
--
|
||||
-- > , ((modm, xK_f), selectWindow def >>= (`whenJust` windows . W.focusWindow))
|
||||
--
|
||||
-- Similarly, to kill a window with @M-f@:
|
||||
--
|
||||
-- > , ((modm, xK_f), selectWindow def >>= (`whenJust` killWindow))
|
||||
--
|
||||
-- See 'EasyMotionConfig' for all configuration options. A short summary follows.
|
||||
--
|
||||
-- Default chord keys are @s,d,f,j,k,l@. To customise these and display options assign
|
||||
-- different values to 'def' (the default configuration):
|
||||
--
|
||||
-- > , ((modm, xK_f), (selectWindow def{sKeys = AnyKeys [xK_f, xK_d]}) >>= (`whenJust` windows . W.focusWindow))
|
||||
--
|
||||
-- You must supply at least two different keys in the 'sKeys' list. Keys provided earlier in the list
|
||||
-- will be used preferentially—therefore, keys you would like to use more frequently should be
|
||||
-- earlier in the list.
|
||||
--
|
||||
-- To map different sets of keys to different screens. The following configuration maps keys @fdsa@
|
||||
-- to screen 0 and @hjkl@ to screen 1. Keys provided earlier in the list will be used preferentially.
|
||||
-- Providing the same key for multiple screens is possible but will break down in some scenarios.
|
||||
--
|
||||
-- > import qualified Data.Map.Strict as StrictMap (fromList)
|
||||
-- > emConf :: EasyMotionConfig
|
||||
-- > emConf = def { sKeys = PerScreenKeys $ StrictMap.fromList [(0, [xK_f, xK_d, xK_s, xK_a]), (1, [xK_h, xK_j, xK_k, xK_l])] }
|
||||
-- > -- key bindings
|
||||
-- > , ((modm, xK_f), selectWindow emConf >>= (`whenJust` windows . W.focusWindow))
|
||||
--
|
||||
-- To customise the font:
|
||||
--
|
||||
-- > , ((modm, xK_f), (selectWindow def{emFont = "xft: Sans-40"}) >>= (`whenJust` windows . W.focusWindow))
|
||||
--
|
||||
-- The 'emFont' field provided is supplied directly to the 'initXMF' function. The default is
|
||||
-- @"xft:Sans-100"@. Some example options:
|
||||
--
|
||||
-- > "xft: Sans-40"
|
||||
-- > "xft: Arial-100"
|
||||
-- > "xft: Cambria-80"
|
||||
--
|
||||
-- Customise the overlay by supplying a function to 'overlayF'. The signature is
|
||||
-- @'Position' -> 'Rectangle' -> 'Rectangle'@. The parameters are the height in pixels of
|
||||
-- the selection chord and the rectangle of the window to be overlaid. Some are provided:
|
||||
--
|
||||
-- > import XMonad.Actions.EasyMotion (selectWindow, EasyMotionConfig(..), proportional, bar, fullSize)
|
||||
-- > , ((modm, xK_f), (selectWindow def{ overlayF = proportional 0.3 }) >>= (`whenJust` windows . W.focusWindow))
|
||||
-- > , ((modm, xK_f), (selectWindow def{ overlayF = bar 0.5 }) >>= (`whenJust` windows . W.focusWindow))
|
||||
-- > , ((modm, xK_f), (selectWindow def{ overlayF = fullSize }) >>= (`whenJust` windows . W.focusWindow))
|
||||
-- > , ((modm, xK_f), (selectWindow def{ overlayF = fixedSize 300 350 }) >>= (`whenJust` windows . W.focusWindow))
|
||||
|
||||
-- TODO:
|
||||
-- - An overlay function that creates an overlay a proportion of the width XOR height of the
|
||||
-- window it's over, and with a fixed w/h proportion? E.g. overlay-height = 0.3 *
|
||||
-- target-window-height; overlay-width = 0.5 * overlay-height.
|
||||
-- - An overlay function that creates an overlay of a fixed w,h, aligned mid,mid, or parametrised
|
||||
-- alignment?
|
||||
-- - Parametrise chord generation?
|
||||
-- - W.shift example; bring window from other screen to current screen? Only useful if we don't
|
||||
-- show chords on current workspace.
|
||||
-- - Use stringToKeysym, keysymToKeycode, keycodeToKeysym, keysymToString to take a string from
|
||||
-- the user?
|
||||
-- - Think a bit more about improving functionality with floating windows.
|
||||
-- - currently, floating window z-order is not respected
|
||||
-- - could ignore floating windows
|
||||
-- - may be able to calculate the visible section of a floating window, and display the chord in
|
||||
-- that space
|
||||
-- - Provide an option to prepend the screen key to the easymotion keys (i.e. w,e,r)?
|
||||
-- - overlay alpha
|
||||
-- - Delay after selection so the user can see what they've chosen? Min-delay: 0 seconds. If
|
||||
-- there's a delay, perhaps keep the other windows covered briefly to naturally draw the user's
|
||||
-- attention to the window they've selected? Or briefly highlight the border of the selected
|
||||
-- window?
|
||||
-- - Option to cover windows that will not be selected by the current chord, such that it's
|
||||
-- slightly more obvious where to maintain focus.
|
||||
-- - Something unpleasant happens when the user provides only two keys (let's say f, d) for
|
||||
-- chords. When they have five windows open, the following chords are generated: ddd, ddf, dfd,
|
||||
-- dff, fdd. When 'f' is pressed, all chords disappear unexpectedly because we know there are no
|
||||
-- other valid options. The user expects to press 'fdd'. This is an optimisation in software but
|
||||
-- pretty bad for usability, as the user continues firing keys into their
|
||||
-- now-unexpectedly-active window. And is of course only one concrete example of a more general
|
||||
-- problem.
|
||||
-- Short-term solution:
|
||||
-- - Keep displaying the chord until the user has fully entered it
|
||||
-- Fix:
|
||||
-- - Show the shortest possible chords
|
||||
|
||||
-- | Associates a user window, an overlay window created by this module and a rectangle
|
||||
-- circumscribing these windows
|
||||
data OverlayWindow =
|
||||
OverlayWindow { win :: !Window -- ^ The window managed by xmonad
|
||||
, attrs :: !WindowAttributes -- ^ Window attributes for @win@
|
||||
, overlay :: !Window -- ^ Our window used to display the overlay
|
||||
, rect :: !Rectangle -- ^ The rectangle of @overlay@
|
||||
}
|
||||
|
||||
-- | An overlay window and the chord used to select it
|
||||
data Overlay =
|
||||
Overlay { overlayWin :: !OverlayWindow -- ^ The window managed by xmonad
|
||||
, chord :: ![KeySym] -- ^ The chord we'll display in the overlay
|
||||
}
|
||||
|
||||
|
||||
-- | Maps keys to windows. 'AnyKeys' maps keys to windows regardless which screen they're on.
|
||||
-- 'PerScreenKeys' maps keys to screens to windows. See @Usage@ for more examples.
|
||||
data ChordKeys = AnyKeys ![KeySym]
|
||||
| PerScreenKeys !(M.Map ScreenId [KeySym])
|
||||
|
||||
-- | Configuration options for EasyMotion.
|
||||
--
|
||||
-- All colors are hex strings, e.g. "#000000"
|
||||
--
|
||||
-- If the number of windows for which chords are required exceeds 'maxChordLen', chords
|
||||
-- will simply not be generated for these windows. In this way, single-key selection may be
|
||||
-- preferred over the ability to select any window.
|
||||
--
|
||||
-- 'cancelKey', @xK_BackSpace@ and any duplicates will be removed from 'sKeys' if included.
|
||||
-- See @Usage@ for examples of 'sKeys'.
|
||||
data EasyMotionConfig =
|
||||
EMConf { txtCol :: !String -- ^ Color of the text displayed
|
||||
, bgCol :: !String -- ^ Color of the window overlaid
|
||||
, overlayF :: !(Position -> Rectangle -> Rectangle) -- ^ Function to generate overlay rectangle
|
||||
, borderCol :: !String -- ^ Color of the overlay window borders
|
||||
, sKeys :: !ChordKeys -- ^ Keys to use for window selection
|
||||
, cancelKey :: !KeySym -- ^ Key to use to cancel selection
|
||||
, emFont :: !String -- ^ Font for selection characters (passed to 'initXMF')
|
||||
, borderPx :: !Int -- ^ Width of border in pixels
|
||||
, maxChordLen :: !Int -- ^ Maximum chord length. Use 0 for no maximum.
|
||||
}
|
||||
|
||||
instance Default EasyMotionConfig where
|
||||
def =
|
||||
EMConf { txtCol = "#ffffff"
|
||||
, bgCol = "#000000"
|
||||
, overlayF = proportional (0.3::Double)
|
||||
, borderCol = "#ffffff"
|
||||
, sKeys = AnyKeys [xK_s, xK_d, xK_f, xK_j, xK_k, xK_l]
|
||||
, cancelKey = xK_q
|
||||
, borderPx = 1
|
||||
, maxChordLen = 0
|
||||
#ifdef XFT
|
||||
, emFont = "xft:Sans-100"
|
||||
#else
|
||||
, emFont = "-misc-fixed-*-*-*-*-200-*-*-*-*-*-*-*"
|
||||
#endif
|
||||
}
|
||||
|
||||
-- | Create overlay windows of the same size as the window they select
|
||||
fullSize :: Position -> Rectangle -> Rectangle
|
||||
fullSize _ = id
|
||||
|
||||
-- | Create overlay windows a proportion of the size of the window they select
|
||||
proportional :: RealFrac f => f -> Position -> Rectangle -> Rectangle
|
||||
proportional f th r = Rectangle { rect_width = newW
|
||||
, rect_height = newH
|
||||
, rect_x = rect_x r + fi (rect_width r - newW) `div` 2
|
||||
, rect_y = rect_y r + fi (rect_height r - newH) `div` 2 }
|
||||
where
|
||||
newH = max (fi th) (round $ f * fi (rect_height r))
|
||||
newW = newH
|
||||
|
||||
-- | Create fixed-size overlay windows
|
||||
fixedSize :: (Integral a, Integral b) => a -> b -> Position -> Rectangle -> Rectangle
|
||||
fixedSize w h th r = Rectangle { rect_width = rw
|
||||
, rect_height = rh
|
||||
, rect_x = rect_x r + fi (rect_width r - rw) `div` 2
|
||||
, rect_y = rect_y r + fi (rect_height r - rh) `div` 2 }
|
||||
where
|
||||
rw = max (fi w) (fi th)
|
||||
rh = max (fi h) (fi th)
|
||||
|
||||
-- | Create overlay windows the minimum size to contain their key chord
|
||||
textSize :: Position -> Rectangle -> Rectangle
|
||||
textSize th r = Rectangle { rect_width = fi th
|
||||
, rect_height = fi th
|
||||
, rect_x = rect_x r + (fi (rect_width r) - fi th) `div` 2
|
||||
, rect_y = rect_y r + (fi (rect_height r) - fi th) `div` 2 }
|
||||
|
||||
-- | Create overlay windows the full width of the window they select, the minimum height to contain
|
||||
-- their chord, and a proportion of the distance from the top of the window they select
|
||||
bar :: RealFrac f => f -> Position -> Rectangle -> Rectangle
|
||||
bar f th r = Rectangle { rect_width = rect_width r
|
||||
, rect_height = fi th
|
||||
, rect_x = rect_x r
|
||||
, rect_y = rect_y r + round (f' * (fi (rect_height r) - fi th)) }
|
||||
where
|
||||
-- clamp f in [0,1] as other values will appear to lock up xmonad as the overlay will be
|
||||
-- displayed off-screen
|
||||
f' = min 0.0 $ max f 1.0
|
||||
|
||||
-- | Handles overlay display and window selection. Called after config has been sanitised.
|
||||
handleSelectWindow :: EasyMotionConfig -> X (Maybe Window)
|
||||
handleSelectWindow EMConf { sKeys = AnyKeys [] } = return Nothing
|
||||
handleSelectWindow c = do
|
||||
f <- initXMF $ emFont c
|
||||
th <- (\(asc, dsc) -> asc + dsc + 2) <$> textExtentsXMF f (concatMap keysymToString (allKeys . sKeys $ c))
|
||||
XConf { theRoot = rw, display = dpy } <- ask
|
||||
XState { mapped = mappedWins, windowset = ws } <- get
|
||||
-- build overlays depending on key configuration
|
||||
overlays :: [Overlay] <- case sKeys c of
|
||||
AnyKeys ks -> buildOverlays ks <$> sortedOverlayWindows
|
||||
where
|
||||
visibleWindows :: [Window]
|
||||
visibleWindows = toList mappedWins
|
||||
sortedOverlayWindows :: X [OverlayWindow]
|
||||
sortedOverlayWindows = sortOverlayWindows <$> buildOverlayWindows dpy th visibleWindows
|
||||
PerScreenKeys m ->
|
||||
fmap concat
|
||||
$ sequence
|
||||
$ M.elems
|
||||
$ M.mapWithKey (\sid ks -> buildOverlays ks <$> sortedOverlayWindows sid) m
|
||||
where
|
||||
screenById :: ScreenId -> Maybe (W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail)
|
||||
screenById sid = find ((== sid) . W.screen) (W.screens ws)
|
||||
visibleWindowsOnScreen :: ScreenId -> [Window]
|
||||
visibleWindowsOnScreen sid = filter (`elem` toList mappedWins) $ W.integrate' $ screenById sid >>= W.stack . W.workspace
|
||||
sortedOverlayWindows :: ScreenId -> X [OverlayWindow]
|
||||
sortedOverlayWindows sid = sortOverlayWindows <$> buildOverlayWindows dpy th (visibleWindowsOnScreen sid)
|
||||
status <- io $ grabKeyboard dpy rw True grabModeAsync grabModeAsync currentTime
|
||||
if status == grabSuccess
|
||||
then do
|
||||
resultWin <- handleKeyboard dpy (displayOverlay f) (cancelKey c) overlays []
|
||||
io $ ungrabKeyboard dpy currentTime
|
||||
mapM_ (deleteWindow . overlay . overlayWin) overlays
|
||||
io $ sync dpy False
|
||||
releaseXMF f
|
||||
case resultWin of
|
||||
-- focus the selected window
|
||||
Selected o -> return . Just . win . overlayWin $ o
|
||||
-- return focus correctly
|
||||
_ -> whenJust (W.peek ws) (windows . W.focusWindow) $> Nothing
|
||||
else releaseXMF f $> Nothing
|
||||
where
|
||||
allKeys :: ChordKeys -> [KeySym]
|
||||
allKeys (AnyKeys ks) = ks
|
||||
allKeys (PerScreenKeys m) = concat $ M.elems m
|
||||
|
||||
buildOverlays :: [KeySym] -> [OverlayWindow] -> [Overlay]
|
||||
buildOverlays ks = appendChords (maxChordLen c) ks
|
||||
|
||||
buildOverlayWindows :: Display -> Position -> [Window] -> X [OverlayWindow]
|
||||
buildOverlayWindows dpy th ws = sequence $ buildOverlayWin dpy th <$> ws
|
||||
|
||||
sortOverlayWindows :: [OverlayWindow] -> [OverlayWindow]
|
||||
sortOverlayWindows = sortOn ((wa_x &&& wa_y) . attrs)
|
||||
|
||||
makeRect :: WindowAttributes -> Rectangle
|
||||
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 dpy th w = do
|
||||
wAttrs <- io $ getWindowAttributes dpy w
|
||||
let r = overlayF c th $ makeRect wAttrs
|
||||
o <- createNewWindow r Nothing "" True
|
||||
return OverlayWindow { rect=r, overlay=o, win=w, attrs=wAttrs }
|
||||
|
||||
-- | Display an overlay with the provided formatting
|
||||
displayOverlay :: XMonadFont -> Overlay -> X ()
|
||||
displayOverlay f Overlay { overlayWin = OverlayWindow { rect = r, overlay = o }, chord = ch } = do
|
||||
showWindow o
|
||||
paintAndWrite o f (fi (rect_width r)) (fi (rect_height r)) (fi (borderPx c)) (bgCol c) (borderCol c) (txtCol c) (bgCol c) [AlignCenter] [concatMap keysymToString ch]
|
||||
|
||||
-- | Display overlay windows and chords for window selection
|
||||
selectWindow :: EasyMotionConfig -> X (Maybe Window)
|
||||
selectWindow conf =
|
||||
handleSelectWindow conf { sKeys = sanitiseKeys (sKeys conf) }
|
||||
where
|
||||
-- make sure the key lists don't contain: backspace, our cancel key, or duplicates
|
||||
sanitise :: [KeySym] -> [KeySym]
|
||||
sanitise = nub . filter (`notElem` [xK_BackSpace, cancelKey conf])
|
||||
sanitiseKeys :: ChordKeys -> ChordKeys
|
||||
sanitiseKeys cKeys =
|
||||
case cKeys of
|
||||
AnyKeys ks -> AnyKeys . sanitise $ ks
|
||||
PerScreenKeys m -> PerScreenKeys $ M.map sanitise m
|
||||
|
||||
-- | Take a list of overlays lacking chords, return a list of overlays with key chords
|
||||
appendChords :: Int -> [KeySym] -> [OverlayWindow] -> [Overlay]
|
||||
appendChords _ [] _ = []
|
||||
appendChords maxUserSelectedLen ks overlayWins =
|
||||
zipWith Overlay overlayWins chords
|
||||
where
|
||||
chords = replicateM chordLen ks
|
||||
-- the minimum necessary chord length to assign a unique chord to each visible window
|
||||
minCoverLen = -((-(length overlayWins)) `div` length ks)
|
||||
-- if the user has specified a max chord length we use this even if it will not cover all
|
||||
-- windows, as they may prefer to focus windows with fewer keys over the ability to focus any
|
||||
-- window
|
||||
chordLen = if maxUserSelectedLen <= 0 then minCoverLen else min minCoverLen maxUserSelectedLen
|
||||
|
||||
-- | A three-state result for handling user-initiated selection cancellation, successful selection,
|
||||
-- or backspace.
|
||||
data HandleResult = Exit | Selected Overlay | Backspace
|
||||
|
||||
-- | Handle key press events for window selection.
|
||||
handleKeyboard :: Display -> (Overlay -> X()) -> KeySym -> [Overlay] -> [Overlay] -> X HandleResult
|
||||
handleKeyboard _ _ _ [] _ = return Exit
|
||||
handleKeyboard dpy drawFn cancel selected deselected = do
|
||||
redraw
|
||||
ev <- io $ allocaXEvent $ \e -> do
|
||||
maskEvent dpy (keyPressMask .|. keyReleaseMask .|. buttonPressMask) e
|
||||
getEvent e
|
||||
if | ev_event_type ev == keyPress -> do
|
||||
s <- io $ keycodeToKeysym dpy (ev_keycode ev) 0
|
||||
if | s == cancel -> return Exit
|
||||
| s == xK_BackSpace -> return Backspace
|
||||
| isNextOverlayKey s -> handleNextOverlayKey s
|
||||
| otherwise -> handleKeyboard dpy drawFn cancel selected deselected
|
||||
| ev_event_type ev == buttonPress -> do
|
||||
-- See XMonad.Prompt Note [Allow ButtonEvents]
|
||||
io $ allowEvents dpy replayPointer currentTime
|
||||
handleKeyboard dpy drawFn cancel selected deselected
|
||||
| otherwise -> handleKeyboard dpy drawFn cancel selected deselected
|
||||
where
|
||||
redraw = mapM (mapM_ drawFn) [selected, deselected]
|
||||
retryBackspace x =
|
||||
case x of
|
||||
Backspace -> redraw >> handleKeyboard dpy drawFn cancel selected deselected
|
||||
_ -> return x
|
||||
isNextOverlayKey keySym = isJust (find ((== Just keySym) . listToMaybe .chord) selected)
|
||||
handleNextOverlayKey keySym =
|
||||
case fg of
|
||||
[x] -> return $ Selected x
|
||||
_ -> handleKeyboard dpy drawFn cancel (trim fg) (clear bg) >>= retryBackspace
|
||||
where
|
||||
(fg, bg) = partition ((== Just keySym) . listToMaybe . chord) selected
|
||||
trim = map (\o -> o { chord = tail $ chord o })
|
||||
clear = map (\o -> o { chord = [] })
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.FindEmptyWorkspace
|
||||
-- Description : Find an empty workspace.
|
||||
-- Copyright : (c) Miikka Koskinen 2007
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -18,9 +19,7 @@ module XMonad.Actions.FindEmptyWorkspace (
|
||||
viewEmptyWorkspace, tagToEmptyWorkspace, sendToEmptyWorkspace
|
||||
) where
|
||||
|
||||
import Data.List
|
||||
import Data.Maybe ( isNothing )
|
||||
|
||||
import XMonad.Prelude
|
||||
import XMonad
|
||||
import XMonad.StackSet
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.FlexibleManipulate
|
||||
-- Description : Move and resize floating windows without warping the mouse.
|
||||
-- Copyright : (c) Michael Sloan
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -23,8 +24,9 @@ module XMonad.Actions.FlexibleManipulate (
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude ((<&>))
|
||||
import qualified Prelude as P
|
||||
import Prelude (($), (.), fst, snd, uncurry, const, id, Ord(..), Monad(..), fromIntegral, Double, Integer, map, round, otherwise)
|
||||
import Prelude (Double, Integer, Ord (..), const, fromIntegral, fst, id, map, otherwise, round, snd, uncurry, ($), (.))
|
||||
|
||||
-- $usage
|
||||
-- First, add this import to your @~\/.xmonad\/xmonad.hs@:
|
||||
@@ -79,24 +81,23 @@ position = const 0.5
|
||||
-- manipulation action.
|
||||
mouseWindow :: (Double -> Double) -> Window -> X ()
|
||||
mouseWindow f w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
[wpos, wsize] <- io $ getWindowAttributes d w >>= return . winAttrs
|
||||
[wpos, wsize] <- io $ getWindowAttributes d w <&> winAttrs
|
||||
sh <- io $ getWMNormalHints d w
|
||||
pointer <- io $ queryPointer d w >>= return . pointerPos
|
||||
pointer <- io $ queryPointer d w <&> pointerPos
|
||||
|
||||
let uv = (pointer - wpos) / wsize
|
||||
fc = mapP f uv
|
||||
mul = mapP (\x -> 2 P.- 2 P.* P.abs(x P.- 0.5)) fc --Fudge factors: interpolation between 1 when on edge, 2 in middle
|
||||
atl = ((1, 1) - fc) * mul
|
||||
abr = fc * mul
|
||||
mouseDrag (\ex ey -> io $ do
|
||||
mouseDrag (\ex ey -> do
|
||||
let offset = (fromIntegral ex, fromIntegral ey) - pointer
|
||||
npos = wpos + offset * atl
|
||||
nbr = (wpos + wsize) + offset * abr
|
||||
ntl = minP (nbr - (32, 32)) npos --minimum size
|
||||
nwidth = applySizeHintsContents sh $ mapP (round :: Double -> Integer) (nbr - ntl)
|
||||
moveResizeWindow d w (round $ fst ntl) (round $ snd ntl) `uncurry` nwidth
|
||||
return ())
|
||||
io $ moveResizeWindow d w (round $ fst ntl) (round $ snd ntl) `uncurry` nwidth
|
||||
float w)
|
||||
(float w)
|
||||
|
||||
float w
|
||||
@@ -113,7 +114,7 @@ type Pnt = (Double, Double)
|
||||
pairUp :: [a] -> [(a,a)]
|
||||
pairUp [] = []
|
||||
pairUp [_] = []
|
||||
pairUp (x:y:xs) = (x, y) : (pairUp xs)
|
||||
pairUp (x:y:xs) = (x, y) : pairUp xs
|
||||
|
||||
mapP :: (a -> b) -> (a, a) -> (b, b)
|
||||
mapP f (x, y) = (f x, f y)
|
||||
@@ -132,4 +133,3 @@ infixl 7 *, /
|
||||
(*) = zipP (P.*)
|
||||
(/) :: (P.Fractional a) => (a,a) -> (a,a) -> (a,a)
|
||||
(/) = zipP (P./)
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.FlexibleResize
|
||||
-- Description : Resize floating windows from any corner.
|
||||
-- Copyright : (c) Lukas Mai
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -20,7 +21,7 @@ module XMonad.Actions.FlexibleResize (
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Util.XUtils (fi)
|
||||
import XMonad.Prelude (fi)
|
||||
import Foreign.C.Types
|
||||
|
||||
-- $usage
|
||||
@@ -50,7 +51,6 @@ mouseResizeEdgeWindow
|
||||
-> Window -- ^ The window to resize.
|
||||
-> X ()
|
||||
mouseResizeEdgeWindow edge w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
sh <- io $ getWMNormalHints d w
|
||||
(_, _, _, _, _, ix, iy, _) <- io $ queryPointer d w
|
||||
@@ -62,16 +62,17 @@ mouseResizeEdgeWindow edge w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
(cy, fy, gy) = mkSel north height pos_y
|
||||
io $ warpPointer d none w 0 0 0 0 cx cy
|
||||
mouseDrag (\ex ey -> do let (nw,nh) = applySizeHintsContents sh (gx ex, gy ey)
|
||||
io $ moveResizeWindow d w (fx nw) (fy nh) nw nh)
|
||||
io $ moveResizeWindow d w (fx nw) (fy nh) nw nh
|
||||
float w)
|
||||
(float w)
|
||||
where
|
||||
findPos :: CInt -> Position -> Maybe Bool
|
||||
findPos m s = if p < 0.5 - edge/2
|
||||
then Just True
|
||||
else if p < 0.5 + edge/2
|
||||
then Nothing
|
||||
else Just False
|
||||
where p = fi m / fi s
|
||||
findPos m s
|
||||
| p < 0.5 - edge/2 = Just True
|
||||
| p < 0.5 + edge/2 = Nothing
|
||||
| otherwise = Just False
|
||||
where
|
||||
p = fi m / fi s
|
||||
mkSel :: Maybe Bool -> Position -> Position -> (Position, Dimension -> Position, Position -> Dimension)
|
||||
mkSel b k p = case b of
|
||||
Just True -> (0, (fi k + fi p -).fi, (fi k + fi p -).fi)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.FloatKeys
|
||||
-- Description : Move and resize floating windows.
|
||||
-- Copyright : (c) Karsten Schoelzel <kuser@gmx.de>
|
||||
-- License : BSD
|
||||
--
|
||||
@@ -22,6 +23,7 @@ module XMonad.Actions.FloatKeys (
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude (fi)
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
@@ -43,10 +45,9 @@ import XMonad
|
||||
-- right and @dy@ pixels down.
|
||||
keysMoveWindow :: D -> Window -> X ()
|
||||
keysMoveWindow (dx,dy) w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
io $ moveWindow d w (fromIntegral (fromIntegral (wa_x wa) + dx))
|
||||
(fromIntegral (fromIntegral (wa_y wa) + dy))
|
||||
io $ moveWindow d w (fi (fi (wa_x wa) + dx))
|
||||
(fi (fi (wa_y wa) + dy))
|
||||
float w
|
||||
|
||||
-- | @keysMoveWindowTo (x, y) (gx, gy)@ moves the window relative
|
||||
@@ -61,14 +62,14 @@ keysMoveWindow (dx,dy) w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
-- > keysMoveWindowTo (1024,0) (1, 0) -- put window in the top right corner
|
||||
keysMoveWindowTo :: P -> G -> Window -> X ()
|
||||
keysMoveWindowTo (x,y) (gx, gy) w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
io $ moveWindow d w (x - round (gx * fromIntegral (wa_width wa)))
|
||||
(y - round (gy * fromIntegral (wa_height wa)))
|
||||
io $ moveWindow d w (x - round (gx * fi (wa_width wa)))
|
||||
(y - round (gy * fi (wa_height wa)))
|
||||
float w
|
||||
|
||||
type G = (Rational, Rational)
|
||||
type P = (Position, Position)
|
||||
type ChangeDim = (Int, Int)
|
||||
|
||||
-- | @keysResizeWindow (dx, dy) (gx, gy)@ changes the width by @dx@
|
||||
-- and the height by @dy@, leaving the window-relative point @(gx,
|
||||
@@ -80,7 +81,7 @@ type P = (Position, Position)
|
||||
-- > keysResizeWindow (10, 0) (0, 1%2) -- does the same, unless sizeHints are applied
|
||||
-- > keysResizeWindow (10, 10) (1%2, 1%2) -- add 5 pixels on each side
|
||||
-- > keysResizeWindow (-10, -10) (0, 1) -- shrink the window in direction of the bottom-left corner
|
||||
keysResizeWindow :: D -> G -> Window -> X ()
|
||||
keysResizeWindow :: ChangeDim -> G -> Window -> X ()
|
||||
keysResizeWindow = keysMoveResize keysResizeWindow'
|
||||
|
||||
-- | @keysAbsResizeWindow (dx, dy) (ax, ay)@ changes the width by @dx@
|
||||
@@ -90,34 +91,34 @@ keysResizeWindow = keysMoveResize keysResizeWindow'
|
||||
-- For example:
|
||||
--
|
||||
-- > keysAbsResizeWindow (10, 10) (0, 0) -- enlarge the window; if it is not in the top-left corner it will also be moved down and to the right.
|
||||
keysAbsResizeWindow :: D -> D -> Window -> X ()
|
||||
keysAbsResizeWindow :: ChangeDim -> D -> Window -> X ()
|
||||
keysAbsResizeWindow = keysMoveResize keysAbsResizeWindow'
|
||||
|
||||
keysAbsResizeWindow' :: SizeHints -> P -> D -> D -> D -> (P,D)
|
||||
keysAbsResizeWindow' :: SizeHints -> P -> D -> ChangeDim -> D -> (P,D)
|
||||
keysAbsResizeWindow' sh (x,y) (w,h) (dx,dy) (ax, ay) = ((round nx, round ny), (nw, nh))
|
||||
where
|
||||
(nw, nh) = applySizeHintsContents sh (w + dx, h + dy)
|
||||
-- The width and height of a window are positive and thus
|
||||
-- converting to 'Dimension' should be safe.
|
||||
(nw, nh) = applySizeHintsContents sh (fi w + dx, fi h + dy)
|
||||
nx :: Rational
|
||||
nx = fromIntegral (ax * w + nw * (fromIntegral x - ax)) / fromIntegral w
|
||||
nx = fi (ax * w + nw * (fi x - ax)) / fi w
|
||||
ny :: Rational
|
||||
ny = fromIntegral (ay * h + nh * (fromIntegral y - ay)) / fromIntegral h
|
||||
ny = fi (ay * h + nh * (fi y - ay)) / fi h
|
||||
|
||||
keysResizeWindow' :: SizeHints -> P -> D -> D -> G -> (P,D)
|
||||
keysResizeWindow' :: SizeHints -> P -> D -> ChangeDim -> G -> (P,D)
|
||||
keysResizeWindow' sh (x,y) (w,h) (dx,dy) (gx, gy) = ((nx, ny), (nw, nh))
|
||||
where
|
||||
(nw, nh) = applySizeHintsContents sh (w + dx, h + dy)
|
||||
nx = round $ fromIntegral x + gx * fromIntegral w - gx * fromIntegral nw
|
||||
ny = round $ fromIntegral y + gy * fromIntegral h - gy * fromIntegral nh
|
||||
(nw, nh) = applySizeHintsContents sh (fi w + dx, fi h + dy)
|
||||
nx = round $ fi x + gx * fi w - gx * fi nw
|
||||
ny = round $ fi y + gy * fi h - gy * fi nh
|
||||
|
||||
keysMoveResize :: (SizeHints -> P -> D -> a -> b -> (P,D)) -> a -> b -> Window -> X ()
|
||||
keysMoveResize f move resize w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
sh <- io $ getWMNormalHints d w
|
||||
let wa_dim = (fromIntegral $ wa_width wa, fromIntegral $ wa_height wa)
|
||||
wa_pos = (fromIntegral $ wa_x wa, fromIntegral $ wa_y wa)
|
||||
let wa_dim = (fi $ wa_width wa, fi $ wa_height wa)
|
||||
wa_pos = (fi $ wa_x wa, fi $ wa_y wa)
|
||||
(wn_pos, wn_dim) = f sh wa_pos wa_dim move resize
|
||||
io $ resizeWindow d w `uncurry` wn_dim
|
||||
io $ moveWindow d w `uncurry` wn_pos
|
||||
float w
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.FloatSnap
|
||||
-- Description : Snap to other windows or the edge of the screen while moving or resizing.
|
||||
-- Copyright : (c) 2009 Anders Engstrom <ankaan@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -27,9 +28,7 @@ module XMonad.Actions.FloatSnap (
|
||||
ifClick') where
|
||||
|
||||
import XMonad
|
||||
import Control.Applicative((<$>))
|
||||
import Data.List (sort)
|
||||
import Data.Maybe (listToMaybe,fromJust,isNothing)
|
||||
import XMonad.Prelude (fromJust, isNothing, listToMaybe, sort, when)
|
||||
import qualified XMonad.StackSet as W
|
||||
import qualified Data.Set as S
|
||||
|
||||
@@ -96,14 +95,14 @@ snapMagicMouseResize
|
||||
snapMagicMouseResize middle collidedist snapdist w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
wa <- io $ getWindowAttributes d w
|
||||
(_, _, _, px, py, _, _, _) <- io $ queryPointer d w
|
||||
let x = (fromIntegral px - wx wa)/(ww wa)
|
||||
y = (fromIntegral py - wy wa)/(wh wa)
|
||||
ml = if x <= (0.5 - middle/2) then [L] else []
|
||||
mr = if x > (0.5 + middle/2) then [R] else []
|
||||
mu = if y <= (0.5 - middle/2) then [U] else []
|
||||
md = if y > (0.5 + middle/2) then [D] else []
|
||||
let x = (fromIntegral px - wx wa)/ww wa
|
||||
y = (fromIntegral py - wy wa)/wh wa
|
||||
ml = [L | x <= (0.5 - middle/2)]
|
||||
mr = [R | x > (0.5 + middle/2)]
|
||||
mu = [U | y <= (0.5 - middle/2)]
|
||||
md = [D | y > (0.5 + middle/2)]
|
||||
mdir = ml++mr++mu++md
|
||||
dir = if mdir == []
|
||||
dir = if null mdir
|
||||
then [L,R,U,D]
|
||||
else mdir
|
||||
snapMagicResize dir collidedist snapdist w
|
||||
@@ -121,18 +120,17 @@ snapMagicResize
|
||||
-> Window -- ^ The window to move and resize.
|
||||
-> X ()
|
||||
snapMagicResize dir collidedist snapdist w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
|
||||
(xbegin,xend) <- handleAxis True d wa
|
||||
(ybegin,yend) <- handleAxis False d wa
|
||||
|
||||
let xbegin' = if L `elem` dir then xbegin else (wx wa)
|
||||
xend' = if R `elem` dir then xend else (wx wa + ww wa)
|
||||
ybegin' = if U `elem` dir then ybegin else (wy wa)
|
||||
yend' = if D `elem` dir then yend else (wy wa + wh wa)
|
||||
let xbegin' = if L `elem` dir then xbegin else wx wa
|
||||
xend' = if R `elem` dir then xend else wx wa + ww wa
|
||||
ybegin' = if U `elem` dir then ybegin else wy wa
|
||||
yend' = if D `elem` dir then yend else wy wa + wh wa
|
||||
|
||||
io $ moveWindow d w (fromIntegral $ xbegin') (fromIntegral $ ybegin')
|
||||
io $ moveWindow d w (fromIntegral xbegin') (fromIntegral ybegin')
|
||||
io $ resizeWindow d w (fromIntegral $ xend' - xbegin') (fromIntegral $ yend' - ybegin')
|
||||
float w
|
||||
where
|
||||
@@ -152,13 +150,13 @@ snapMagicResize dir collidedist snapdist w = whenX (isClient w) $ withDisplay $
|
||||
(Nothing,Nothing) -> wpos wa
|
||||
end = if fs
|
||||
then wpos wa + wdim wa
|
||||
else case (if mfl==(Just begin) then Nothing else mfl,mfr) of
|
||||
else case (if mfl==Just begin then Nothing else mfl,mfr) of
|
||||
(Just fl,Just fr) -> if wpos wa + wdim wa - fl < fr - wpos wa - wdim wa then fl else fr
|
||||
(Just fl,Nothing) -> fl
|
||||
(Nothing,Just fr) -> fr
|
||||
(Nothing,Nothing) -> wpos wa + wdim wa
|
||||
begin' = if isNothing snapdist || abs (begin - wpos wa) <= fromJust snapdist then begin else (wpos wa)
|
||||
end' = if isNothing snapdist || abs (end - wpos wa - wdim wa) <= fromJust snapdist then end else (wpos wa + wdim wa)
|
||||
begin' = if isNothing snapdist || abs (begin - wpos wa) <= fromJust snapdist then begin else wpos wa
|
||||
end' = if isNothing snapdist || abs (end - wpos wa - wdim wa) <= fromJust snapdist then end else wpos wa + wdim wa
|
||||
return (begin',end')
|
||||
where
|
||||
(wpos, wdim, _, _) = constructors horiz
|
||||
@@ -171,7 +169,6 @@ snapMagicMove
|
||||
-> Window -- ^ The window to move.
|
||||
-> X ()
|
||||
snapMagicMove collidedist snapdist w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
|
||||
nx <- handleAxis True d wa
|
||||
@@ -194,8 +191,8 @@ snapMagicMove collidedist snapdist w = whenX (isClient w) $ withDisplay $ \d ->
|
||||
(Just fl,Nothing) -> fl
|
||||
(Nothing,Just fr) -> fr
|
||||
(Nothing,Nothing) -> wpos wa
|
||||
newpos = if abs (b - wpos wa) <= abs (f - wpos wa - wdim wa) then b else (f - wdim wa)
|
||||
in if isNothing snapdist || abs (newpos - wpos wa) <= fromJust snapdist then newpos else (wpos wa)
|
||||
newpos = if abs (b - wpos wa) <= abs (f - wpos wa - wdim wa) then b else f - wdim wa
|
||||
in if isNothing snapdist || abs (newpos - wpos wa) <= fromJust snapdist then newpos else wpos wa
|
||||
where
|
||||
(wpos, wdim, _, _) = constructors horiz
|
||||
|
||||
@@ -212,7 +209,6 @@ snapMove D = doSnapMove False False
|
||||
|
||||
doSnapMove :: Bool -> Bool -> Maybe Int -> Window -> X ()
|
||||
doSnapMove horiz rev collidedist w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
((bl,br,_),(fl,fr,_)) <- getSnap horiz collidedist d w
|
||||
|
||||
@@ -252,7 +248,6 @@ snapShrink = snapResize False
|
||||
|
||||
snapResize :: Bool -> Direction2D -> Maybe Int -> Window -> X ()
|
||||
snapResize grow dir collidedist w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
io $ raiseWindow d w
|
||||
wa <- io $ getWindowAttributes d w
|
||||
mr <- case dir of
|
||||
L -> do ((mg,ms,_),(_,_,_)) <- getSnap True collidedist d w
|
||||
@@ -274,9 +269,8 @@ snapResize grow dir collidedist w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
|
||||
case mr of
|
||||
Nothing -> return ()
|
||||
Just (nx,ny,nw,nh) -> if nw>0 && nh>0 then do io $ moveWindow d w (fromIntegral nx) (fromIntegral ny)
|
||||
io $ resizeWindow d w (fromIntegral nw) (fromIntegral nh)
|
||||
else return ()
|
||||
Just (nx,ny,nw,nh) -> when (nw>0 && nh>0) $ do io $ moveWindow d w (fromIntegral nx) (fromIntegral ny)
|
||||
io $ resizeWindow d w (fromIntegral nw) (fromIntegral nh)
|
||||
float w
|
||||
where
|
||||
wx = fromIntegral.wa_x
|
||||
@@ -291,8 +285,8 @@ getSnap horiz collidedist d w = do
|
||||
screen <- W.current <$> gets windowset
|
||||
let sr = screenRect $ W.screenDetail screen
|
||||
wl = W.integrate' . W.stack $ W.workspace screen
|
||||
gr <- fmap ($sr) $ calcGap $ S.fromList [minBound .. maxBound]
|
||||
wla <- filter (collides wa) `fmap` (io $ mapM (getWindowAttributes d) $ filter (/=w) wl)
|
||||
gr <- ($sr) <$> calcGap (S.fromList [minBound .. maxBound])
|
||||
wla <- filter (collides wa) <$> io (mapM (getWindowAttributes d) $ filter (/=w) wl)
|
||||
|
||||
return ( neighbours (back wa sr gr wla) (wpos wa)
|
||||
, neighbours (front wa sr gr wla) (wpos wa + wdim wa)
|
||||
@@ -306,8 +300,8 @@ getSnap horiz collidedist d w = do
|
||||
|
||||
back wa sr gr wla = dropWhile (< rpos sr) $
|
||||
takeWhile (< rpos sr + rdim sr) $
|
||||
sort $ (rpos sr):(rpos gr):(rpos gr + rdim gr):
|
||||
foldr (\a as -> (wpos a):(wpos a + wdim a + wborder a + wborder wa):as) [] wla
|
||||
sort $ rpos sr:rpos gr:(rpos gr + rdim gr):
|
||||
foldr (\a as -> wpos a:(wpos a + wdim a + wborder a + wborder wa):as) [] wla
|
||||
|
||||
front wa sr gr wla = dropWhile (<= rpos sr) $
|
||||
takeWhile (<= rpos sr + rdim sr) $
|
||||
@@ -321,8 +315,8 @@ getSnap horiz collidedist d w = do
|
||||
|
||||
collides wa oa = case collidedist of
|
||||
Nothing -> True
|
||||
Just dist -> ( refwpos oa - wborder oa < refwpos wa + refwdim wa + wborder wa + dist
|
||||
&& refwpos wa - wborder wa - dist < refwpos oa + refwdim oa + wborder oa )
|
||||
Just dist -> refwpos oa - wborder oa < refwpos wa + refwdim wa + wborder wa + dist
|
||||
&& refwpos wa - wborder wa - dist < refwpos oa + refwdim oa + wborder oa
|
||||
|
||||
|
||||
constructors :: Bool -> (WindowAttributes -> Int, WindowAttributes -> Int, Rectangle -> Int, Rectangle -> Int)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.FocusNth
|
||||
-- Description : Focus the nth window of the current workspace.
|
||||
-- Copyright : (c) Karsten Schoelzel <kuser@gmx.de>
|
||||
-- License : BSD
|
||||
--
|
||||
@@ -39,7 +40,7 @@ focusNth :: Int -> X ()
|
||||
focusNth = windows . modify' . focusNth'
|
||||
|
||||
focusNth' :: Int -> Stack a -> Stack a
|
||||
focusNth' n s@(Stack _ ls rs) | (n < 0) || (n > length(ls) + length(rs)) = s
|
||||
focusNth' n s@(Stack _ ls rs) | (n < 0) || (n > length ls + length rs) = s
|
||||
| otherwise = listToStack n (integrate s)
|
||||
|
||||
-- | Swap current window with nth. Focus stays in the same position
|
||||
@@ -51,12 +52,9 @@ swapNth' n s@(Stack c l r)
|
||||
| (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
|
||||
| otherwise = let (nl, 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)
|
||||
|
||||
|
||||
|
@@ -1,7 +1,8 @@
|
||||
{-# LANGUAGE ScopedTypeVariables, GeneralizedNewtypeDeriving, TypeSynonymInstances, FlexibleInstances, OverlappingInstances #-}
|
||||
{-# LANGUAGE ScopedTypeVariables, GeneralizedNewtypeDeriving, FlexibleInstances, TupleSections #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.GridSelect
|
||||
-- Description : Display items in a 2D grid and select from it with the keyboard or the mouse.
|
||||
-- Copyright : Clemens Fruhwirth <clemens@endorphin.org>
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
@@ -28,7 +29,6 @@ module XMonad.Actions.GridSelect (
|
||||
-- * Configuration
|
||||
GSConfig(..),
|
||||
def,
|
||||
defaultGSConfig,
|
||||
TwoDPosition,
|
||||
buildDefaultGSConfig,
|
||||
|
||||
@@ -48,6 +48,7 @@ module XMonad.Actions.GridSelect (
|
||||
fromClassName,
|
||||
stringColorizer,
|
||||
colorRangeFromClassName,
|
||||
stringToRatio,
|
||||
|
||||
-- * Navigation Mode assembly
|
||||
TwoD,
|
||||
@@ -79,16 +80,14 @@ module XMonad.Actions.GridSelect (
|
||||
-- * Types
|
||||
TwoDState,
|
||||
) where
|
||||
import Data.Maybe
|
||||
import Control.Arrow ((***))
|
||||
import Data.Bits
|
||||
import Data.Char
|
||||
import Data.Ord (comparing)
|
||||
import Control.Applicative
|
||||
import Control.Monad.State
|
||||
import Control.Arrow
|
||||
import Data.List as L
|
||||
import qualified Data.Map as M
|
||||
import XMonad hiding (liftX)
|
||||
import XMonad.Prelude
|
||||
import XMonad.Util.Font
|
||||
import XMonad.Prompt (mkUnmanagedWindow)
|
||||
import XMonad.StackSet as W
|
||||
@@ -96,7 +95,7 @@ import XMonad.Layout.Decoration
|
||||
import XMonad.Util.NamedWindows
|
||||
import XMonad.Actions.WindowBringer (bringWindow)
|
||||
import Text.Printf
|
||||
import System.Random (mkStdGen, genRange, next)
|
||||
import System.Random (mkStdGen, randomR)
|
||||
import Data.Word (Word8)
|
||||
|
||||
-- $usage
|
||||
@@ -107,13 +106,13 @@ import Data.Word (Word8)
|
||||
--
|
||||
-- Then add a keybinding, e.g.
|
||||
--
|
||||
-- > , ((modm, xK_g), goToSelected defaultGSConfig)
|
||||
-- > , ((modm, xK_g), goToSelected def)
|
||||
--
|
||||
-- This module also supports displaying arbitrary information in a grid and letting
|
||||
-- the user select from it. E.g. to spawn an application from a given list, you
|
||||
-- can use the following:
|
||||
--
|
||||
-- > , ((modm, xK_s), spawnSelected defaultGSConfig ["xterm","gmplayer","gvim"])
|
||||
-- > , ((modm, xK_s), spawnSelected def ["xterm","gmplayer","gvim"])
|
||||
|
||||
-- $commonGSConfig
|
||||
--
|
||||
@@ -123,7 +122,7 @@ import Data.Word (Word8)
|
||||
-- > {-# LANGUAGE NoMonomorphismRestriction #-}
|
||||
-- > import XMonad
|
||||
-- > ...
|
||||
-- > gsconfig1 = defaultGSConfig { gs_cellheight = 30, gs_cellwidth = 100 }
|
||||
-- > gsconfig1 = def { gs_cellheight = 30, gs_cellwidth = 100 }
|
||||
--
|
||||
-- An example where 'buildDefaultGSConfig' is used instead of 'defaultGSConfig'
|
||||
-- in order to specify a custom colorizer is @gsconfig2@ (found in
|
||||
@@ -222,18 +221,14 @@ instance HasColorizer Window where
|
||||
instance HasColorizer String where
|
||||
defaultColorizer = stringColorizer
|
||||
|
||||
instance HasColorizer a where
|
||||
instance {-# OVERLAPPABLE #-} HasColorizer a where
|
||||
defaultColorizer _ isFg =
|
||||
let getColor = if isFg then focusedBorderColor else normalBorderColor
|
||||
in asks $ flip (,) "black" . getColor . config
|
||||
in asks $ (, "black") . getColor . config
|
||||
|
||||
instance HasColorizer a => Default (GSConfig a) where
|
||||
def = buildDefaultGSConfig defaultColorizer
|
||||
|
||||
{-# DEPRECATED defaultGSConfig "Use def (from Data.Default, and re-exported from XMonad.Actions.GridSelect) instead." #-}
|
||||
defaultGSConfig :: HasColorizer a => GSConfig a
|
||||
defaultGSConfig = def
|
||||
|
||||
type TwoDPosition = (Integer, Integer)
|
||||
|
||||
type TwoDElementMap a = [(TwoDPosition,(String,a))]
|
||||
@@ -264,7 +259,7 @@ generateElementmap s = do
|
||||
-- Sorts the elementmap
|
||||
sortedElements = orderElementmap searchString filteredElements
|
||||
-- Case Insensitive version of isInfixOf
|
||||
needle `isInfixOfI` haystack = (upper needle) `isInfixOf` (upper haystack)
|
||||
needle `isInfixOfI` haystack = upper needle `isInfixOf` upper haystack
|
||||
upper = map toUpper
|
||||
|
||||
|
||||
@@ -308,8 +303,8 @@ diamondLayer n =
|
||||
-- tr = top right
|
||||
-- r = ur ++ 90 degree clock-wise rotation of ur
|
||||
let tr = [ (x,n-x) | x <- [0..n-1] ]
|
||||
r = tr ++ (map (\(x,y) -> (y,-x)) tr)
|
||||
in r ++ (map (negate *** negate) r)
|
||||
r = tr ++ map (\(x,y) -> (y,-x)) tr
|
||||
in r ++ map (negate *** negate) r
|
||||
|
||||
diamond :: (Enum a, Num a, Eq a) => [(a, a)]
|
||||
diamond = concatMap diamondLayer [0..]
|
||||
@@ -339,7 +334,7 @@ drawWinBox win font (fg,bg) bc ch cw text x y cp =
|
||||
drawRectangle dpy win bordergc (fromInteger x) (fromInteger y) (fromInteger cw) (fromInteger ch)
|
||||
stext <- shrinkWhile (shrinkIt shrinkText)
|
||||
(\n -> do size <- liftIO $ textWidthXMF dpy font n
|
||||
return $ size > (fromInteger (cw-(2*cp))))
|
||||
return $ size > fromInteger (cw-(2*cp)))
|
||||
text
|
||||
-- calculate the offset to vertically centre the text based on the ascender and descender
|
||||
(asc,desc) <- liftIO $ textExtentsXMF font stext
|
||||
@@ -392,9 +387,9 @@ updateElementsWithColorizer colorizer elementmap = do
|
||||
mapM_ updateElement elementmap
|
||||
|
||||
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
|
||||
s @ TwoDState { td_paneX = px, td_paneY = py,
|
||||
s@TwoDState { td_paneX = px, td_paneY = py,
|
||||
td_gsconfig = (GSConfig ch cw _ _ _ _ _ _ _ _) } <- get
|
||||
let gridX = (fi x - (px - cw) `div` 2) `div` cw
|
||||
gridY = (fi y - (py - ch) `div` 2) `div` ch
|
||||
@@ -403,7 +398,7 @@ stdHandle (ButtonEvent { ev_event_type = t, ev_x = x, ev_y = y }) contEventloop
|
||||
Nothing -> contEventloop
|
||||
| otherwise = contEventloop
|
||||
|
||||
stdHandle (ExposeEvent { }) contEventloop = updateAllElements >> contEventloop
|
||||
stdHandle ExposeEvent{} contEventloop = updateAllElements >> contEventloop
|
||||
|
||||
stdHandle _ contEventloop = contEventloop
|
||||
|
||||
@@ -435,7 +430,7 @@ shadowWithKeymap keymap dflt keyEvent@(ks,_,m') = fromMaybe (dflt keyEvent) (M.l
|
||||
select :: TwoD a (Maybe a)
|
||||
select = do
|
||||
s <- get
|
||||
return $ fmap (snd . snd) $ findInElementMap (td_curpos s) (td_elementmap s)
|
||||
return $ snd . snd <$> findInElementMap (td_curpos s) (td_elementmap s)
|
||||
|
||||
-- | Closes gridselect returning no element.
|
||||
cancel :: TwoD a (Maybe a)
|
||||
@@ -450,7 +445,7 @@ setPos newPos = do
|
||||
oldPos = td_curpos s
|
||||
when (isJust newSelectedEl && newPos /= oldPos) $ do
|
||||
put s { td_curpos = newPos }
|
||||
updateElements (catMaybes [(findInElementMap oldPos elmap), newSelectedEl])
|
||||
updateElements (catMaybes [findInElementMap oldPos elmap, newSelectedEl])
|
||||
|
||||
-- | Moves the cursor by the offsets specified
|
||||
move :: (Integer, Integer) -> TwoD a ()
|
||||
@@ -550,7 +545,7 @@ navNSearch = makeXEventhandler $ shadowWithKeymap navNSearchKeyMap navNSearchDef
|
||||
,((0,xK_Up) , move (0,-1) >> navNSearch)
|
||||
,((0,xK_Tab) , moveNext >> navNSearch)
|
||||
,((shiftMask,xK_Tab), movePrev >> navNSearch)
|
||||
,((0,xK_BackSpace), transformSearchString (\s -> if (s == "") then "" else init s) >> navNSearch)
|
||||
,((0,xK_BackSpace), transformSearchString (\s -> if s == "" then "" else init s) >> navNSearch)
|
||||
]
|
||||
-- The navigation handler ignores unknown key symbols, therefore we const
|
||||
navNSearchDefaultHandler (_,s,_) = do
|
||||
@@ -564,7 +559,7 @@ substringSearch returnNavigation = fix $ \me ->
|
||||
let searchKeyMap = M.fromList [
|
||||
((0,xK_Escape) , transformSearchString (const "") >> returnNavigation)
|
||||
,((0,xK_Return) , returnNavigation)
|
||||
,((0,xK_BackSpace), transformSearchString (\s -> if (s == "") then "" else init s) >> me)
|
||||
,((0,xK_BackSpace), transformSearchString (\s -> if s == "" then "" else init s) >> me)
|
||||
]
|
||||
searchDefaultHandler (_,s,_) = do
|
||||
transformSearchString (++ s)
|
||||
@@ -576,8 +571,8 @@ substringSearch returnNavigation = fix $ \me ->
|
||||
-- Conversion scheme as in http://en.wikipedia.org/wiki/HSV_color_space
|
||||
hsv2rgb :: Fractional a => (Integer,a,a) -> (a,a,a)
|
||||
hsv2rgb (h,s,v) =
|
||||
let hi = (div h 60) `mod` 6 :: Integer
|
||||
f = (((fromInteger h)/60) - (fromInteger hi)) :: Fractional a => a
|
||||
let hi = div h 60 `mod` 6 :: Integer
|
||||
f = ((fromInteger h/60) - fromInteger hi) :: Fractional a => a
|
||||
q = v * (1-f)
|
||||
p = v * (1-s)
|
||||
t = v * (1-(1-f)*s)
|
||||
@@ -594,19 +589,19 @@ hsv2rgb (h,s,v) =
|
||||
stringColorizer :: String -> Bool -> X (String, String)
|
||||
stringColorizer s active =
|
||||
let seed x = toInteger (sum $ map ((*x).fromEnum) s) :: Integer
|
||||
(r,g,b) = hsv2rgb ((seed 83) `mod` 360,
|
||||
(fromInteger ((seed 191) `mod` 1000))/2500+0.4,
|
||||
(fromInteger ((seed 121) `mod` 1000))/2500+0.4)
|
||||
(r,g,b) = hsv2rgb (seed 83 `mod` 360,
|
||||
fromInteger (seed 191 `mod` 1000)/2500+0.4,
|
||||
fromInteger (seed 121 `mod` 1000)/2500+0.4)
|
||||
in if active
|
||||
then return ("#faff69", "black")
|
||||
else return ("#" ++ concat (map (twodigitHex.(round :: Double -> Word8).(*256)) [r, g, b] ), "white")
|
||||
else return ("#" ++ concatMap (twodigitHex.(round :: Double -> Word8).(*256)) [r, g, b], "white")
|
||||
|
||||
-- | Colorize a window depending on it's className.
|
||||
fromClassName :: Window -> Bool -> X (String, String)
|
||||
fromClassName w active = runQuery className w >>= flip defaultColorizer active
|
||||
|
||||
twodigitHex :: Word8 -> String
|
||||
twodigitHex a = printf "%02x" a
|
||||
twodigitHex = printf "%02x"
|
||||
|
||||
-- | A colorizer that picks a color inside a range,
|
||||
-- and depending on the window's class.
|
||||
@@ -635,15 +630,12 @@ mix (r1, g1, b1) (r2, g2, b2) r = (mix' r1 r2, mix' g1 g2, mix' b1 b2)
|
||||
|
||||
-- | Generates a Double from a string, trying to
|
||||
-- achieve a random distribution.
|
||||
-- We create a random seed from the sum of all characters
|
||||
-- We create a random seed from the hash of all characters
|
||||
-- in the string, and use it to generate a ratio between 0 and 1
|
||||
stringToRatio :: String -> Double
|
||||
stringToRatio "" = 0
|
||||
stringToRatio s = let gen = mkStdGen $ sum $ map fromEnum s
|
||||
range = (\(a, b) -> b - a) $ genRange gen
|
||||
randomInt = foldr1 combine $ replicate 20 next
|
||||
combine f1 f2 g = let (_, g') = f1 g in f2 g'
|
||||
in fi (fst $ randomInt gen) / fi range
|
||||
stringToRatio s = let gen = mkStdGen $ foldl' (\t c -> t * 31 + fromEnum c) 0 s
|
||||
in fst $ randomR (0, 1) gen
|
||||
|
||||
-- | Brings up a 2D grid of elements in the center of the screen, and one can
|
||||
-- select an element with cursors keys. The selected element is returned.
|
||||
@@ -662,14 +654,14 @@ gridselect gsconfig elements =
|
||||
font <- initXMF (gs_font gsconfig)
|
||||
let screenWidth = toInteger $ rect_width scr
|
||||
screenHeight = toInteger $ rect_height scr
|
||||
selectedElement <- if (status == grabSuccess) then do
|
||||
selectedElement <- if status == grabSuccess then do
|
||||
let restriction ss cs = (fromInteger ss/fromInteger (cs gsconfig)-1)/2 :: Double
|
||||
restrictX = floor $ restriction screenWidth gs_cellwidth
|
||||
restrictY = floor $ restriction screenHeight gs_cellheight
|
||||
originPosX = floor $ ((gs_originFractX gsconfig) - (1/2)) * 2 * fromIntegral restrictX
|
||||
originPosY = floor $ ((gs_originFractY gsconfig) - (1/2)) * 2 * fromIntegral restrictY
|
||||
originPosX = floor $ (gs_originFractX gsconfig - (1/2)) * 2 * fromIntegral restrictX
|
||||
originPosY = floor $ (gs_originFractY gsconfig - (1/2)) * 2 * fromIntegral restrictY
|
||||
coords = diamondRestrict restrictX restrictY originPosX originPosY
|
||||
s = TwoDState { td_curpos = (head coords),
|
||||
s = TwoDState { td_curpos = head coords,
|
||||
td_availSlots = coords,
|
||||
td_elements = elements,
|
||||
td_gsconfig = gsconfig,
|
||||
@@ -680,7 +672,7 @@ gridselect gsconfig elements =
|
||||
td_searchString = "",
|
||||
td_elementmap = [] }
|
||||
m <- generateElementmap s
|
||||
evalTwoD (updateAllElements >> (gs_navigate gsconfig))
|
||||
evalTwoD (updateAllElements >> gs_navigate gsconfig)
|
||||
(s { td_elementmap = m })
|
||||
else
|
||||
return Nothing
|
||||
@@ -702,20 +694,17 @@ gridselectWindow gsconf = windowMap >>= gridselect gsconf
|
||||
withSelectedWindow :: (Window -> X ()) -> GSConfig Window -> X ()
|
||||
withSelectedWindow callback conf = do
|
||||
mbWindow <- gridselectWindow conf
|
||||
case mbWindow of
|
||||
Just w -> callback w
|
||||
Nothing -> return ()
|
||||
for_ mbWindow callback
|
||||
|
||||
windowMap :: X [(String,Window)]
|
||||
windowMap = do
|
||||
ws <- gets windowset
|
||||
wins <- mapM keyValuePair (W.allWindows ws)
|
||||
return wins
|
||||
where keyValuePair w = flip (,) w `fmap` decorateName' w
|
||||
mapM keyValuePair (W.allWindows ws)
|
||||
where keyValuePair w = (, w) <$> decorateName' w
|
||||
|
||||
decorateName' :: Window -> X String
|
||||
decorateName' w = do
|
||||
fmap show $ getName w
|
||||
show <$> getName w
|
||||
|
||||
-- | Builds a default gs config from a colorizer function.
|
||||
buildDefaultGSConfig :: (a -> Bool -> X (String,String)) -> GSConfig a
|
||||
@@ -770,7 +759,7 @@ gridselectWorkspace' conf func = withWindowSet $ \ws -> do
|
||||
--
|
||||
-- > import XMonad.Actions.DynamicWorkspaces (addWorkspace)
|
||||
-- >
|
||||
-- > gridselectWorkspace' defaultGSConfig
|
||||
-- > gridselectWorkspace' def
|
||||
-- > { gs_navigate = navNSearch
|
||||
-- > , gs_rearranger = searchStringRearrangerGenerator id
|
||||
-- > }
|
||||
@@ -789,7 +778,7 @@ noRearranger _ = return
|
||||
-- already present).
|
||||
searchStringRearrangerGenerator :: (String -> a) -> Rearranger a
|
||||
searchStringRearrangerGenerator f =
|
||||
let r "" xs = return $ xs
|
||||
r s xs | s `elem` map fst xs = return $ xs
|
||||
let r "" xs = return xs
|
||||
r s xs | s `elem` map fst xs = return xs
|
||||
| otherwise = return $ xs ++ [(s, f s)]
|
||||
in r
|
||||
|
@@ -1,8 +1,7 @@
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.GroupNavigation
|
||||
-- Description : Cycle through groups of windows across workspaces.
|
||||
-- Copyright : (c) nzeh@cs.dal.ca
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -16,7 +15,7 @@
|
||||
-- query.
|
||||
--
|
||||
-- Also provides a method for jumping back to the most recently used
|
||||
-- window in any given group.
|
||||
-- window in any given group, and predefined groups.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
|
||||
@@ -27,18 +26,25 @@ module XMonad.Actions.GroupNavigation ( -- * Usage
|
||||
, nextMatchOrDo
|
||||
, nextMatchWithThis
|
||||
, historyHook
|
||||
|
||||
-- * Utilities
|
||||
-- $utilities
|
||||
, isOnAnyVisibleWS
|
||||
) where
|
||||
|
||||
import Control.Monad.Reader
|
||||
import Data.Foldable as Fold
|
||||
import Data.Map as Map
|
||||
import Data.Sequence as Seq
|
||||
import Data.Set as Set
|
||||
import Control.Monad.State
|
||||
import Data.Map ((!))
|
||||
import qualified Data.Map as Map
|
||||
import Data.Sequence (Seq, ViewL (EmptyL, (:<)), viewl, (<|), (><), (|>))
|
||||
import qualified Data.Sequence as Seq
|
||||
import qualified Data.Set as Set
|
||||
import Graphics.X11.Types
|
||||
import Prelude hiding (concatMap, drop, elem, filter, null, reverse)
|
||||
import XMonad.Core
|
||||
import XMonad.ManageHook
|
||||
import XMonad.Operations (windows, withFocused)
|
||||
import XMonad.Prelude (elem, foldl')
|
||||
import qualified XMonad.StackSet as SS
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
|
||||
@@ -122,12 +128,12 @@ focusNextMatchOrDo qry act = findM (runQuery qry)
|
||||
-- Returns the list of windows ordered by workspace as specified in
|
||||
-- ~/.xmonad/xmonad.hs
|
||||
orderedWindowList :: Direction -> X (Seq Window)
|
||||
orderedWindowList History = liftM (\(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
|
||||
wsids <- asks (Seq.fromList . workspaces . config)
|
||||
let wspcs = orderedWorkspaceList ss wsids
|
||||
wins = dirfun dir
|
||||
$ Fold.foldl' (><) Seq.empty
|
||||
$ foldl' (><) Seq.empty
|
||||
$ fmap (Seq.fromList . SS.integrate' . SS.stack) wspcs
|
||||
cur = SS.peek ss
|
||||
return $ maybe wins (rotfun wins) cur
|
||||
@@ -141,8 +147,8 @@ orderedWorkspaceList :: WindowSet -> Seq String -> Seq WindowSpace
|
||||
orderedWorkspaceList ss wsids = rotateTo isCurWS wspcs'
|
||||
where
|
||||
wspcs = SS.workspaces ss
|
||||
wspcsMap = Fold.foldl' (\m ws -> Map.insert (SS.tag ws) ws m) Map.empty wspcs
|
||||
wspcs' = fmap (\wsid -> wspcsMap ! wsid) wsids
|
||||
wspcsMap = foldl' (\m ws -> Map.insert (SS.tag ws) ws m) Map.empty wspcs
|
||||
wspcs' = fmap (wspcsMap !) wsids
|
||||
isCurWS ws = SS.tag ws == SS.tag (SS.workspace $ SS.current ss)
|
||||
|
||||
--- History navigation, requires a layout modifier -------------------
|
||||
@@ -150,7 +156,7 @@ orderedWorkspaceList ss wsids = rotateTo isCurWS wspcs'
|
||||
-- The state extension that holds the history information
|
||||
data HistoryDB = HistoryDB (Maybe Window) -- currently focused window
|
||||
(Seq Window) -- previously focused windows
|
||||
deriving (Read, Show, Typeable)
|
||||
deriving (Read, Show)
|
||||
|
||||
instance ExtensionClass HistoryDB where
|
||||
|
||||
@@ -167,26 +173,11 @@ updateHistory :: HistoryDB -> X HistoryDB
|
||||
updateHistory (HistoryDB oldcur oldhist) = withWindowSet $ \ss -> do
|
||||
let newcur = SS.peek ss
|
||||
wins = Set.fromList $ SS.allWindows ss
|
||||
newhist = flt (flip Set.member wins) (ins oldcur oldhist)
|
||||
newhist = Seq.filter (`Set.member` wins) (ins oldcur oldhist)
|
||||
return $ HistoryDB newcur (del newcur newhist)
|
||||
where
|
||||
ins x xs = maybe xs (<| xs) x
|
||||
del x xs = maybe xs (\x' -> flt (/= x') xs) x
|
||||
|
||||
--- Two replacements for Seq.filter and Seq.breakl available only in
|
||||
--- containers-0.3.0.0, which only ships with ghc 6.12. Once we
|
||||
--- decide to no longer support ghc < 6.12, these should be replaced
|
||||
--- with Seq.filter and Seq.breakl.
|
||||
|
||||
flt :: (a -> Bool) -> Seq a -> Seq a
|
||||
flt p = Fold.foldl (\xs x -> if p x then xs |> x else xs) Seq.empty
|
||||
|
||||
brkl :: (a -> Bool) -> Seq a -> (Seq a, Seq a)
|
||||
brkl p xs = flip Seq.splitAt xs
|
||||
$ snd
|
||||
$ Fold.foldr (\x (i, j) -> if p x then (i-1, i-1) else (i-1, j)) (l, l) xs
|
||||
where
|
||||
l = Seq.length xs
|
||||
del x xs = maybe xs (\x' -> Seq.filter (/= x') xs) x
|
||||
|
||||
--- Some sequence helpers --------------------------------------------
|
||||
|
||||
@@ -200,7 +191,7 @@ rotate xs = rotate' (viewl xs)
|
||||
-- Rotates the sequence until an element matching the given condition
|
||||
-- is at the beginning of the sequence.
|
||||
rotateTo :: (a -> Bool) -> Seq a -> Seq a
|
||||
rotateTo cond xs = let (lxs, rxs) = brkl cond xs in rxs >< lxs
|
||||
rotateTo cond xs = let (lxs, rxs) = Seq.breakl cond xs in rxs >< lxs
|
||||
|
||||
--- A monadic find ---------------------------------------------------
|
||||
|
||||
@@ -216,3 +207,21 @@ findM cond xs = findM' cond (viewl xs)
|
||||
if isMatch
|
||||
then return (Just x')
|
||||
else findM qry xs'
|
||||
|
||||
|
||||
-- $utilities
|
||||
-- #utilities#
|
||||
-- Below are handy queries for use with 'nextMatch', 'nextMatchOrDo',
|
||||
-- and 'nextMatchWithThis'.
|
||||
|
||||
-- | A query that matches all windows on visible workspaces. This is
|
||||
-- useful for configurations with multiple screens, and matches even
|
||||
-- invisible windows.
|
||||
isOnAnyVisibleWS :: Query Bool
|
||||
isOnAnyVisibleWS = do
|
||||
w <- ask
|
||||
ws <- liftX $ gets windowset
|
||||
let allVisible = concat $ maybe [] SS.integrate . SS.stack . SS.workspace <$> SS.current ws:SS.visible ws
|
||||
visibleWs = w `elem` allVisible
|
||||
unfocused = Just w /= SS.peek ws
|
||||
return $ visibleWs && unfocused
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.KeyRemap
|
||||
-- Description : Remap Keybinding on the fly.
|
||||
-- Copyright : (c) Christian Dietrich
|
||||
-- License : BSD-style (as xmonad)
|
||||
--
|
||||
@@ -27,14 +27,13 @@ module XMonad.Actions.KeyRemap (
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude
|
||||
import XMonad.Util.Paste
|
||||
import Data.List
|
||||
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
import Control.Monad
|
||||
|
||||
|
||||
data KeymapTable = KeymapTable [((KeyMask, KeySym), (KeyMask, KeySym))] deriving (Typeable, Show)
|
||||
newtype KeymapTable = KeymapTable [((KeyMask, KeySym), (KeyMask, KeySym))] deriving (Show)
|
||||
|
||||
instance ExtensionClass KeymapTable where
|
||||
initialValue = KeymapTable []
|
||||
@@ -125,8 +124,8 @@ extractKeyMapping (KeymapTable table) mask sym =
|
||||
buildKeyRemapBindings :: [KeymapTable] -> [((KeyMask, KeySym), X ())]
|
||||
buildKeyRemapBindings keyremaps =
|
||||
[((mask, sym), doKeyRemap mask sym) | (mask, sym) <- bindings]
|
||||
where mappings = concat (map (\(KeymapTable table) -> table) keyremaps)
|
||||
bindings = nub (map (\binding -> fst binding) mappings)
|
||||
where mappings = concatMap (\(KeymapTable table) -> table) keyremaps
|
||||
bindings = nub (map fst mappings)
|
||||
|
||||
|
||||
-- Here come the Keymappings
|
||||
@@ -138,7 +137,7 @@ emptyKeyRemap = KeymapTable []
|
||||
dvorakProgrammerKeyRemap :: KeymapTable
|
||||
dvorakProgrammerKeyRemap =
|
||||
KeymapTable [((charToMask maskFrom, from), (charToMask maskTo, to)) |
|
||||
(maskFrom, from, maskTo, to) <- (zip4 layoutUsShift layoutUsKey layoutDvorakShift layoutDvorakKey)]
|
||||
(maskFrom, from, maskTo, to) <- zip4 layoutUsShift layoutUsKey layoutDvorakShift layoutDvorakKey]
|
||||
where
|
||||
|
||||
layoutUs = map (fromIntegral . fromEnum) "`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?" :: [KeySym]
|
||||
|
@@ -1,5 +1,6 @@
|
||||
{- |
|
||||
Module : XMonad.Actions.Launcher
|
||||
Description : A set of prompts for XMonad.
|
||||
Copyright : (C) 2012 Carlos López-Camey
|
||||
License : None; public domain
|
||||
|
||||
@@ -18,10 +19,9 @@ module XMonad.Actions.Launcher(
|
||||
, launcherPrompt
|
||||
) where
|
||||
|
||||
import Data.List (find, findIndex, isPrefixOf, tails)
|
||||
import qualified Data.Map as M
|
||||
import Data.Maybe (isJust)
|
||||
import XMonad hiding (config)
|
||||
import XMonad.Prelude
|
||||
import XMonad.Prompt
|
||||
import XMonad.Util.Run
|
||||
|
||||
@@ -62,8 +62,8 @@ type ExtensionActions = M.Map String (String -> X())
|
||||
instance XPrompt CalculatorMode where
|
||||
showXPrompt CalcMode = "calc %s> "
|
||||
commandToComplete CalcMode = id --send the whole string to `calc`
|
||||
completionFunction CalcMode = \s -> if (length s == 0) then return [] else do
|
||||
fmap lines $ runProcessWithInput "calc" [s] ""
|
||||
completionFunction CalcMode = \s -> if null s then return [] else
|
||||
lines <$> runProcessWithInput "calc" [s] ""
|
||||
modeAction CalcMode _ _ = return () -- do nothing; this might copy the result to the clipboard
|
||||
|
||||
-- | Uses the program `hoogle` to search for functions
|
||||
@@ -88,7 +88,7 @@ instance XPrompt HoogleMode where
|
||||
|
||||
-- | Creates an autocompletion function for a programm given the program's name and a list of args to send to the command.
|
||||
completionFunctionWith :: String -> [String] -> IO [String]
|
||||
completionFunctionWith cmd args = do fmap lines $ runProcessWithInput cmd args ""
|
||||
completionFunctionWith cmd args = lines <$> runProcessWithInput cmd args ""
|
||||
|
||||
-- | Creates a prompt with the given modes
|
||||
launcherPrompt :: XPConfig -> [XPMode] -> X()
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.LinkWorkspaces
|
||||
-- Description : Bindings to add and delete links between workspaces.
|
||||
-- Copyright : (c) Jan-David Quesel <quesel@gmail.org>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -14,7 +15,6 @@
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
module XMonad.Actions.LinkWorkspaces (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
@@ -27,6 +27,7 @@ module XMonad.Actions.LinkWorkspaces (
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude (for_)
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Layout.IndependentScreens(countScreens)
|
||||
import qualified XMonad.Util.ExtensibleState as XS (get, put)
|
||||
@@ -59,7 +60,7 @@ import qualified Data.Map as M
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
data MessageConfig = MessageConfig { messageFunction :: (ScreenId -> [Char] -> [Char] -> [Char] -> X())
|
||||
data MessageConfig = MessageConfig { messageFunction :: ScreenId -> [Char] -> [Char] -> [Char] -> X()
|
||||
, foreground :: [Char]
|
||||
, alertedForeground :: [Char]
|
||||
, background :: [Char]
|
||||
@@ -75,8 +76,8 @@ noMessageFn :: ScreenId -> [Char] -> [Char] -> [Char] -> X()
|
||||
noMessageFn _ _ _ _ = return () :: X ()
|
||||
|
||||
-- | Stuff for linking workspaces
|
||||
data WorkspaceMap = WorkspaceMap (M.Map WorkspaceId WorkspaceId) deriving (Read, Show, Typeable)
|
||||
instance ExtensionClass WorkspaceMap
|
||||
newtype WorkspaceMap = WorkspaceMap (M.Map WorkspaceId WorkspaceId) deriving (Read, Show)
|
||||
instance ExtensionClass WorkspaceMap
|
||||
where initialValue = WorkspaceMap M.empty
|
||||
extensionType = PersistentExtension
|
||||
|
||||
@@ -85,12 +86,12 @@ switchWS f m ws = switchWS' f m ws Nothing
|
||||
|
||||
-- | Switch to the given workspace in a non greedy way, stop if we reached the first screen
|
||||
-- | we already did switching on
|
||||
switchWS' :: (WorkspaceId -> X ()) -> MessageConfig -> WorkspaceId -> (Maybe ScreenId) -> X ()
|
||||
switchWS' :: (WorkspaceId -> X ()) -> MessageConfig -> WorkspaceId -> Maybe ScreenId -> X ()
|
||||
switchWS' switchFn message workspace stopAtScreen = do
|
||||
ws <- gets windowset
|
||||
nScreens <- countScreens
|
||||
let now = W.screen (W.current ws)
|
||||
let next = ((now + 1) `mod` nScreens)
|
||||
let next = (now + 1) `mod` nScreens
|
||||
switchFn workspace
|
||||
case stopAtScreen of
|
||||
Nothing -> sTM now next (Just now)
|
||||
@@ -99,21 +100,21 @@ switchWS' switchFn message workspace stopAtScreen = do
|
||||
|
||||
-- | Switch to the workspace that matches the current one, executing switches for that workspace as well.
|
||||
-- | The function switchWorkspaceNonGreedy' will take of stopping if we reached the first workspace again.
|
||||
switchToMatching :: (WorkspaceId -> (Maybe ScreenId) -> X ()) -> MessageConfig -> WorkspaceId -> ScreenId
|
||||
-> ScreenId -> (Maybe ScreenId) -> X ()
|
||||
switchToMatching :: (WorkspaceId -> Maybe ScreenId -> X ()) -> MessageConfig -> WorkspaceId -> ScreenId
|
||||
-> ScreenId -> Maybe ScreenId -> X ()
|
||||
switchToMatching f message t now next stopAtScreen = do
|
||||
WorkspaceMap matchings <- XS.get :: X WorkspaceMap
|
||||
case (M.lookup t matchings) of
|
||||
case M.lookup t matchings of
|
||||
Nothing -> return () :: X()
|
||||
Just newWorkspace -> do
|
||||
onScreen' (f newWorkspace stopAtScreen) FocusCurrent next
|
||||
onScreen' (f newWorkspace stopAtScreen) FocusCurrent next
|
||||
messageFunction message now (foreground message) (background message) ("Switching to: " ++ (t ++ " and " ++ newWorkspace))
|
||||
|
||||
-- | Insert a mapping between t1 and t2 or remove it was already present
|
||||
toggleMatching :: MessageConfig -> WorkspaceId -> WorkspaceId -> X ()
|
||||
toggleMatching message t1 t2 = do
|
||||
WorkspaceMap matchings <- XS.get :: X WorkspaceMap
|
||||
case (M.lookup t1 matchings) of
|
||||
case M.lookup t1 matchings of
|
||||
Nothing -> setMatching message t1 t2 matchings
|
||||
Just t -> if t == t2 then removeMatching' message t1 t2 matchings else setMatching message t1 t2 matchings
|
||||
return ()
|
||||
@@ -142,7 +143,7 @@ removeAllMatchings :: MessageConfig -> X ()
|
||||
removeAllMatchings message = do
|
||||
ws <- gets windowset
|
||||
let now = W.screen (W.current ws)
|
||||
XS.put $ WorkspaceMap $ M.empty
|
||||
XS.put $ WorkspaceMap M.empty
|
||||
messageFunction message now (alertedForeground message) (background message) "All links removed!"
|
||||
|
||||
-- | remove all matching regarding a given workspace
|
||||
@@ -163,7 +164,6 @@ toggleLinkWorkspaces' first message = do
|
||||
let now = W.screen (W.current ws)
|
||||
let next = (now + 1) `mod` nScreens
|
||||
if next == first then return () else do -- this is also the case if there is only one screen
|
||||
case (W.lookupWorkspace next ws) of
|
||||
Nothing -> return ()
|
||||
Just name -> toggleMatching message (W.currentTag ws) (name)
|
||||
for_ (W.lookupWorkspace next ws)
|
||||
(toggleMatching message (W.currentTag ws))
|
||||
onScreen' (toggleLinkWorkspaces' first message) FocusCurrent next
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.MessageFeedback
|
||||
-- Description : An alternative @sendMessage@.
|
||||
-- Copyright : (c) -- Quentin Moser <moserq@gmail.com>
|
||||
-- 2018 Yclept Nemo
|
||||
-- License : BSD3
|
||||
@@ -51,13 +52,11 @@ module XMonad.Actions.MessageFeedback
|
||||
|
||||
import XMonad ( Window )
|
||||
import XMonad.Core ( X(), Message, SomeMessage(..), LayoutClass(..), windowset, catchX, WorkspaceId, Layout, whenJust )
|
||||
import XMonad.Operations ( updateLayout, windowBracket, modifyWindowSet )
|
||||
import XMonad.Prelude ( isJust, liftA2, void )
|
||||
import XMonad.StackSet ( Workspace, current, workspace, layout, tag )
|
||||
import XMonad.Operations ( updateLayout, refresh, windows )
|
||||
|
||||
import Data.Maybe ( isJust )
|
||||
import Control.Monad ( when, void )
|
||||
import Control.Monad.State ( gets )
|
||||
import Control.Applicative ( (<$>), liftA2 )
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
@@ -107,11 +106,11 @@ import Control.Applicative ( (<$>), liftA2 )
|
||||
-- for efficiency this is pretty much an exact copy of the
|
||||
-- 'XMonad.Operations.sendMessage' code - foregoes the O(n) 'updateLayout'.
|
||||
sendSomeMessageB :: SomeMessage -> X Bool
|
||||
sendSomeMessageB m = do
|
||||
w <- workspace . current <$> gets windowset
|
||||
sendSomeMessageB m = windowBracket id $ do
|
||||
w <- gets ((workspace . current) . windowset)
|
||||
ml <- handleMessage (layout w) m `catchX` return Nothing
|
||||
whenJust ml $ \l ->
|
||||
windows $ \ws -> ws { current = (current ws)
|
||||
modifyWindowSet $ \ws -> ws { current = (current ws)
|
||||
{ workspace = (workspace $ current ws)
|
||||
{ layout = l }}}
|
||||
return $ isJust ml
|
||||
@@ -140,7 +139,7 @@ sendSomeMessageWithNoRefresh m = void . sendSomeMessageWithNoRefreshB m
|
||||
-- 'XMonad.Operations.sendMessageWithNoRefresh' (does not refresh).
|
||||
sendSomeMessageWithNoRefreshToCurrentB :: SomeMessage -> X Bool
|
||||
sendSomeMessageWithNoRefreshToCurrentB m
|
||||
= (gets $ workspace . current . windowset)
|
||||
= gets (workspace . current . windowset)
|
||||
>>= sendSomeMessageWithNoRefreshB m
|
||||
|
||||
-- | Variant of 'sendSomeMessageWithNoRefreshToCurrentB' that discards the
|
||||
@@ -178,9 +177,9 @@ sendMessageWithNoRefreshToCurrent = void . sendMessageWithNoRefreshToCurrentB
|
||||
-- that would have otherwise used 'XMonad.Operations.sendMessage' while
|
||||
-- minimizing refreshes, use this.
|
||||
sendSomeMessagesB :: [SomeMessage] -> X [Bool]
|
||||
sendSomeMessagesB m
|
||||
= mapM sendSomeMessageWithNoRefreshToCurrentB m
|
||||
>>= liftA2 (>>) (flip when refresh . or) return
|
||||
sendSomeMessagesB
|
||||
= windowBracket or
|
||||
. mapM sendSomeMessageWithNoRefreshToCurrentB
|
||||
|
||||
-- | Variant of 'sendSomeMessagesB' that discards the results.
|
||||
sendSomeMessages :: [SomeMessage] -> X ()
|
||||
|
@@ -1,6 +1,7 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.Minimize
|
||||
-- Description : Actions for minimizing and maximizing windows.
|
||||
-- Copyright : (c) Bogdan Sinitsyn (2016)
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -35,6 +36,7 @@ module XMonad.Actions.Minimize
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude (fromMaybe, join, listToMaybe)
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import qualified XMonad.Layout.BoringWindows as BW
|
||||
@@ -43,9 +45,6 @@ import XMonad.Util.Minimize
|
||||
import XMonad.Util.WindowProperties (getProp32)
|
||||
|
||||
import Foreign.C.Types (CLong)
|
||||
import Control.Applicative((<$>))
|
||||
import Control.Monad (join)
|
||||
import Data.Maybe (fromMaybe, listToMaybe)
|
||||
import qualified Data.List as L
|
||||
import qualified Data.Map as M
|
||||
|
||||
@@ -120,7 +119,7 @@ maximizeWindowAndFocus = maximizeWindowAndChangeWSet W.focusWindow
|
||||
-- | Perform an action with first minimized window on current workspace
|
||||
-- or do nothing if there is no minimized windows on current workspace
|
||||
withFirstMinimized :: (Window -> X ()) -> X ()
|
||||
withFirstMinimized action = withFirstMinimized' (flip whenJust action)
|
||||
withFirstMinimized action = withFirstMinimized' (`whenJust` action)
|
||||
|
||||
-- | Like withFirstMinimized but the provided action is always invoked with a
|
||||
-- 'Maybe Window', that will be nothing if there is no first minimized window.
|
||||
@@ -130,7 +129,7 @@ withFirstMinimized' action = withMinimized (action . listToMaybe . reverse)
|
||||
-- | Perform an action with last minimized window on current workspace
|
||||
-- or do nothing if there is no minimized windows on current workspace
|
||||
withLastMinimized :: (Window -> X ()) -> X ()
|
||||
withLastMinimized action = withLastMinimized' (flip whenJust action)
|
||||
withLastMinimized action = withLastMinimized' (`whenJust` action)
|
||||
|
||||
-- | Like withLastMinimized but the provided action is always invoked with a
|
||||
-- 'Maybe Window', that will be nothing if there is no last minimized window.
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.MouseGestures
|
||||
-- Description : Support for simple mouse gestures.
|
||||
-- Copyright : (c) Lukas Mai
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -21,14 +22,13 @@ module XMonad.Actions.MouseGestures (
|
||||
mkCollect
|
||||
) where
|
||||
|
||||
import XMonad.Prelude
|
||||
import XMonad
|
||||
import XMonad.Util.Types (Direction2D(..))
|
||||
|
||||
import Data.IORef
|
||||
import qualified Data.Map as M
|
||||
import Data.Map (Map)
|
||||
import Data.Maybe
|
||||
import Control.Monad
|
||||
|
||||
-- $usage
|
||||
--
|
||||
@@ -111,7 +111,7 @@ mouseGestureH moveHook endHook = do
|
||||
mouseGesture :: Map [Direction2D] (Window -> X ()) -> Window -> X ()
|
||||
mouseGesture tbl win = do
|
||||
(mov, end) <- mkCollect
|
||||
mouseGestureH (\d -> mov d >> return ()) $ end >>= \gest ->
|
||||
mouseGestureH (void . mov) $ end >>= \gest ->
|
||||
case M.lookup gest tbl of
|
||||
Nothing -> return ()
|
||||
Just f -> f win
|
||||
|
@@ -1,8 +1,9 @@
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.MouseResize
|
||||
-- Description : A layout modifier to resize windows with the mouse.
|
||||
-- Copyright : (c) 2007 Andrea Rossato
|
||||
-- License : BSD-style (see xmonad/LICENSE)
|
||||
--
|
||||
@@ -56,7 +57,7 @@ import XMonad.Util.XUtils
|
||||
mouseResize :: l a -> ModifiedLayout MouseResize l a
|
||||
mouseResize = ModifiedLayout (MR [])
|
||||
|
||||
data MouseResize a = MR [((a,Rectangle),Maybe a)]
|
||||
newtype MouseResize a = MR [((a,Rectangle),Maybe a)]
|
||||
instance Show (MouseResize a) where show _ = ""
|
||||
instance Read (MouseResize a) where readsPrec _ s = [(MR [], s)]
|
||||
|
||||
@@ -68,7 +69,7 @@ instance LayoutModifier MouseResize Window where
|
||||
where
|
||||
wrs' = wrs_to_state [] . filter (isInStack s . fst) $ wrs
|
||||
initState = mapM createInputWindow wrs'
|
||||
processState = mapM (deleteInputWin . snd) st >> mapM createInputWindow wrs'
|
||||
processState = mapM_ (deleteInputWin . snd) st >> mapM createInputWindow wrs'
|
||||
|
||||
inputRectangle (Rectangle x y wh ht) = Rectangle (x + fi wh - 5) (y + fi ht - 5) 10 10
|
||||
|
||||
@@ -105,7 +106,7 @@ handleResize st ButtonEvent { ev_window = ew, ev_event_type = et }
|
||||
handleResize _ _ = return ()
|
||||
|
||||
createInputWindow :: ((Window,Rectangle), Maybe Rectangle) -> X ((Window,Rectangle),Maybe Window)
|
||||
createInputWindow ((w,r),mr) = do
|
||||
createInputWindow ((w,r),mr) =
|
||||
case mr of
|
||||
Just tr -> withDisplay $ \d -> do
|
||||
tw <- mkInputWindow d tr
|
||||
|
@@ -1,8 +1,9 @@
|
||||
{-# LANGUAGE DeriveDataTypeable, MultiParamTypeClasses, PatternGuards, RankNTypes, TypeSynonymInstances #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses, PatternGuards, RankNTypes, TypeSynonymInstances #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.Navigation2D
|
||||
-- Description : Directional navigation of windows and screens.
|
||||
-- Copyright : (c) 2011 Norbert Zeh <nzeh@cs.dal.ca>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -39,7 +40,6 @@ module XMonad.Actions.Navigation2D ( -- * Usage
|
||||
, withNavigation2DConfig
|
||||
, Navigation2DConfig(..)
|
||||
, def
|
||||
, defaultNavigation2DConfig
|
||||
, Navigation2D
|
||||
, lineNavigation
|
||||
, centerNavigation
|
||||
@@ -58,11 +58,10 @@ module XMonad.Actions.Navigation2D ( -- * Usage
|
||||
, Direction2D(..)
|
||||
) where
|
||||
|
||||
import Control.Applicative
|
||||
import qualified Data.List as L
|
||||
import qualified Data.Map as M
|
||||
import Data.Maybe
|
||||
import Data.Ord (comparing)
|
||||
import Control.Arrow (second)
|
||||
import XMonad.Prelude
|
||||
import XMonad hiding (Screen)
|
||||
import qualified XMonad.StackSet as W
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
@@ -386,7 +385,7 @@ data Navigation2DConfig = Navigation2DConfig
|
||||
-- function calculates a rectangle for a given unmapped
|
||||
-- window from the screen it is on and its window ID.
|
||||
-- See <#Finer_Points> for how to use this.
|
||||
} deriving Typeable
|
||||
}
|
||||
|
||||
-- | Shorthand for the tedious screen type
|
||||
type Screen = W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
|
||||
@@ -451,10 +450,6 @@ withNavigation2DConfig conf2d xconf = xconf { startupHook = startupHook xconf
|
||||
>> XS.put conf2d
|
||||
}
|
||||
|
||||
{-# DEPRECATED defaultNavigation2DConfig "Use def (from Data.Default, and re-exported from XMonad.Actions.Navigation2D) instead." #-}
|
||||
defaultNavigation2DConfig :: Navigation2DConfig
|
||||
defaultNavigation2DConfig = def
|
||||
|
||||
instance Default Navigation2DConfig where
|
||||
def = Navigation2DConfig { defaultTiledNavigation = lineNavigation
|
||||
, floatNavigation = centerNavigation
|
||||
@@ -482,7 +477,7 @@ switchLayer = actOnLayer otherLayer
|
||||
-- navigation should wrap around (e.g., from the left edge of the leftmost
|
||||
-- screen to the right edge of the rightmost screen).
|
||||
windowGo :: Direction2D -> Bool -> X ()
|
||||
windowGo dir wrap = actOnLayer thisLayer
|
||||
windowGo dir = actOnLayer thisLayer
|
||||
( \ conf cur wins -> windows
|
||||
$ doTiledNavigation conf dir W.focusWindow cur wins
|
||||
)
|
||||
@@ -492,7 +487,6 @@ windowGo dir wrap = actOnLayer thisLayer
|
||||
( \ conf cur wspcs -> windows
|
||||
$ doScreenNavigation conf dir W.view cur wspcs
|
||||
)
|
||||
wrap
|
||||
|
||||
-- | Swaps the current window with the next window in the given direction and in
|
||||
-- the same layer as the current window. (In the floating layer, all that
|
||||
@@ -501,7 +495,7 @@ windowGo dir wrap = actOnLayer thisLayer
|
||||
-- window's screen but retains its position and size relative to the screen.)
|
||||
-- The second argument indicates wrapping (see 'windowGo').
|
||||
windowSwap :: Direction2D -> Bool -> X ()
|
||||
windowSwap dir wrap = actOnLayer thisLayer
|
||||
windowSwap dir = actOnLayer thisLayer
|
||||
( \ conf cur wins -> windows
|
||||
$ doTiledNavigation conf dir swap cur wins
|
||||
)
|
||||
@@ -509,32 +503,28 @@ windowSwap dir wrap = actOnLayer thisLayer
|
||||
$ doFloatNavigation conf dir swap cur wins
|
||||
)
|
||||
( \ _ _ _ -> return () )
|
||||
wrap
|
||||
|
||||
-- | Moves the current window to the next screen in the given direction. The
|
||||
-- second argument indicates wrapping (see 'windowGo').
|
||||
windowToScreen :: Direction2D -> Bool -> X ()
|
||||
windowToScreen dir wrap = actOnScreens ( \ conf cur wspcs -> windows
|
||||
windowToScreen dir = actOnScreens ( \ conf cur wspcs -> windows
|
||||
$ doScreenNavigation conf dir W.shift cur wspcs
|
||||
)
|
||||
wrap
|
||||
|
||||
-- | Moves the focus to the next screen in the given direction. The second
|
||||
-- argument indicates wrapping (see 'windowGo').
|
||||
screenGo :: Direction2D -> Bool -> X ()
|
||||
screenGo dir wrap = actOnScreens ( \ conf cur wspcs -> windows
|
||||
screenGo dir = actOnScreens ( \ conf cur wspcs -> windows
|
||||
$ doScreenNavigation conf dir W.view cur wspcs
|
||||
)
|
||||
wrap
|
||||
|
||||
-- | Swaps the workspace on the current screen with the workspace on the screen
|
||||
-- in the given direction. The second argument indicates wrapping (see
|
||||
-- 'windowGo').
|
||||
screenSwap :: Direction2D -> Bool -> X ()
|
||||
screenSwap dir wrap = actOnScreens ( \ conf cur wspcs -> windows
|
||||
screenSwap dir = actOnScreens ( \ conf cur wspcs -> windows
|
||||
$ doScreenNavigation conf dir W.greedyView cur wspcs
|
||||
)
|
||||
wrap
|
||||
|
||||
-- | Maps each window to a fullscreen rect. This may not be the same rectangle the
|
||||
-- window maps to under the Full layout or a similar layout if the layout
|
||||
@@ -654,7 +644,7 @@ doFocusClosestWindow (cur, rect) winrects
|
||||
where
|
||||
ctr = centerOf rect
|
||||
winctrs = filter ((cur /=) . fst)
|
||||
$ map (\(w, r) -> (w, centerOf r)) winrects
|
||||
$ map (second centerOf) winrects
|
||||
closer wc1@(_, c1) wc2@(_, c2) | lDist ctr c1 > lDist ctr c2 = wc2
|
||||
| otherwise = wc1
|
||||
|
||||
@@ -674,8 +664,7 @@ doTiledNavigation conf dir act cur winrects winset
|
||||
nav = maximum
|
||||
$ map ( fromMaybe (defaultTiledNavigation conf)
|
||||
. flip L.lookup (layoutNavigation conf)
|
||||
)
|
||||
$ layouts
|
||||
) layouts
|
||||
|
||||
-- | Implements navigation for the float layer
|
||||
doFloatNavigation :: Navigation2DConfig
|
||||
@@ -720,7 +709,7 @@ doLineNavigation dir (cur, rect) winrects
|
||||
|
||||
-- The list of windows that are candidates to receive focus.
|
||||
winrects' = filter dirFilter
|
||||
$ filter ((cur /=) . fst)
|
||||
. filter ((cur /=) . fst)
|
||||
$ winrects
|
||||
|
||||
-- Decides whether a given window matches the criteria to be a candidate to
|
||||
@@ -761,9 +750,8 @@ doCenterNavigation dir (cur, rect) winrects
|
||||
-- center rotated so the right cone becomes the relevant cone.
|
||||
-- The windows are ordered in the order they should be preferred
|
||||
-- when they are otherwise tied.
|
||||
winctrs = map (\(w, r) -> (w, dirTransform . centerOf $ r))
|
||||
$ stackTransform
|
||||
$ winrects
|
||||
winctrs = map (second (dirTransform . centerOf))
|
||||
$ stackTransform winrects
|
||||
|
||||
-- Give preference to windows later in the stack for going left or up and to
|
||||
-- windows earlier in the stack for going right or down. (The stack order
|
||||
@@ -821,7 +809,7 @@ doSideNavigationWithBias ::
|
||||
Eq a => Int -> Direction2D -> Rect a -> [Rect a] -> Maybe a
|
||||
doSideNavigationWithBias bias dir (cur, rect)
|
||||
= fmap fst . listToMaybe
|
||||
. L.sortBy (comparing dist) . foldr acClosest []
|
||||
. L.sortOn dist . foldr acClosest []
|
||||
. filter (`toRightOf` (cur, transform rect))
|
||||
. map (fmap transform)
|
||||
where
|
||||
@@ -849,7 +837,7 @@ doSideNavigationWithBias bias dir (cur, rect)
|
||||
-- Greedily accumulate the windows tied for the leftmost left side.
|
||||
acClosest (w, r) l@((_, r'):_) | x1 r == x1 r' = (w, r) : l
|
||||
| x1 r > x1 r' = l
|
||||
acClosest (w, r) _ = (w, r) : []
|
||||
acClosest (w, r) _ = [(w, r)]
|
||||
|
||||
-- Given a (_, SideRect), calculate how far it is from the y=bias line.
|
||||
dist (_, r) | (y1 r <= bias) && (bias <= y2 r) = 0
|
||||
@@ -870,7 +858,7 @@ swap win winset = W.focusWindow cur
|
||||
visws = map W.workspace scrs
|
||||
|
||||
-- The focused windows of the visible workspaces
|
||||
focused = mapMaybe (\ws -> W.focus <$> W.stack ws) visws
|
||||
focused = mapMaybe (fmap W.focus . W.stack) visws
|
||||
|
||||
-- The window lists of the visible workspaces
|
||||
wins = map (W.integrate' . W.stack) visws
|
||||
@@ -895,14 +883,10 @@ swap win winset = W.focusWindow cur
|
||||
centerOf :: Rectangle -> (Position, Position)
|
||||
centerOf r = (rect_x r + fi (rect_width r) `div` 2, rect_y r + fi (rect_height r) `div` 2)
|
||||
|
||||
-- | Shorthand for integer conversions
|
||||
fi :: (Integral a, Num b) => a -> b
|
||||
fi = fromIntegral
|
||||
|
||||
-- | Functions to choose the subset of windows to operate on
|
||||
thisLayer, otherLayer :: a -> a -> a
|
||||
thisLayer = curry fst
|
||||
otherLayer = curry snd
|
||||
thisLayer = const
|
||||
otherLayer _ x = x
|
||||
|
||||
-- | Returns the list of visible workspaces and their screen rects
|
||||
visibleWorkspaces :: WindowSet -> Bool -> [WSRect]
|
||||
@@ -939,8 +923,8 @@ wrapOffsets winset = (max_x - min_x, max_y - min_y)
|
||||
where
|
||||
min_x = fi $ minimum $ map rect_x rects
|
||||
min_y = fi $ minimum $ map rect_y rects
|
||||
max_x = fi $ maximum $ map (\r -> rect_x r + (fi $ rect_width r)) rects
|
||||
max_y = fi $ maximum $ map (\r -> rect_y r + (fi $ rect_height r)) rects
|
||||
max_x = fi $ maximum $ map (\r -> rect_x r + fi (rect_width r)) rects
|
||||
max_y = fi $ maximum $ map (\r -> rect_y r + fi (rect_height r)) rects
|
||||
rects = map snd $ visibleWorkspaces winset False
|
||||
|
||||
|
||||
@@ -950,16 +934,16 @@ sortedScreens :: WindowSet -> [Screen]
|
||||
sortedScreens winset = L.sortBy cmp
|
||||
$ W.screens winset
|
||||
where
|
||||
cmp s1 s2 | x1 < x2 = LT
|
||||
| x1 > x2 = GT
|
||||
| y1 < x2 = LT
|
||||
| y1 > y2 = GT
|
||||
cmp s1 s2 | x < x' = LT
|
||||
| x > x' = GT
|
||||
| y < x' = LT
|
||||
| y > y' = GT
|
||||
| otherwise = EQ
|
||||
where
|
||||
(x1, y1) = centerOf (screenRect . W.screenDetail $ s1)
|
||||
(x2, y2) = centerOf (screenRect . W.screenDetail $ s2)
|
||||
(x , y ) = centerOf (screenRect . W.screenDetail $ s1)
|
||||
(x', y') = centerOf (screenRect . W.screenDetail $ s2)
|
||||
|
||||
|
||||
-- | Calculates the L1-distance between two points.
|
||||
lDist :: (Position, Position) -> (Position, Position) -> Int
|
||||
lDist (x1, y1) (x2, y2) = abs (fi $ x1 - x2) + abs (fi $ y1 - y2)
|
||||
lDist (x, y) (x', y') = abs (fi $ x - x') + abs (fi $ y - y')
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.NoBorders
|
||||
-- Description : Helper functions for dealing with window borders.
|
||||
-- Copyright : (c) Lukas Mai
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -27,7 +28,7 @@ toggleBorder :: Window -> X ()
|
||||
toggleBorder w = do
|
||||
bw <- asks (borderWidth . config)
|
||||
withDisplay $ \d -> io $ do
|
||||
cw <- wa_border_width `fmap` getWindowAttributes d w
|
||||
cw <- wa_border_width <$> getWindowAttributes d w
|
||||
if cw == 0
|
||||
then setWindowBorderWidth d w bw
|
||||
else setWindowBorderWidth d w 0
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.OnScreen
|
||||
-- Description : Control workspaces on different screens (in xinerama mode).
|
||||
-- Copyright : (c) 2009 Nils Schweinsberg
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -26,12 +27,9 @@ module XMonad.Actions.OnScreen (
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude (fromMaybe, guard)
|
||||
import XMonad.StackSet hiding (new)
|
||||
|
||||
import Control.Monad (guard)
|
||||
-- import Control.Monad.State.Class (gets)
|
||||
import Data.Maybe (fromMaybe)
|
||||
|
||||
|
||||
-- | Focus data definitions
|
||||
data Focus = FocusNew -- ^ always focus the new screen
|
||||
|
61
XMonad/Actions/PerWindowKeys.hs
Normal file
61
XMonad/Actions/PerWindowKeys.hs
Normal file
@@ -0,0 +1,61 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.PerWindowKeys
|
||||
-- Description : Define key-bindings on a per-window basis.
|
||||
-- Copyright : (c) Wilson Sales, 2019
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Wilson Sales <spoonm@spoonm.org>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Define key-bindings on a per-window basis.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.PerWindowKeys (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
bindAll,
|
||||
bindFirst
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
|
||||
-- $usage
|
||||
--
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.PerWindowKeys
|
||||
--
|
||||
-- > ,((0, xK_F2), bindFirst [(className =? "firefox", spawn "dmenu"), (isFloat, withFocused $ windows . W.sink)])
|
||||
--
|
||||
-- > ,((0, xK_F3), bindAll [(isDialog, kill), (pure True, doSomething)])
|
||||
--
|
||||
-- If you want an action that will always run, but also want to do something for
|
||||
-- other queries, you can use @'bindAll' [(query1, action1), ..., (pure True,
|
||||
-- alwaysDoThisAction)]@.
|
||||
--
|
||||
-- Similarly, if you want a default action to be run if all the others failed,
|
||||
-- you can use @'bindFirst' [(query1, action1), ..., (pure True,
|
||||
-- doThisIfTheOthersFail)]@.
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
-- | Run an action if a Query holds true. Doesn't stop at the first one that
|
||||
-- does, however, and could potentially run all actions.
|
||||
bindAll :: [(Query Bool, X ())] -> X ()
|
||||
bindAll = mapM_ choose where
|
||||
choose (mh,action) = withFocused $ \w -> whenX (runQuery mh w) action
|
||||
|
||||
-- | Run the action paired with the first Query that holds true.
|
||||
bindFirst :: [(Query Bool, X ())] -> X ()
|
||||
bindFirst = withFocused . chooseOne
|
||||
|
||||
chooseOne :: [(Query Bool, X ())] -> Window -> X ()
|
||||
chooseOne [] _ = return ()
|
||||
chooseOne ((mh,a):bs) w = do
|
||||
c <- runQuery mh w
|
||||
if c then a
|
||||
else chooseOne bs w
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.PerWorkspaceKeys
|
||||
-- Description : Define key-bindings on per-workspace basis.
|
||||
-- Copyright : (c) Roman Cheplyaka, 2008
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -46,4 +47,3 @@ bindOn bindings = chooseAction chooser where
|
||||
Nothing -> case lookup "" bindings of
|
||||
Just action -> action
|
||||
Nothing -> return ()
|
||||
|
||||
|
@@ -2,6 +2,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.PhysicalScreens
|
||||
-- Description : Manipulate screens ordered by physical location instead of ID.
|
||||
-- Copyright : (c) Nelson Elhage <nelhage@mit.edu>
|
||||
-- License : BSD
|
||||
--
|
||||
@@ -30,11 +31,9 @@ module XMonad.Actions.PhysicalScreens (
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude (elemIndex, fromMaybe, on, sortBy)
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import Data.List (sortBy,findIndex)
|
||||
import Data.Function (on)
|
||||
|
||||
{- $usage
|
||||
|
||||
This module allows you name Xinerama screens from XMonad using their
|
||||
@@ -63,7 +62,7 @@ Example usage in your @~\/.xmonad\/xmonad.hs@ file:
|
||||
> --
|
||||
> [((modm .|. mask, key), f sc)
|
||||
> | (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
|
||||
> , (f, mask) <- [(viewScreen, 0), (sendToScreen def, shiftMask)]]
|
||||
> , (f, mask) <- [(viewScreen def, 0), (sendToScreen def, shiftMask)]]
|
||||
|
||||
For detailed instructions on editing your key bindings, see
|
||||
"XMonad.Doc.Extending#Editing_key_bindings".
|
||||
@@ -72,7 +71,7 @@ For detailed instructions on editing your key bindings, see
|
||||
-- | The type of the index of a screen by location
|
||||
newtype PhysicalScreen = P Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
|
||||
|
||||
getScreenIdAndRectangle :: (W.Screen i l a ScreenId ScreenDetail) -> (ScreenId, Rectangle)
|
||||
getScreenIdAndRectangle :: W.Screen i l a ScreenId ScreenDetail -> (ScreenId, Rectangle)
|
||||
getScreenIdAndRectangle screen = (W.screen screen, rect) where
|
||||
rect = screenRect $ W.screenDetail screen
|
||||
|
||||
@@ -89,8 +88,8 @@ getScreen (ScreenComparator cmpScreen) (P i) = do w <- gets windowset
|
||||
viewScreen :: ScreenComparator -> PhysicalScreen -> X ()
|
||||
viewScreen sc p = do i <- getScreen sc p
|
||||
whenJust i $ \s -> do
|
||||
w <- screenWorkspace s
|
||||
whenJust w $ windows . W.view
|
||||
w <- screenWorkspace s
|
||||
whenJust w $ windows . W.view
|
||||
|
||||
-- | Send the active window to a given physical screen
|
||||
sendToScreen :: ScreenComparator -> PhysicalScreen -> X ()
|
||||
@@ -131,7 +130,7 @@ getNeighbour :: ScreenComparator -> Int -> X ScreenId
|
||||
getNeighbour (ScreenComparator cmpScreen) d =
|
||||
do w <- gets windowset
|
||||
let ss = map W.screen $ sortBy (cmpScreen `on` getScreenIdAndRectangle) $ W.current w : W.visible w
|
||||
curPos = maybe 0 id $ findIndex (== W.screen (W.current w)) ss
|
||||
curPos = fromMaybe 0 $ elemIndex (W.screen (W.current w)) ss
|
||||
pos = (curPos + d) `mod` length ss
|
||||
return $ ss !! pos
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.Plane
|
||||
-- Description : Navigate through workspaces in a bidimensional manner.
|
||||
-- Copyright : (c) Marco Túlio Gontijo e Silva <marcot@riseup.net>,
|
||||
-- Leonardo Serra <leoserra@minaslivre.org>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
@@ -38,11 +39,9 @@ module XMonad.Actions.Plane
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Monad
|
||||
import Data.List
|
||||
import Data.Map hiding (split)
|
||||
import Data.Maybe
|
||||
import Data.Map (Map, fromList)
|
||||
|
||||
import XMonad.Prelude
|
||||
import XMonad
|
||||
import XMonad.StackSet hiding (workspaces)
|
||||
import XMonad.Util.Run
|
||||
|
212
XMonad/Actions/Prefix.hs
Normal file
212
XMonad/Actions/Prefix.hs
Normal file
@@ -0,0 +1,212 @@
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.Prefix
|
||||
-- Description : Use an Emacs-style prefix argument for commands.
|
||||
-- Copyright : (c) Matus Goljer <matus.goljer@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Matus Goljer <matus.goljer@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- A module that allows the user to use a prefix argument (raw or numeric).
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.Prefix
|
||||
(
|
||||
-- * Usage
|
||||
-- $usage
|
||||
|
||||
-- * Installation
|
||||
-- $installation
|
||||
|
||||
PrefixArgument(..)
|
||||
, usePrefixArgument
|
||||
, useDefaultPrefixArgument
|
||||
, withPrefixArgument
|
||||
, isPrefixRaw
|
||||
, isPrefixNumeric
|
||||
, ppFormatPrefix
|
||||
) where
|
||||
|
||||
import qualified Data.Map as M
|
||||
|
||||
import XMonad.Prelude
|
||||
import XMonad
|
||||
import XMonad.Util.ExtensibleState as XS
|
||||
import XMonad.Util.Paste (sendKey)
|
||||
import XMonad.Actions.Submap (submapDefaultWithKey)
|
||||
import XMonad.Util.EZConfig (readKeySequence)
|
||||
|
||||
{- $usage
|
||||
|
||||
This module implements Emacs-style prefix argument. The argument
|
||||
comes in two flavours, "Raw" and "Numeric".
|
||||
|
||||
To initiate the "prefix mode" you hit the prefix keybinding (default
|
||||
C-u). This sets the Raw argument value to 1. Repeatedly hitting this
|
||||
key increments the raw value by 1. The Raw argument is usually used
|
||||
as a toggle, changing the behaviour of the function called in some way.
|
||||
|
||||
An example might be calling "mpc add" to add new song to the playlist,
|
||||
but with C-u we also clean up the playlist beforehand.
|
||||
|
||||
When in the "Raw mode", you can hit numeric keys 0..9 (with no
|
||||
modifier) to enter a "Numeric argument". Numeric argument represents
|
||||
a natural number. Hitting numeric keys in sequence produces the
|
||||
decimal number that would result from typing them. That is, the
|
||||
sequence C-u 4 2 sets the Numeric argument value to the number 42.
|
||||
|
||||
If you have a function which understands the prefix argument, for example:
|
||||
|
||||
> addMaybeClean :: PrefixArgument -> X ()
|
||||
> addMaybeClean (Raw _) = spawn "mpc clear" >> spawn "mpc add <file>"
|
||||
> addMaybeClean _ = spawn "mpc add <file>"
|
||||
|
||||
you can turn it into an X action with the function 'withPrefixArgument'.
|
||||
|
||||
Binding it in your config
|
||||
|
||||
> ((modm, xK_a), withPrefixArgument addMaybeClean)
|
||||
|
||||
Hitting MOD-a will add the <file> to the playlist while C-u MOD-a will
|
||||
clear the playlist and then add the file.
|
||||
|
||||
You can of course use an anonymous action, like so:
|
||||
|
||||
> ((modm, xK_a), withPrefixArgument $ \prefix -> do
|
||||
> case prefix of ...
|
||||
> )
|
||||
|
||||
If the prefix key is followed by a binding which is unknown to XMonad,
|
||||
the prefix along with that binding is sent to the active window.
|
||||
|
||||
There is one caveat: when you use an application which has a nested
|
||||
C-u binding, for example C-c C-u in Emacs org-mode, you have to hit
|
||||
C-g (or any other non-recognized key really) to get out of the "xmonad
|
||||
grab" and let the C-c C-u be sent to the application.
|
||||
|
||||
-}
|
||||
|
||||
{- $installation
|
||||
|
||||
The simplest way to enable this is to use 'useDefaultPrefixArgument'
|
||||
|
||||
> xmonad $ useDefaultPrefixArgument $ def { .. }
|
||||
|
||||
The default prefix argument is C-u. If you want to customize the
|
||||
prefix argument, 'usePrefixArgument' can be used:
|
||||
|
||||
> xmonad $ usePrefixArgument "M-u" $ def { .. }
|
||||
|
||||
where the key is entered in Emacs style (or "XMonad.Util.EZConfig"
|
||||
style) notation. The letter `M` stands for your chosen modifier. The
|
||||
function defaults to C-u if the argument could not be parsed.
|
||||
-}
|
||||
|
||||
data PrefixArgument = Raw Int | Numeric Int | None
|
||||
deriving (Read, Show)
|
||||
instance ExtensionClass PrefixArgument where
|
||||
initialValue = None
|
||||
extensionType = PersistentExtension
|
||||
|
||||
-- | Run 'job' in the 'X' monad and then execute 'cleanup'. In case
|
||||
-- of exception, 'cleanup' is executed anyway.
|
||||
--
|
||||
-- Return the return value of 'job'.
|
||||
finallyX :: X a -> X a -> X a
|
||||
finallyX job cleanup = catchX (job >>= \r -> cleanup >> return r) cleanup
|
||||
|
||||
-- | Set up Prefix. Defaults to C-u when given an invalid key.
|
||||
--
|
||||
-- See usage section.
|
||||
usePrefixArgument :: LayoutClass l Window
|
||||
=> String
|
||||
-> XConfig l
|
||||
-> XConfig l
|
||||
usePrefixArgument prefix conf =
|
||||
conf{ keys = M.insert binding (handlePrefixArg [binding]) . keys conf }
|
||||
where
|
||||
binding = case readKeySequence conf prefix of
|
||||
Just [key] -> key
|
||||
_ -> (controlMask, xK_u)
|
||||
|
||||
-- | Set Prefix up with default prefix key (C-u).
|
||||
useDefaultPrefixArgument :: LayoutClass l Window
|
||||
=> XConfig l
|
||||
-> XConfig l
|
||||
useDefaultPrefixArgument = usePrefixArgument "C-u"
|
||||
|
||||
handlePrefixArg :: [(KeyMask, KeySym)] -> X ()
|
||||
handlePrefixArg events = do
|
||||
ks <- asks keyActions
|
||||
logger <- asks (logHook . config)
|
||||
flip finallyX (XS.put None >> logger) $ do
|
||||
prefix <- XS.get
|
||||
case prefix of
|
||||
Raw a -> XS.put $ Raw (a + 1)
|
||||
None -> XS.put $ Raw 1
|
||||
_ -> return ()
|
||||
logger
|
||||
submapDefaultWithKey defaultKey ks
|
||||
where defaultKey key@(m, k) =
|
||||
if k `elem` (xK_0 : [xK_1 .. xK_9]) && m == noModMask
|
||||
then do
|
||||
prefix <- XS.get
|
||||
let x = fromJust (Prelude.lookup k keyToNum)
|
||||
case prefix of
|
||||
Raw _ -> XS.put $ Numeric x
|
||||
Numeric a -> XS.put $ Numeric $ a * 10 + x
|
||||
None -> return () -- should never happen
|
||||
handlePrefixArg (key:events)
|
||||
else do
|
||||
prefix <- XS.get
|
||||
mapM_ (uncurry sendKey) $ case prefix of
|
||||
Raw a -> replicate a (head events) ++ [key]
|
||||
_ -> reverse (key:events)
|
||||
keyToNum = (xK_0, 0) : zip [xK_1 .. xK_9] [1..9]
|
||||
|
||||
-- | Turn a prefix-aware X action into an X-action.
|
||||
--
|
||||
-- First, fetch the current prefix, then pass it as argument to the
|
||||
-- original function. You should use this to "run" your commands.
|
||||
withPrefixArgument :: (PrefixArgument -> X ()) -> X ()
|
||||
withPrefixArgument = (>>=) XS.get
|
||||
|
||||
-- | Test if 'PrefixArgument' is 'Raw' or not.
|
||||
isPrefixRaw :: PrefixArgument -> Bool
|
||||
isPrefixRaw (Raw _) = True
|
||||
isPrefixRaw _ = False
|
||||
|
||||
-- | Test if 'PrefixArgument' is 'Numeric' or not.
|
||||
isPrefixNumeric :: PrefixArgument -> Bool
|
||||
isPrefixNumeric (Numeric _) = True
|
||||
isPrefixNumeric _ = False
|
||||
|
||||
-- | Format the prefix using the Emacs convetion for use in a
|
||||
-- statusbar, like xmobar.
|
||||
--
|
||||
-- To add this formatted prefix to printer output, you can set it up
|
||||
-- like so
|
||||
--
|
||||
-- > myPrinter :: PP
|
||||
-- > myPrinter = def { ppExtras = [ppFormatPrefix] }
|
||||
--
|
||||
-- And then add to your status bar using "XMonad.Hooks.StatusBar":
|
||||
--
|
||||
-- > mySB = statusBarProp "xmobar" myPrinter
|
||||
-- > main = xmonad $ withEasySB mySB defToggleStrutsKey def
|
||||
--
|
||||
-- Or, directly in your 'logHook' configuration
|
||||
--
|
||||
-- > logHook = dynamicLogWithPP myPrinter
|
||||
ppFormatPrefix :: X (Maybe String)
|
||||
ppFormatPrefix = do
|
||||
prefix <- XS.get
|
||||
return $ case prefix of
|
||||
Raw n -> Just $ foldr1 (\a b -> a ++ " " ++ b) $ replicate n "C-u"
|
||||
Numeric n -> Just $ "C-u " ++ show n
|
||||
None -> Nothing
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.Promote
|
||||
-- Description : Alternate promote function for xmonad.
|
||||
-- Copyright : (c) Miikka Koskinen 2007
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.RandomBackground
|
||||
-- Description : Start terminals with a random background color.
|
||||
-- Copyright : (c) 2009 Anze Slosar
|
||||
-- translation to Haskell by Adam Vogt
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
@@ -24,7 +25,6 @@ module XMonad.Actions.RandomBackground (
|
||||
import XMonad(X, XConf(config), XConfig(terminal), io, spawn,
|
||||
MonadIO, asks)
|
||||
import System.Random
|
||||
import Control.Monad(liftM)
|
||||
import Numeric(showHex)
|
||||
|
||||
-- $usage
|
||||
@@ -55,7 +55,7 @@ randPermutation xs g = swap $ zip (randoms g) xs
|
||||
|
||||
-- | @randomBg'@ produces a random hex number in the form @'#xxyyzz'@
|
||||
randomBg' :: (MonadIO m) => RandomColor -> m String
|
||||
randomBg' (RGB l h) = io $ liftM (toHex . take 3 . randomRs (l,h)) newStdGen
|
||||
randomBg' (RGB l h) = io $ fmap (toHex . take 3 . randomRs (l,h)) newStdGen
|
||||
randomBg' (HSV s v) = io $ do
|
||||
g <- newStdGen
|
||||
let x = (^(2::Int)) $ fst $ randomR (0,sqrt $ pi / 3) g
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.RotSlaves
|
||||
-- Description : Rotate all windows except the master window and keep the focus in place.
|
||||
-- Copyright : (c) Hans Philipp Annen <haphi@gmx.net>, Mischa Dieterle <der_m@freenet.de>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -40,8 +41,8 @@ import XMonad
|
||||
-- | Rotate the windows in the current stack, excluding the first one
|
||||
-- (master).
|
||||
rotSlavesUp,rotSlavesDown :: X ()
|
||||
rotSlavesUp = windows $ modify' (rotSlaves' (\l -> (tail l)++[head l]))
|
||||
rotSlavesDown = windows $ modify' (rotSlaves' (\l -> [last l]++(init l)))
|
||||
rotSlavesUp = windows $ modify' (rotSlaves' (\l -> tail l++[head l]))
|
||||
rotSlavesDown = windows $ modify' (rotSlaves' (\l -> last l : init l))
|
||||
|
||||
-- | The actual rotation, as a pure function on the window stack.
|
||||
rotSlaves' :: ([a] -> [a]) -> Stack a -> Stack a
|
||||
@@ -49,12 +50,12 @@ rotSlaves' _ s@(Stack _ [] []) = s
|
||||
rotSlaves' f (Stack t [] rs) = Stack t [] (f rs) -- Master has focus
|
||||
rotSlaves' f s@(Stack _ ls _ ) = Stack t' (reverse revls') rs' -- otherwise
|
||||
where (master:ws) = integrate s
|
||||
(revls',t':rs') = splitAt (length ls) (master:(f ws))
|
||||
(revls',t':rs') = splitAt (length ls) (master:f ws)
|
||||
|
||||
-- | Rotate all the windows in the current stack.
|
||||
rotAllUp,rotAllDown :: X ()
|
||||
rotAllUp = windows $ modify' (rotAll' (\l -> (tail l)++[head l]))
|
||||
rotAllDown = windows $ modify' (rotAll' (\l -> [last l]++(init l)))
|
||||
rotAllUp = windows $ modify' (rotAll' (\l -> tail l++[head l]))
|
||||
rotAllDown = windows $ modify' (rotAll' (\l -> last l : init l))
|
||||
|
||||
-- | The actual rotation, as a pure function on the window stack.
|
||||
rotAll' :: ([a] -> [a]) -> Stack a -> Stack a
|
||||
|
161
XMonad/Actions/RotateSome.hs
Normal file
161
XMonad/Actions/RotateSome.hs
Normal file
@@ -0,0 +1,161 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.RotateSome
|
||||
-- Description : Rotate some elements around the stack.
|
||||
-- Copyright : (c) 2020 Ivan Brennan <ivanbrennan@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Ivan Brennan <ivanbrennan@gmail.com>
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Functions for rotating some elements around the stack while keeping others
|
||||
-- anchored in place. Useful in combination with layouts that dictate window
|
||||
-- visibility based on stack position, such as "XMonad.Layout.LimitWindows".
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.RotateSome (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
-- * Example
|
||||
-- $example
|
||||
surfaceNext,
|
||||
surfacePrev,
|
||||
rotateSome,
|
||||
) where
|
||||
|
||||
import Control.Arrow ((***))
|
||||
import XMonad.Prelude (partition, sortOn, (\\))
|
||||
import qualified Data.Map as M
|
||||
import XMonad (Window, WindowSpace, Rectangle, X, runLayout, screenRect, windows, withWindowSet)
|
||||
import XMonad.StackSet (Screen (Screen), Stack (Stack), current, floating, modify', stack)
|
||||
import XMonad.Util.Stack (reverseS)
|
||||
|
||||
{- $usage
|
||||
You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
|
||||
> import XMonad.Actions.RotateSome
|
||||
|
||||
and add keybindings such as the following:
|
||||
|
||||
> , ((modMask .|. controlMask, xK_n), surfaceNext)
|
||||
> , ((modMask .|. controlMask, xK_p), surfacePrev)
|
||||
|
||||
-}
|
||||
|
||||
{- $example
|
||||
#Example#
|
||||
|
||||
Consider a workspace whose stack contains five windows A B C D E but whose
|
||||
layout limits how many will actually be shown, showing only the first plus
|
||||
two additional windows, starting with the third:
|
||||
|
||||
> ┌─────┬─────┐
|
||||
> │ │ C │
|
||||
> │ A ├─────┤
|
||||
> │ │ D │
|
||||
> └─────┴─────┘
|
||||
>
|
||||
> A B C D E
|
||||
> _ ____
|
||||
|
||||
If C has focus and we'd like to replace it with one of the unshown windows,
|
||||
'surfaceNext' will move the next unshown window, E, into the focused position:
|
||||
|
||||
> ┌─────┬─────┐ ┌─────┬─────┐
|
||||
> │ │ *C* │ │ │ *E* │
|
||||
> │ A ├─────┤ surfaceNext -> │ A ├─────┤
|
||||
> │ │ D │ │ │ D │
|
||||
> └─────┴─────┘ └─────┴─────┘
|
||||
>
|
||||
> A B *C* D E A C *E* D B
|
||||
> _ ____ _ ____
|
||||
|
||||
This repositioned windows B C E by treating them as a sequence that can be
|
||||
rotated through the focused stack position. Windows A and D remain anchored
|
||||
to their original (visible) positions.
|
||||
|
||||
A second call to 'surfaceNext' moves B into focus:
|
||||
|
||||
> ┌─────┬─────┐ ┌─────┬─────┐
|
||||
> │ │ *E* │ │ │ *B* │
|
||||
> │ A ├─────┤ surfaceNext -> │ A ├─────┤
|
||||
> │ │ D │ │ │ D │
|
||||
> └─────┴─────┘ └─────┴─────┘
|
||||
>
|
||||
> A C *E* D B A E *B* D C
|
||||
> _ ____ _ ____
|
||||
|
||||
A third call would complete the cycle, bringing C back into focus.
|
||||
|
||||
-}
|
||||
|
||||
-- |
|
||||
-- Treating the focused window and any unshown windows as a ring that can be
|
||||
-- rotated through the focused position, surface the next element in the ring.
|
||||
surfaceNext :: X ()
|
||||
surfaceNext = do
|
||||
ring <- surfaceRing
|
||||
windows . modify' $ rotateSome (`elem` ring)
|
||||
|
||||
-- | Like 'surfaceNext' in reverse.
|
||||
surfacePrev :: X ()
|
||||
surfacePrev = do
|
||||
ring <- surfaceRing
|
||||
windows . modify' $ reverseS . rotateSome (`elem` ring) . reverseS
|
||||
|
||||
-- |
|
||||
-- Return a list containing the current focus plus any unshown windows. Note
|
||||
-- that windows are shown if 'runLayout' provides them with a rectangle or if
|
||||
-- they are floating.
|
||||
surfaceRing :: X [Window]
|
||||
surfaceRing = withWindowSet $ \wset -> do
|
||||
let Screen wsp _ sd = current wset
|
||||
|
||||
case stack wsp >>= filter' (`M.notMember` floating wset) of
|
||||
Nothing -> pure []
|
||||
Just st -> go st <$> layoutWindows wsp {stack = Just st} (screenRect sd)
|
||||
where
|
||||
go :: Stack Window -> [Window] -> [Window]
|
||||
go (Stack t ls rs) shown = t : ((ls ++ rs) \\ shown)
|
||||
|
||||
layoutWindows :: WindowSpace -> Rectangle -> X [Window]
|
||||
layoutWindows wsp rect = map fst . fst <$> runLayout wsp rect
|
||||
|
||||
-- | Like "XMonad.StackSet.filter" but won't move focus.
|
||||
filter' :: (a -> Bool) -> Stack a -> Maybe (Stack a)
|
||||
filter' p (Stack f ls rs)
|
||||
| p f = Just $ Stack f (filter p ls) (filter p rs)
|
||||
| otherwise = Nothing
|
||||
|
||||
-- |
|
||||
-- @'rotateSome' p stack@ treats the elements of @stack@ that satisfy predicate
|
||||
-- @p@ as a ring that can be rotated, while all other elements remain anchored
|
||||
-- in place.
|
||||
rotateSome :: (a -> Bool) -> Stack a -> Stack a
|
||||
rotateSome p (Stack t ls rs) =
|
||||
let
|
||||
-- Flatten the stack, index each element relative to the focused position,
|
||||
-- then partition into movable and anchored elements.
|
||||
(movables, anchors) =
|
||||
partition (p . snd) $
|
||||
zip
|
||||
[negate (length ls)..]
|
||||
(reverse ls ++ t : rs)
|
||||
|
||||
-- Pair each movable element with the index of its next movable neighbor.
|
||||
-- Append anchored elements, along with their unchanged indices, and sort
|
||||
-- by index. Separate lefts (negative indices) from the rest, and grab the
|
||||
-- new focus from the head of the remaining elements.
|
||||
(ls', t':rs') =
|
||||
(map snd *** map snd)
|
||||
. span ((< 0) . fst)
|
||||
. sortOn fst
|
||||
. (++) anchors
|
||||
$ zipWith (curry (fst *** snd)) movables (rotate movables)
|
||||
in
|
||||
Stack t' (reverse ls') rs'
|
||||
|
||||
rotate :: [a] -> [a]
|
||||
rotate = uncurry (flip (++)) . splitAt 1
|
@@ -1,4 +1,6 @@
|
||||
{- | Module : XMonad.Actions.Search
|
||||
{- |
|
||||
Module : XMonad.Actions.Search
|
||||
Description : Easily run Internet searches on web sites through xmonad.
|
||||
Copyright : (C) 2007 Gwern Branwen
|
||||
License : None; public domain
|
||||
|
||||
@@ -18,6 +20,7 @@ module XMonad.Actions.Search ( -- * Usage
|
||||
searchEngineF,
|
||||
promptSearch,
|
||||
promptSearchBrowser,
|
||||
promptSearchBrowser',
|
||||
selectSearch,
|
||||
selectSearchBrowser,
|
||||
isPrefixOf,
|
||||
@@ -35,12 +38,13 @@ module XMonad.Actions.Search ( -- * Usage
|
||||
debbts,
|
||||
debpts,
|
||||
dictionary,
|
||||
ebay,
|
||||
github,
|
||||
google,
|
||||
hackage,
|
||||
hoogle,
|
||||
images,
|
||||
imdb,
|
||||
isohunt,
|
||||
lucky,
|
||||
maps,
|
||||
mathworld,
|
||||
@@ -63,13 +67,12 @@ module XMonad.Actions.Search ( -- * Usage
|
||||
) where
|
||||
|
||||
import Codec.Binary.UTF8.String (encode)
|
||||
import Data.Char (isAlphaNum, isAscii)
|
||||
import Data.List (isPrefixOf)
|
||||
import Text.Printf
|
||||
import XMonad (X (), liftIO)
|
||||
import XMonad.Prompt (XPConfig (), XPrompt (showXPrompt, nextCompletion, commandToComplete),
|
||||
getNextCompletion,
|
||||
historyCompletionP, mkXPrompt)
|
||||
import XMonad.Prelude (isAlphaNum, isAscii, isPrefixOf)
|
||||
import XMonad.Prompt.Shell (getBrowser)
|
||||
import XMonad.Util.Run (safeSpawn)
|
||||
import XMonad.Util.XSelection (getSelection)
|
||||
@@ -113,6 +116,10 @@ import XMonad.Util.XSelection (getSelection)
|
||||
|
||||
* 'dictionary' -- dictionary.reference.com search.
|
||||
|
||||
* 'ebay' -- Ebay keyword search.
|
||||
|
||||
* 'github' -- GitHub keyword search.
|
||||
|
||||
* 'google' -- basic Google search.
|
||||
|
||||
* 'hackage' -- Hackage, the Haskell package database.
|
||||
@@ -125,8 +132,6 @@ import XMonad.Util.XSelection (getSelection)
|
||||
|
||||
* 'imdb' -- the Internet Movie Database.
|
||||
|
||||
* 'isohunt' -- isoHunt search.
|
||||
|
||||
* 'lucky' -- Google "I'm feeling lucky" search.
|
||||
|
||||
* 'maps' -- Google maps.
|
||||
@@ -137,7 +142,7 @@ import XMonad.Util.XSelection (getSelection)
|
||||
|
||||
* 'scholar' -- Google scholar academic search.
|
||||
|
||||
* 'thesaurus' -- thesaurus.reference.com search.
|
||||
* 'thesaurus' -- thesaurus.com search.
|
||||
|
||||
* 'wayback' -- the Wayback Machine.
|
||||
|
||||
@@ -191,7 +196,7 @@ Or in combination with XMonad.Util.EZConfig:
|
||||
>
|
||||
> searchList :: [(String, S.SearchEngine)]
|
||||
> searchList = [ ("g", S.google)
|
||||
> , ("h", S.hoohle)
|
||||
> , ("h", S.hoogle)
|
||||
> , ("w", S.wikipedia)
|
||||
> ]
|
||||
|
||||
@@ -210,7 +215,7 @@ engine.
|
||||
Happy searching! -}
|
||||
|
||||
-- | A customized prompt indicating we are searching, and the name of the site.
|
||||
data Search = Search Name
|
||||
newtype Search = Search Name
|
||||
instance XPrompt Search where
|
||||
showXPrompt (Search name)= "Search [" ++ name ++ "]: "
|
||||
nextCompletion _ = getNextCompletion
|
||||
@@ -248,7 +253,7 @@ search browser site query = safeSpawn browser [site query]
|
||||
appends it to the base. You can easily define a new engine locally using
|
||||
exported functions without needing to modify "XMonad.Actions.Search":
|
||||
|
||||
> myNewEngine = searchEngine "site" "http://site.com/search="
|
||||
> myNewEngine = searchEngine "site" "https://site.com/search="
|
||||
|
||||
The important thing is that the site has a interface which accepts the escaped query
|
||||
string as part of the URL. Alas, the exact URL to feed searchEngine varies
|
||||
@@ -257,21 +262,21 @@ search browser site query = safeSpawn browser [site query]
|
||||
Generally, examining the resultant URL of a search will allow you to reverse-engineer
|
||||
it if you can't find the necessary URL already described in other projects such as Surfraw. -}
|
||||
searchEngine :: Name -> String -> SearchEngine
|
||||
searchEngine name site = searchEngineF name (\s -> site ++ (escape s))
|
||||
searchEngine name site = searchEngineF name (\s -> site ++ escape s)
|
||||
|
||||
{- | If your search engine is more complex than this (you may want to identify
|
||||
the kind of input and make the search URL dependent on the input or put the query
|
||||
inside of a URL instead of in the end) you can use the alternative 'searchEngineF' function.
|
||||
|
||||
> searchFunc :: String -> String
|
||||
> searchFunc s | "wiki:" `isPrefixOf` s = "http://en.wikipedia.org/wiki/" ++ (escape $ tail $ snd $ break (==':') s)
|
||||
> | "http://" `isPrefixOf` s = s
|
||||
> | otherwise = (use google) s
|
||||
> searchFunc s | "wiki:" `isPrefixOf` s = "https://en.wikipedia.org/wiki/" ++ (escape $ tail $ snd $ break (==':') s)
|
||||
> | "https://" `isPrefixOf` s = s
|
||||
> | otherwise = (use google) s
|
||||
> myNewEngine = searchEngineF "mymulti" searchFunc
|
||||
|
||||
@searchFunc@ here searches for a word in wikipedia if it has a prefix
|
||||
of \"wiki:\" (you can use the 'escape' function to escape any forbidden characters), opens an address
|
||||
directly if it starts with \"http:\/\/\" and otherwise uses the provided google search engine.
|
||||
directly if it starts with \"https:\/\/\" and otherwise uses the provided google search engine.
|
||||
You can use other engines inside of your own through the 'use' function as shown above to make
|
||||
complex searches.
|
||||
|
||||
@@ -281,38 +286,39 @@ searchEngineF :: Name -> Site -> SearchEngine
|
||||
searchEngineF = SearchEngine
|
||||
|
||||
-- The engines.
|
||||
amazon, alpha, codesearch, deb, debbts, debpts, dictionary, google, hackage, hoogle,
|
||||
images, imdb, isohunt, lucky, maps, mathworld, openstreetmap, scholar, stackage, thesaurus, vocabulary, wayback, wikipedia, wiktionary,
|
||||
amazon, alpha, codesearch, deb, debbts, debpts, dictionary, ebay, github, google, hackage, hoogle,
|
||||
images, imdb, lucky, maps, mathworld, openstreetmap, scholar, stackage, thesaurus, vocabulary, wayback, wikipedia, wiktionary,
|
||||
youtube, duckduckgo :: SearchEngine
|
||||
amazon = searchEngine "amazon" "http://www.amazon.com/s/ref=nb_sb_noss_2?url=search-alias%3Daps&field-keywords="
|
||||
alpha = searchEngine "alpha" "http://www.wolframalpha.com/input/?i="
|
||||
codesearch = searchEngine "codesearch" "http://www.google.com/codesearch?q="
|
||||
deb = searchEngine "deb" "http://packages.debian.org/"
|
||||
debbts = searchEngine "debbts" "http://bugs.debian.org/"
|
||||
debpts = searchEngine "debpts" "http://packages.qa.debian.org/"
|
||||
dictionary = searchEngine "dict" "http://dictionary.reference.com/browse/"
|
||||
google = searchEngine "google" "http://www.google.com/search?num=100&q="
|
||||
hackage = searchEngine "hackage" "http://hackage.haskell.org/package/"
|
||||
hoogle = searchEngine "hoogle" "http://www.haskell.org/hoogle/?q="
|
||||
images = searchEngine "images" "http://images.google.fr/images?q="
|
||||
imdb = searchEngine "imdb" "http://www.imdb.com/find?s=all&q="
|
||||
isohunt = searchEngine "isohunt" "http://isohunt.com/torrents/?ihq="
|
||||
lucky = searchEngine "lucky" "http://www.google.com/search?btnI&q="
|
||||
maps = searchEngine "maps" "http://maps.google.com/maps?q="
|
||||
mathworld = searchEngine "mathworld" "http://mathworld.wolfram.com/search/?query="
|
||||
openstreetmap = searchEngine "openstreetmap" "http://gazetteer.openstreetmap.org/namefinder/?find="
|
||||
scholar = searchEngine "scholar" "http://scholar.google.com/scholar?q="
|
||||
stackage = searchEngine "stackage" "www.stackage.org/lts/hoogle?q="
|
||||
thesaurus = searchEngine "thesaurus" "http://thesaurus.reference.com/search?q="
|
||||
wikipedia = searchEngine "wiki" "http://en.wikipedia.org/wiki/Special:Search?go=Go&search="
|
||||
wiktionary = searchEngine "wikt" "http://en.wiktionary.org/wiki/Special:Search?go=Go&search="
|
||||
youtube = searchEngine "youtube" "http://www.youtube.com/results?search_type=search_videos&search_query="
|
||||
wayback = searchEngineF "wayback" ("http://web.archive.org/web/*/"++)
|
||||
vocabulary = searchEngine "vocabulary" "http://www.vocabulary.com/search?q="
|
||||
amazon = searchEngine "amazon" "https://www.amazon.com/s/ref=nb_sb_noss_2?url=search-alias%3Daps&field-keywords="
|
||||
alpha = searchEngine "alpha" "https://www.wolframalpha.com/input/?i="
|
||||
codesearch = searchEngine "codesearch" "https://developers.google.com/s/results/code-search?q="
|
||||
deb = searchEngine "deb" "https://packages.debian.org/"
|
||||
debbts = searchEngine "debbts" "https://bugs.debian.org/"
|
||||
debpts = searchEngine "debpts" "https://packages.qa.debian.org/"
|
||||
dictionary = searchEngine "dict" "https://dictionary.reference.com/browse/"
|
||||
ebay = searchEngine "ebay" "https://www.ebay.com/sch/i.html?_nkw="
|
||||
github = searchEngine "github" "https://github.com/search?q="
|
||||
google = searchEngine "google" "https://www.google.com/search?num=100&q="
|
||||
hackage = searchEngine "hackage" "https://hackage.haskell.org/package/"
|
||||
hoogle = searchEngine "hoogle" "https://hoogle.haskell.org/?hoogle="
|
||||
images = searchEngine "images" "https://images.google.fr/images?q="
|
||||
imdb = searchEngine "imdb" "https://www.imdb.com/find?s=all&q="
|
||||
lucky = searchEngine "lucky" "https://www.google.com/search?btnI&q="
|
||||
maps = searchEngine "maps" "https://maps.google.com/maps?q="
|
||||
mathworld = searchEngine "mathworld" "https://mathworld.wolfram.com/search/?query="
|
||||
openstreetmap = searchEngine "openstreetmap" "https://www.openstreetmap.org/search?query="
|
||||
scholar = searchEngine "scholar" "https://scholar.google.com/scholar?q="
|
||||
stackage = searchEngine "stackage" "https://www.stackage.org/lts/hoogle?q="
|
||||
thesaurus = searchEngine "thesaurus" "https://thesaurus.com/browse/"
|
||||
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="
|
||||
youtube = searchEngine "youtube" "https://www.youtube.com/results?search_type=search_videos&search_query="
|
||||
wayback = searchEngineF "wayback" ("https://web.archive.org/web/*/"++)
|
||||
vocabulary = searchEngine "vocabulary" "https://www.vocabulary.com/search?q="
|
||||
duckduckgo = searchEngine "duckduckgo" "https://duckduckgo.com/?t=lm&q="
|
||||
|
||||
multi :: SearchEngine
|
||||
multi = namedEngine "multi" $ foldr1 (!>) [amazon, alpha, codesearch, deb, debbts, debpts, dictionary, google, hackage, hoogle, images, imdb, isohunt, lucky, maps, mathworld, openstreetmap, scholar, thesaurus, wayback, wikipedia, wiktionary, duckduckgo, (prefixAware google)]
|
||||
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]
|
||||
|
||||
{- | 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
|
||||
@@ -320,9 +326,9 @@ multi = namedEngine "multi" $ foldr1 (!>) [amazon, alpha, codesearch, deb, debbt
|
||||
|
||||
> myIntelligentGoogleEngine = intelligent google
|
||||
|
||||
Now if you search for http:\/\/xmonad.org it will directly open in your browser-}
|
||||
Now if you search for https:\/\/xmonad.org it will directly open in your browser-}
|
||||
intelligent :: SearchEngine -> SearchEngine
|
||||
intelligent (SearchEngine name site) = searchEngineF name (\s -> if (fst $ break (==':') s) `elem` ["http", "https", "ftp"] then s else (site s))
|
||||
intelligent (SearchEngine name site) = searchEngineF name (\s -> if takeWhile (/= ':') s `elem` ["http", "https", "ftp"] then s else site s)
|
||||
|
||||
-- | > removeColonPrefix "foo://bar" ~> "//bar"
|
||||
-- > removeColonPrefix "foo//bar" ~> "foo//bar"
|
||||
@@ -342,6 +348,7 @@ removeColonPrefix s = if ':' `elem` s then drop 1 $ dropWhile (':' /=) s else s
|
||||
google. The use of intelligent will make sure that URLs are opened directly. -}
|
||||
(!>) :: SearchEngine -> SearchEngine -> SearchEngine
|
||||
(SearchEngine name1 site1) !> (SearchEngine name2 site2) = searchEngineF (name1 ++ "/" ++ name2) (\s -> if (name1++":") `isPrefixOf` s then site1 (removeColonPrefix s) else site2 s)
|
||||
infixr 6 !>
|
||||
|
||||
{- | Makes a search engine prefix-aware. Especially useful together with '!>'.
|
||||
It will automatically remove the prefix from a query so that you don\'t end
|
||||
@@ -358,8 +365,18 @@ namedEngine name (SearchEngine _ site) = searchEngineF name site
|
||||
Prompt's result, passes it to a given searchEngine and opens it in a given
|
||||
browser. -}
|
||||
promptSearchBrowser :: XPConfig -> Browser -> SearchEngine -> X ()
|
||||
promptSearchBrowser config browser (SearchEngine name site) =
|
||||
mkXPrompt (Search name) config (historyCompletionP ("Search [" `isPrefixOf`)) $ search browser site
|
||||
promptSearchBrowser config browser (SearchEngine name site) = do
|
||||
hc <- historyCompletionP ("Search [" `isPrefixOf`)
|
||||
mkXPrompt (Search name) config hc $ search browser site
|
||||
|
||||
{- | Like 'promptSearchBrowser', but only suggest previous searches for the
|
||||
given 'SearchEngine' in the prompt. -}
|
||||
promptSearchBrowser' :: XPConfig -> Browser -> SearchEngine -> X ()
|
||||
promptSearchBrowser' config browser (SearchEngine name site) = do
|
||||
hc <- historyCompletionP (searchName `isPrefixOf`)
|
||||
mkXPrompt (Search name) config hc $ search browser site
|
||||
where
|
||||
searchName = showXPrompt (Search name)
|
||||
|
||||
{- | Like 'search', but in this case, the string is not specified but grabbed
|
||||
from the user's response to a prompt. Example:
|
||||
|
@@ -1,7 +1,8 @@
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
{-# LANGUAGE CPP #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.ShowText
|
||||
-- Description : Display text on the screen.
|
||||
-- Copyright : (c) Mario Pastorelli (2012)
|
||||
-- License : BSD-style (see xmonad/LICENSE)
|
||||
--
|
||||
@@ -17,17 +18,15 @@ module XMonad.Actions.ShowText
|
||||
( -- * Usage
|
||||
-- $usage
|
||||
def
|
||||
, defaultSTConfig
|
||||
, handleTimerEvent
|
||||
, flashText
|
||||
, ShowTextConfig(..)
|
||||
) where
|
||||
|
||||
import Control.Monad (when)
|
||||
import Data.Map (Map,empty,insert,lookup)
|
||||
import Data.Monoid (mempty, All)
|
||||
import Prelude hiding (lookup)
|
||||
import XMonad
|
||||
import XMonad.Prelude (All, fi, when)
|
||||
import XMonad.StackSet (current,screen)
|
||||
import XMonad.Util.Font (Align(AlignCenter)
|
||||
, initXMF
|
||||
@@ -37,7 +36,6 @@ import XMonad.Util.Font (Align(AlignCenter)
|
||||
import XMonad.Util.Timer (startTimer)
|
||||
import XMonad.Util.XUtils (createNewWindow
|
||||
, deleteWindow
|
||||
, fi
|
||||
, showWindow
|
||||
, paintAndWrite)
|
||||
import qualified XMonad.Util.ExtensibleState as ES
|
||||
@@ -58,7 +56,7 @@ import qualified XMonad.Util.ExtensibleState as ES
|
||||
|
||||
-- | ShowText contains the map with timers as keys and created windows as values
|
||||
newtype ShowText = ShowText (Map Atom Window)
|
||||
deriving (Read,Show,Typeable)
|
||||
deriving (Read,Show)
|
||||
|
||||
instance ExtensionClass ShowText where
|
||||
initialValue = ShowText empty
|
||||
@@ -75,22 +73,22 @@ data ShowTextConfig =
|
||||
|
||||
instance Default ShowTextConfig where
|
||||
def =
|
||||
#ifdef XFT
|
||||
STC { st_font = "xft:monospace-20"
|
||||
#else
|
||||
STC { st_font = "-misc-fixed-*-*-*-*-20-*-*-*-*-*-*-*"
|
||||
#endif
|
||||
, st_bg = "black"
|
||||
, st_fg = "white"
|
||||
}
|
||||
|
||||
{-# DEPRECATED defaultSTConfig "Use def (from Data.Default, and re-exported by XMonad.Actions.ShowText) instead." #-}
|
||||
defaultSTConfig :: ShowTextConfig
|
||||
defaultSTConfig = def
|
||||
|
||||
-- | Handles timer events that notify when a window should be removed
|
||||
handleTimerEvent :: Event -> X All
|
||||
handleTimerEvent (ClientMessageEvent _ _ _ dis _ mtyp d) = do
|
||||
(ShowText m) <- ES.get :: X ShowText
|
||||
a <- io $ internAtom dis "XMONAD_TIMER" False
|
||||
when (mtyp == a && length d >= 1)
|
||||
(whenJust (lookup (fromIntegral $ d !! 0) m) deleteWindow)
|
||||
when (mtyp == a && not (null d))
|
||||
(whenJust (lookup (fromIntegral $ head d) m) deleteWindow)
|
||||
mempty
|
||||
handleTimerEvent _ = mempty
|
||||
|
||||
|
56
XMonad/Actions/Sift.hs
Normal file
56
XMonad/Actions/Sift.hs
Normal file
@@ -0,0 +1,56 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.Sift
|
||||
-- Description : Functions for sifting windows up and down.
|
||||
-- Copyright : (c) 2020 Ivan Brennan <ivanbrennan@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Ivan Brennan <ivanbrennan@gmail.com>
|
||||
-- Stability : stable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Functions for sifting windows up and down. Sifts behave identically to
|
||||
-- swaps (i.e. 'swapUp' and 'swapDown' from "XMonad.StackSet"), except in
|
||||
-- the wrapping case: rather than rotating the entire stack by one position
|
||||
-- like a swap would, a sift causes the windows at either end of the stack
|
||||
-- to trade positions.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.Sift (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
siftUp,
|
||||
siftDown,
|
||||
) where
|
||||
|
||||
import XMonad.StackSet (Stack (Stack), StackSet, modify')
|
||||
import XMonad.Util.Stack (reverseS)
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.Sift
|
||||
--
|
||||
-- and add keybindings such as the following:
|
||||
--
|
||||
-- > , ((modMask .|. shiftMask, xK_j ), windows siftDown)
|
||||
-- > , ((modMask .|. shiftMask, xK_k ), windows siftUp )
|
||||
--
|
||||
|
||||
-- |
|
||||
-- siftUp, siftDown. Exchange the focused window with its neighbour in
|
||||
-- the stack ordering, wrapping if we reach the end. Unlike 'swapUp' and
|
||||
-- 'swapDown', wrapping is handled by trading positions with the window
|
||||
-- at the other end of the stack.
|
||||
--
|
||||
siftUp, siftDown :: StackSet i l a s sd -> StackSet i l a s sd
|
||||
siftUp = modify' siftUp'
|
||||
siftDown = modify' (reverseS . siftUp' . reverseS)
|
||||
|
||||
siftUp' :: Stack a -> Stack a
|
||||
siftUp' (Stack t (l:ls) rs) = Stack t ls (l:rs)
|
||||
siftUp' (Stack t [] rs) =
|
||||
case reverse rs of
|
||||
(x:xs) -> Stack t (xs ++ [x]) []
|
||||
[] -> Stack t [] []
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.SimpleDate
|
||||
-- Description : An example external contrib module for XMonad.
|
||||
-- Copyright : (c) Don Stewart 2007
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.SinkAll
|
||||
-- Description : (DEPRECATED) Push floating windows back into tiling.
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.SpawnOn
|
||||
-- Description : Modify a window spawned by a command.
|
||||
-- Copyright : (c) Spencer Janssen
|
||||
-- License : BSD
|
||||
--
|
||||
@@ -29,15 +29,13 @@ module XMonad.Actions.SpawnOn (
|
||||
) where
|
||||
|
||||
import Control.Exception (tryJust)
|
||||
import Control.Monad (guard)
|
||||
import Data.List (isInfixOf)
|
||||
import Data.Maybe (isJust)
|
||||
import System.IO.Error (isDoesNotExistError)
|
||||
import System.IO.Unsafe (unsafePerformIO)
|
||||
import System.Posix.Types (ProcessID)
|
||||
import Text.Printf (printf)
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import XMonad.Hooks.ManageHelpers
|
||||
@@ -68,15 +66,15 @@ import qualified XMonad.Util.ExtensibleState as XS
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
newtype Spawner = Spawner {pidsRef :: [(ProcessID, ManageHook)]} deriving Typeable
|
||||
newtype Spawner = Spawner {pidsRef :: [(ProcessID, ManageHook)]}
|
||||
|
||||
instance ExtensionClass Spawner where
|
||||
initialValue = Spawner []
|
||||
|
||||
|
||||
getPPIDOf :: ProcessID -> Maybe ProcessID
|
||||
getPPIDOf pid =
|
||||
case unsafePerformIO . tryJust (guard . isDoesNotExistError) . readFile . printf "/proc/%d/stat" $ toInteger pid of
|
||||
getPPIDOf thisPid =
|
||||
case unsafePerformIO . tryJust (guard . isDoesNotExistError) . readFile . printf "/proc/%d/stat" $ toInteger thisPid of
|
||||
Left _ -> Nothing
|
||||
Right contents -> case lines contents of
|
||||
[] -> Nothing
|
||||
@@ -85,11 +83,11 @@ getPPIDOf pid =
|
||||
_ -> Nothing
|
||||
|
||||
getPPIDChain :: ProcessID -> [ProcessID]
|
||||
getPPIDChain pid' = ppid_chain pid' []
|
||||
where ppid_chain pid acc =
|
||||
if pid == 0
|
||||
getPPIDChain thisPid = ppid_chain thisPid []
|
||||
where ppid_chain pid' acc =
|
||||
if pid' == 0
|
||||
then acc
|
||||
else case getPPIDOf pid of
|
||||
else case getPPIDOf pid' of
|
||||
Nothing -> acc
|
||||
Just ppid -> ppid_chain ppid (ppid : acc)
|
||||
|
||||
@@ -126,7 +124,7 @@ manageSpawnWithGC garbageCollect = do
|
||||
|
||||
mkPrompt :: (String -> X ()) -> XPConfig -> X ()
|
||||
mkPrompt cb c = do
|
||||
cmds <- io $ getCommands
|
||||
cmds <- io getCommands
|
||||
mkXPrompt Shell c (getShellCompl cmds $ searchPredicate c) cb
|
||||
|
||||
-- | Replacement for Shell prompt ("XMonad.Prompt.Shell") which launches
|
||||
@@ -147,13 +145,13 @@ spawnHere cmd = withWindowSet $ \ws -> spawnOn (W.currentTag ws) cmd
|
||||
-- | Replacement for 'spawn' which launches
|
||||
-- application on given workspace.
|
||||
spawnOn :: WorkspaceId -> String -> X ()
|
||||
spawnOn ws cmd = spawnAndDo (doShift ws) cmd
|
||||
spawnOn ws = spawnAndDo (doShift ws)
|
||||
|
||||
-- | Spawn an application and apply the manage hook when it opens.
|
||||
spawnAndDo :: ManageHook -> String -> X ()
|
||||
spawnAndDo mh cmd = do
|
||||
p <- spawnPID $ mangle cmd
|
||||
modifySpawner $ ((p,mh) :)
|
||||
modifySpawner ((p,mh) :)
|
||||
where
|
||||
-- TODO this is silly, search for a better solution
|
||||
mangle xs | any (`elem` metaChars) xs || "exec" `isInfixOf` xs = xs
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.Submap
|
||||
-- Description : Create a sub-mapping of key bindings.
|
||||
-- Copyright : (c) Jason Creighton <jcreigh@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -20,10 +21,9 @@ module XMonad.Actions.Submap (
|
||||
submapDefaultWithKey
|
||||
) where
|
||||
import Data.Bits
|
||||
import Data.Maybe (fromMaybe)
|
||||
import XMonad.Prelude (fix, fromMaybe)
|
||||
import XMonad hiding (keys)
|
||||
import qualified Data.Map as M
|
||||
import Control.Monad.Fix (fix)
|
||||
|
||||
{- $usage
|
||||
|
||||
@@ -93,5 +93,6 @@ submapDefaultWithKey defAction keys = do
|
||||
|
||||
io $ ungrabPointer d currentTime
|
||||
io $ ungrabKeyboard d currentTime
|
||||
io $ sync d False
|
||||
|
||||
fromMaybe (defAction (m', s)) (M.lookup (m', s) keys)
|
||||
|
@@ -1,8 +1,7 @@
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.SwapPromote
|
||||
-- Description : Track the master window history per workspace.
|
||||
-- Copyright : (c) 2018 Yclept Nemo
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
@@ -57,16 +56,13 @@ module XMonad.Actions.SwapPromote
|
||||
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude
|
||||
import qualified XMonad.StackSet as W
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
|
||||
import qualified Data.Map as M
|
||||
import qualified Data.Set as S
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Control.Arrow
|
||||
import Control.Applicative ((<$>),(<*>))
|
||||
import Control.Monad
|
||||
|
||||
|
||||
-- $usage
|
||||
@@ -118,7 +114,7 @@ import Control.Monad
|
||||
-- Without history, the list is empty.
|
||||
newtype MasterHistory = MasterHistory
|
||||
{ getMasterHistory :: M.Map WorkspaceId [Window]
|
||||
} deriving (Read,Show,Typeable)
|
||||
} deriving (Read,Show)
|
||||
|
||||
instance ExtensionClass MasterHistory where
|
||||
initialValue = MasterHistory M.empty
|
||||
@@ -341,7 +337,7 @@ split' p i l =
|
||||
then (c+1,e:ys,ns)
|
||||
else (c+1,ys,e:ns)
|
||||
(c',ys',ns') = foldr accumulate (0,[],[]) $ zip [i..] l
|
||||
in (c',ys',snd . unzip $ ns')
|
||||
in (c',ys',map snd ns')
|
||||
|
||||
-- | Wrap 'merge'' with an initial virtual index of @0@. Return only the
|
||||
-- unindexed list with elements from the leftover indexed list appended.
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.SwapWorkspaces
|
||||
-- Description : Swap workspace tags without having to move individual windows.
|
||||
-- Copyright : (c) Devin Mullins <me@twifkak.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -53,12 +54,13 @@ swapWithCurrent t s = swapWorkspaces t (currentTag s) s
|
||||
-- | Say @swapTo Next@ or @swapTo Prev@ to move your current workspace.
|
||||
-- This is an @X ()@ so can be hooked up to your keybindings directly.
|
||||
swapTo :: Direction1D -> X ()
|
||||
swapTo dir = findWorkspace getSortByIndex dir AnyWS 1 >>= windows . swapWithCurrent
|
||||
swapTo dir = findWorkspace getSortByIndex dir anyWS 1 >>= windows . swapWithCurrent
|
||||
|
||||
-- | Takes two workspace tags and an existing XMonad.StackSet and returns a new
|
||||
-- one with the two corresponding workspaces' tags swapped.
|
||||
swapWorkspaces :: Eq i => i -> i -> StackSet i l a s sd -> StackSet i l a s sd
|
||||
swapWorkspaces t1 t2 = mapWorkspace swap
|
||||
where swap w = if tag w == t1 then w { tag = t2 }
|
||||
else if tag w == t2 then w { tag = t1 }
|
||||
else w
|
||||
where swap w
|
||||
| tag w == t1 = w { tag = t2 }
|
||||
| tag w == t2 = w { tag = t1 }
|
||||
| otherwise = w
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.TagWindows
|
||||
-- Description : Functions for tagging windows and selecting them by tags.
|
||||
-- Copyright : (c) Karsten Schoelzel <kuser@gmx.de>
|
||||
-- License : BSD
|
||||
--
|
||||
@@ -26,14 +27,12 @@ module XMonad.Actions.TagWindows (
|
||||
TagPrompt,
|
||||
) where
|
||||
|
||||
import Data.List (nub,sortBy)
|
||||
import Control.Monad
|
||||
import Control.Exception as E
|
||||
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
import XMonad.Prompt
|
||||
import XMonad hiding (workspaces)
|
||||
import XMonad.Prelude
|
||||
import XMonad.Prompt
|
||||
import XMonad.StackSet hiding (filter)
|
||||
|
||||
econst :: Monad m => a -> IOException -> m a
|
||||
econst = const . return
|
||||
@@ -84,18 +83,17 @@ getTags w = withDisplay $ \d ->
|
||||
io $ E.catch (internAtom d "_XMONAD_TAGS" False >>=
|
||||
getTextProperty d w >>=
|
||||
wcTextPropertyToTextList d)
|
||||
(econst [[]])
|
||||
>>= return . words . unwords
|
||||
(econst [[]]) <&> (words . unwords)
|
||||
|
||||
-- | check a window for the given tag
|
||||
hasTag :: String -> Window -> X Bool
|
||||
hasTag s w = (s `elem`) `fmap` getTags w
|
||||
hasTag s w = (s `elem`) <$> getTags w
|
||||
|
||||
-- | add a tag to the existing ones
|
||||
addTag :: String -> Window -> X ()
|
||||
addTag s w = do
|
||||
tags <- getTags w
|
||||
if (s `notElem` tags) then setTags (s:tags) w else return ()
|
||||
when (s `notElem` tags) $ setTags (s:tags) w
|
||||
|
||||
-- | remove a tag from a window, if it exists
|
||||
delTag :: String -> Window -> X ()
|
||||
@@ -158,7 +156,7 @@ withTagged' t m = gets windowset >>= filterM (hasTag t) . index >>= m
|
||||
|
||||
withTaggedGlobal' :: String -> ([Window] -> X ()) -> X ()
|
||||
withTaggedGlobal' t m = gets windowset >>=
|
||||
filterM (hasTag t) . concat . map (integrate' . stack) . workspaces >>= m
|
||||
filterM (hasTag t) . concatMap (integrate' . stack) . workspaces >>= m
|
||||
|
||||
withFocusedP :: (Window -> WindowSet -> WindowSet) -> X ()
|
||||
withFocusedP f = withFocused $ windows . f
|
||||
@@ -167,7 +165,7 @@ shiftHere :: (Ord a, Eq s, Eq i) => a -> StackSet i l a s sd -> StackSet i l a s
|
||||
shiftHere w s = shiftWin (currentTag s) w s
|
||||
|
||||
shiftToScreen :: (Ord a, Eq s, Eq i) => s -> a -> StackSet i l a s sd -> StackSet i l a s sd
|
||||
shiftToScreen sid w s = case filter (\m -> sid /= screen m) ((current s):(visible s)) of
|
||||
shiftToScreen sid w s = case filter (\m -> sid /= screen m) (current s:visible s) of
|
||||
[] -> s
|
||||
(t:_) -> shiftWin (tag . workspace $ t) w s
|
||||
|
||||
@@ -180,20 +178,19 @@ instance XPrompt TagPrompt where
|
||||
tagPrompt :: XPConfig -> (String -> X ()) -> X ()
|
||||
tagPrompt c f = do
|
||||
sc <- tagComplList
|
||||
mkXPrompt TagPrompt c (mkComplFunFromList' sc) f
|
||||
mkXPrompt TagPrompt c (mkComplFunFromList' c sc) f
|
||||
|
||||
tagComplList :: X [String]
|
||||
tagComplList = gets (concat . map (integrate' . stack) . workspaces . windowset) >>=
|
||||
mapM getTags >>=
|
||||
return . nub . concat
|
||||
tagComplList = gets (concatMap (integrate' . stack) . workspaces . windowset)
|
||||
>>= mapM getTags
|
||||
<&> nub . concat
|
||||
|
||||
|
||||
tagDelPrompt :: XPConfig -> X ()
|
||||
tagDelPrompt c = do
|
||||
sc <- tagDelComplList
|
||||
if (sc /= [])
|
||||
then mkXPrompt TagPrompt c (mkComplFunFromList' sc) (\s -> withFocused (delTag s))
|
||||
else return ()
|
||||
when (sc /= []) $
|
||||
mkXPrompt TagPrompt c (mkComplFunFromList' c sc) (withFocused . delTag)
|
||||
|
||||
tagDelComplList :: X [String]
|
||||
tagDelComplList = gets windowset >>= maybe (return []) getTags . peek
|
||||
|
94
XMonad/Actions/TiledWindowDragging.hs
Normal file
94
XMonad/Actions/TiledWindowDragging.hs
Normal file
@@ -0,0 +1,94 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.TiledWindowDragging
|
||||
-- Description : Change the position of windows by dragging them.
|
||||
-- Copyright : (c) 2020 Leon Kowarschick
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Leon Kowarschick. <thereal.elkowar@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Provides an action that allows you to change the position of windows by dragging them around.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.TiledWindowDragging
|
||||
(
|
||||
-- * Usage
|
||||
-- $usage
|
||||
dragWindow
|
||||
)
|
||||
where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Layout.DraggingVisualizer
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.TiledWindowDragging
|
||||
-- > import XMonad.Layout.DraggingVisualizer
|
||||
--
|
||||
-- then edit your 'layoutHook' by adding the draggingVisualizer to your layout:
|
||||
--
|
||||
-- > myLayout = draggingVisualizer $ layoutHook def
|
||||
--
|
||||
-- Then add a mouse binding for 'dragWindow':
|
||||
--
|
||||
-- > , ((modMask .|. shiftMask, button1), dragWindow)
|
||||
--
|
||||
-- For detailed instructions on editing your mouse bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_mouse_bindings".
|
||||
|
||||
|
||||
|
||||
-- | Create a mouse binding for this to be able to drag your windows around.
|
||||
-- You need "XMonad.Layout.DraggingVisualizer" for this to look good.
|
||||
dragWindow :: Window -> X ()
|
||||
dragWindow window = whenX (isClient window) $ do
|
||||
focus window
|
||||
(offsetX, offsetY) <- getPointerOffset window
|
||||
(winX, winY, winWidth, winHeight) <- getWindowPlacement window
|
||||
|
||||
mouseDrag
|
||||
(\posX posY ->
|
||||
let rect = Rectangle (fi (fi winX + (posX - fi offsetX)))
|
||||
(fi (fi winY + (posY - fi offsetY)))
|
||||
(fi winWidth)
|
||||
(fi winHeight)
|
||||
in sendMessage $ DraggingWindow window rect
|
||||
)
|
||||
(sendMessage DraggingStopped >> performWindowSwitching window)
|
||||
|
||||
|
||||
-- | get the pointer offset relative to the given windows root coordinates
|
||||
getPointerOffset :: Window -> X (Int, Int)
|
||||
getPointerOffset win = do
|
||||
(_, _, _, oX, oY, _, _, _) <- withDisplay (\d -> io $ queryPointer d win)
|
||||
return (fi oX, fi oY)
|
||||
|
||||
-- | return a tuple of windowX, windowY, windowWidth, windowHeight
|
||||
getWindowPlacement :: Window -> X (Int, Int, Int, Int)
|
||||
getWindowPlacement window = do
|
||||
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 win = do
|
||||
root <- asks theRoot
|
||||
(_, _, selWin, _, _, _, _, _) <- withDisplay (\d -> io $ queryPointer d root)
|
||||
ws <- gets windowset
|
||||
let allWindows = W.index ws
|
||||
when ((win `elem` allWindows) && (selWin `elem` allWindows)) $ do
|
||||
let allWindowsSwitched = map (switchEntries win selWin) allWindows
|
||||
let (ls, t : rs) = break (== win) allWindowsSwitched
|
||||
let newStack = W.Stack t (reverse ls) rs
|
||||
windows $ W.modify' $ const newStack
|
||||
where
|
||||
switchEntries a b x | x == a = b
|
||||
| x == b = a
|
||||
| otherwise = x
|
@@ -1,7 +1,8 @@
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.TopicSpace
|
||||
-- Description : Turns your workspaces into a more topic oriented system.
|
||||
-- Copyright : (c) Nicolas Pouillard
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
@@ -19,21 +20,49 @@ module XMonad.Actions.TopicSpace
|
||||
|
||||
-- * Usage
|
||||
-- $usage
|
||||
Topic
|
||||
|
||||
-- * Types for Building Topics
|
||||
Topic
|
||||
, Dir
|
||||
, TopicConfig(..)
|
||||
, def
|
||||
, defaultTopicConfig
|
||||
, getLastFocusedTopics
|
||||
, setLastFocusedTopic
|
||||
, reverseLastFocusedTopics
|
||||
, pprWindowSet
|
||||
, TopicItem(..)
|
||||
|
||||
-- * Managing 'TopicItem's
|
||||
, topicNames
|
||||
, tiActions
|
||||
, tiDirs
|
||||
, noAction
|
||||
, inHome
|
||||
|
||||
-- * Switching and Shifting Topics
|
||||
, switchTopic
|
||||
, switchNthLastFocused
|
||||
, switchNthLastFocusedByScreen
|
||||
, switchNthLastFocusedExclude
|
||||
, shiftNthLastFocused
|
||||
|
||||
-- * Topic Actions
|
||||
, topicActionWithPrompt
|
||||
, topicAction
|
||||
, currentTopicAction
|
||||
, switchTopic
|
||||
, switchNthLastFocused
|
||||
, shiftNthLastFocused
|
||||
|
||||
-- * Getting the Topic History
|
||||
, getLastFocusedTopics
|
||||
, workspaceHistory
|
||||
, workspaceHistoryByScreen
|
||||
|
||||
-- * Modifying the Topic History
|
||||
, setLastFocusedTopic
|
||||
, reverseLastFocusedTopics
|
||||
|
||||
-- * History hooks
|
||||
, workspaceHistoryHook
|
||||
, workspaceHistoryHookExclude
|
||||
|
||||
-- * Pretty Printing
|
||||
, pprWindowSet
|
||||
|
||||
-- * Utility
|
||||
, currentTopicDir
|
||||
, checkTopicConfig
|
||||
, (>*>)
|
||||
@@ -41,25 +70,26 @@ module XMonad.Actions.TopicSpace
|
||||
where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude
|
||||
|
||||
import Data.List
|
||||
import Data.Maybe (fromMaybe, isNothing, listToMaybe, fromJust)
|
||||
import Data.Ord
|
||||
import qualified Data.Map as M
|
||||
import Control.Monad (liftM2,when,unless,replicateM_)
|
||||
import System.IO
|
||||
import qualified Data.Map.Strict as M
|
||||
import qualified XMonad.Hooks.StatusBar.PP as SBPP
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import qualified XMonad.StackSet as W
|
||||
import Data.Map (Map)
|
||||
|
||||
import XMonad.Prompt
|
||||
import XMonad.Prompt.Workspace
|
||||
import XMonad.Prompt (XPConfig)
|
||||
import XMonad.Prompt.Workspace (workspacePrompt)
|
||||
|
||||
import XMonad.Hooks.UrgencyHook
|
||||
import XMonad.Hooks.DynamicLog (PP(..))
|
||||
import qualified XMonad.Hooks.DynamicLog as DL
|
||||
|
||||
import XMonad.Util.Run (spawnPipe)
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
import XMonad.Hooks.StatusBar.PP (PP(ppHidden, ppVisible))
|
||||
import XMonad.Hooks.UrgencyHook (readUrgents)
|
||||
import XMonad.Hooks.WorkspaceHistory
|
||||
( workspaceHistory
|
||||
, workspaceHistoryByScreen
|
||||
, workspaceHistoryHook
|
||||
, workspaceHistoryHookExclude
|
||||
, workspaceHistoryModify
|
||||
)
|
||||
|
||||
-- $overview
|
||||
-- This module allows to organize your workspaces on a precise topic basis. So
|
||||
@@ -75,110 +105,128 @@ import qualified XMonad.Util.ExtensibleState as XS
|
||||
-- of last focused topics.
|
||||
|
||||
-- $usage
|
||||
-- Here is an example of configuration using TopicSpace:
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > -- The list of all topics/workspaces of your xmonad configuration.
|
||||
-- > -- The order is important, new topics must be inserted
|
||||
-- > -- at the end of the list if you want hot-restarting
|
||||
-- > -- to work.
|
||||
-- > myTopics :: [Topic]
|
||||
-- > myTopics =
|
||||
-- > [ "dashboard" -- the first one
|
||||
-- > , "admin", "build", "cleaning", "conf", "darcs", "haskell", "irc"
|
||||
-- > , "mail", "movie", "music", "talk", "text", "tools", "web", "xmonad"
|
||||
-- > , "yi", "documents", "twitter", "pdf"
|
||||
-- > ]
|
||||
-- > import qualified Data.Map.Strict as M
|
||||
-- > import qualified XMonad.StackSet as W
|
||||
-- >
|
||||
-- > import XMonad.Actions.TopicSpace
|
||||
-- > import XMonad.Util.EZConfig -- for the keybindings
|
||||
-- > import XMonad.Prompt.Workspace -- if you want to use the prompt
|
||||
--
|
||||
-- You will then have to
|
||||
--
|
||||
-- * Define a new 'TopicConfig' via 'TopicItem's
|
||||
--
|
||||
-- * Add the appropriate keybindings
|
||||
--
|
||||
-- * Replace the @workspaces@ field in your 'XConfig' with a list of
|
||||
-- your topics names
|
||||
--
|
||||
-- * Optionally, if you want to use the history features, add
|
||||
-- 'workspaceHistoryHook' from "XMonad.Hooks.WorkspaceHistory"
|
||||
-- (re-exported by this module) or an equivalent function to your
|
||||
-- @logHook@. See the documentation of
|
||||
-- "XMonad.Hooks.WorkspaceHistory" for further details
|
||||
--
|
||||
-- Let us go through a full example together.
|
||||
--
|
||||
-- A 'TopicItem' consists of three things: the name of the topic, its
|
||||
-- root directory, and the action associated to it—to be executed if the
|
||||
-- topic is empty or the action is forced via a keybinding.
|
||||
--
|
||||
-- We start by specifying our chosen topics as a list of such
|
||||
-- 'TopicItem's:
|
||||
--
|
||||
-- > topicItems :: [TopicItem]
|
||||
-- > topicItems =
|
||||
-- > [ inHome "1:WEB" (spawn "firefox")
|
||||
-- > , noAction "2" "."
|
||||
-- > , noAction "3:VID" "videos"
|
||||
-- > , TI "4:VPN" "openvpn" (spawn "urxvt -e randomVPN.sh")
|
||||
-- > , inHome "5:IM" (spawn "signal" *> spawn "telegram")
|
||||
-- > , inHome "6:IRC" (spawn "urxvt -e weechat")
|
||||
-- > , TI "dts" ".dotfiles" spawnShell
|
||||
-- > , TI "xm-con" "hs/xm-con" (spawnShell *> spawnShellIn "hs/xm")
|
||||
-- > ]
|
||||
--
|
||||
-- Then we just need to put together our topic config:
|
||||
--
|
||||
-- > myTopicConfig :: TopicConfig
|
||||
-- > myTopicConfig = def
|
||||
-- > { topicDirs = M.fromList $
|
||||
-- > [ ("conf", "w/conf")
|
||||
-- > , ("dashboard", "Desktop")
|
||||
-- > , ("yi", "w/dev-haskell/yi")
|
||||
-- > , ("darcs", "w/dev-haskell/darcs")
|
||||
-- > , ("haskell", "w/dev-haskell")
|
||||
-- > , ("xmonad", "w/dev-haskell/xmonad")
|
||||
-- > , ("tools", "w/tools")
|
||||
-- > , ("movie", "Movies")
|
||||
-- > , ("talk", "w/talks")
|
||||
-- > , ("music", "Music")
|
||||
-- > , ("documents", "w/documents")
|
||||
-- > , ("pdf", "w/documents")
|
||||
-- > ]
|
||||
-- > , defaultTopicAction = const $ spawnShell >*> 3
|
||||
-- > , defaultTopic = "dashboard"
|
||||
-- > , topicActions = M.fromList $
|
||||
-- > [ ("conf", spawnShell >> spawnShellIn "wd/ertai/private")
|
||||
-- > , ("darcs", spawnShell >*> 3)
|
||||
-- > , ("yi", spawnShell >*> 3)
|
||||
-- > , ("haskell", spawnShell >*> 2 >>
|
||||
-- > spawnShellIn "wd/dev-haskell/ghc")
|
||||
-- > , ("xmonad", spawnShellIn "wd/x11-wm/xmonad" >>
|
||||
-- > spawnShellIn "wd/x11-wm/xmonad/contrib" >>
|
||||
-- > spawnShellIn "wd/x11-wm/xmonad/utils" >>
|
||||
-- > spawnShellIn ".xmonad" >>
|
||||
-- > spawnShellIn ".xmonad")
|
||||
-- > , ("mail", mailAction)
|
||||
-- > , ("irc", ssh somewhere)
|
||||
-- > , ("admin", ssh somewhere >>
|
||||
-- > ssh nowhere)
|
||||
-- > , ("dashboard", spawnShell)
|
||||
-- > , ("twitter", spawnShell)
|
||||
-- > , ("web", spawn browserCmd)
|
||||
-- > , ("movie", spawnShell)
|
||||
-- > , ("documents", spawnShell >*> 2 >>
|
||||
-- > spawnShellIn "Documents" >*> 2)
|
||||
-- > , ("pdf", spawn pdfViewerCmd)
|
||||
-- > ]
|
||||
-- > { topicDirs = tiDirs topicItems
|
||||
-- > , topicActions = tiActions topicItems
|
||||
-- > , defaultTopicAction = const (pure ()) -- by default, do nothing
|
||||
-- > , defaultTopic = "1:WEB" -- fallback
|
||||
-- > }
|
||||
-- >
|
||||
-- > -- extend your keybindings
|
||||
-- > myKeys conf@XConfig{modMask=modm} =
|
||||
-- > [ ((modm , xK_n ), spawnShell) -- %! Launch terminal
|
||||
-- > , ((modm , xK_a ), currentTopicAction myTopicConfig)
|
||||
-- > , ((modm , xK_g ), promptedGoto)
|
||||
-- > , ((modm .|. shiftMask, xK_g ), promptedShift)
|
||||
-- > {- more keys ... -}
|
||||
-- > ]
|
||||
-- > ++
|
||||
-- > [ ((modm, k), switchNthLastFocused myTopicConfig i)
|
||||
-- > | (i, k) <- zip [1..] workspaceKeys]
|
||||
-- >
|
||||
--
|
||||
-- Above, we have used the `spawnShell` and `spawnShellIn` helper
|
||||
-- functions; here they are:
|
||||
--
|
||||
-- > spawnShell :: X ()
|
||||
-- > spawnShell = currentTopicDir myTopicConfig >>= spawnShellIn
|
||||
-- >
|
||||
-- > spawnShellIn :: Dir -> X ()
|
||||
-- > spawnShellIn dir = spawn $ "urxvt '(cd ''" ++ dir ++ "'' && " ++ myShell ++ " )'"
|
||||
-- >
|
||||
-- > spawnShellIn dir = spawn $ "alacritty --working-directory " ++ dir
|
||||
--
|
||||
-- Next, we define some other other useful helper functions. It is
|
||||
-- rather common to have a lot of topics—much more than available keys!
|
||||
-- In a situation like that, it's very convenient to switch topics with
|
||||
-- a prompt; the following use of 'workspacePrompt' does exactly that.
|
||||
--
|
||||
-- > goto :: Topic -> X ()
|
||||
-- > goto = switchTopic myTopicConfig
|
||||
-- >
|
||||
-- > promptedGoto :: X ()
|
||||
-- > promptedGoto = workspacePrompt myXPConfig goto
|
||||
-- > promptedGoto = workspacePrompt def goto
|
||||
-- >
|
||||
-- > promptedShift :: X ()
|
||||
-- > promptedShift = workspacePrompt myXPConfig $ windows . W.shift
|
||||
-- >
|
||||
-- > myConfig = do
|
||||
-- > checkTopicConfig myTopics myTopicConfig
|
||||
-- > myLogHook <- makeMyLogHook
|
||||
-- > return $ def
|
||||
-- > { borderWidth = 1 -- Width of the window border in pixels.
|
||||
-- > , workspaces = myTopics
|
||||
-- > , layoutHook = myModifiers myLayout
|
||||
-- > , manageHook = myManageHook
|
||||
-- > , logHook = myLogHook
|
||||
-- > , handleEventHook = myHandleEventHook
|
||||
-- > , terminal = myTerminal -- The preferred terminal program.
|
||||
-- > , normalBorderColor = "#3f3c6d"
|
||||
-- > , focusedBorderColor = "#4f66ff"
|
||||
-- > , XMonad.modMask = mod1Mask
|
||||
-- > , keys = myKeys
|
||||
-- > , mouseBindings = myMouseBindings
|
||||
-- > }
|
||||
-- > promptedShift = workspacePrompt def $ windows . W.shift
|
||||
-- >
|
||||
-- > -- Toggle between the two most recently used topics, but keep
|
||||
-- > -- screens separate. This needs @workspaceHistoryHook@.
|
||||
-- > toggleTopic :: X ()
|
||||
-- > toggleTopic = switchNthLastFocusedByScreen myTopicConfig 1
|
||||
--
|
||||
-- Hopefully you've gotten a general feeling of how to define these kind of
|
||||
-- small helper functions using what's provided in this module.
|
||||
--
|
||||
-- Adding the appropriate keybindings works as it normally would. Here,
|
||||
-- we'll use "XMonad.Util.EZConfig" syntax:
|
||||
--
|
||||
-- > myKeys :: [(String, X ())]
|
||||
-- > myKeys =
|
||||
-- > [ ("M-n" , spawnShell)
|
||||
-- > , ("M-a" , currentTopicAction myTopicConfig)
|
||||
-- > , ("M-g" , promptedGoto)
|
||||
-- > , ("M-S-g" , promptedShift)
|
||||
-- > , ("M-S-<Space>", toggleTopic)
|
||||
-- > ]
|
||||
-- > ++
|
||||
-- > -- The following does two things:
|
||||
-- > -- 1. Switch topics (no modifier)
|
||||
-- > -- 2. Move focused window to topic N (shift modifier)
|
||||
-- > [ ("M-" ++ m ++ k, f i)
|
||||
-- > | (i, k) <- zip (topicNames topicItems) (map show [1 .. 9 :: Int])
|
||||
-- > , (f, m) <- [(goto, ""), (windows . W.shift, "S-")]
|
||||
-- > ]
|
||||
--
|
||||
-- This makes @M-1@ to @M-9@ switch to the first nine topics that we
|
||||
-- have specified in @topicItems@.
|
||||
--
|
||||
-- You can also switch to the nine last-used topics instead:
|
||||
--
|
||||
-- > [ ("M-" ++ show i, switchNthLastFocused myTopicConfig i)
|
||||
-- > | i <- [1 .. 9]
|
||||
-- > ]
|
||||
--
|
||||
-- We can now put the whole configuration together with the following:
|
||||
--
|
||||
-- > main :: IO ()
|
||||
-- > main = xmonad =<< myConfig
|
||||
-- > main = xmonad $ def
|
||||
-- > { workspaces = topicNames topicItems
|
||||
-- > }
|
||||
-- > `additionalKeysP` myKeys
|
||||
|
||||
-- | An alias for @flip replicateM_@
|
||||
(>*>) :: Monad m => m a -> Int -> m ()
|
||||
@@ -188,85 +236,79 @@ infix >*>
|
||||
-- | 'Topic' is just an alias for 'WorkspaceId'
|
||||
type Topic = WorkspaceId
|
||||
|
||||
-- | 'Dir' is just an alias for 'FilePath' but should points to a directory.
|
||||
-- | 'Dir' is just an alias for 'FilePath', but should point to a directory.
|
||||
type Dir = FilePath
|
||||
|
||||
-- | Here is the topic space configuration area.
|
||||
data TopicConfig = TopicConfig { topicDirs :: M.Map Topic Dir
|
||||
-- ^ This mapping associate a directory to each topic.
|
||||
, topicActions :: M.Map Topic (X ())
|
||||
-- ^ This mapping associate an action to trigger when
|
||||
data TopicConfig = TopicConfig { topicDirs :: Map Topic Dir
|
||||
-- ^ This mapping associates a directory to each topic.
|
||||
, topicActions :: Map Topic (X ())
|
||||
-- ^ This mapping associates an action to trigger when
|
||||
-- switching to a given topic which workspace is empty.
|
||||
, defaultTopicAction :: Topic -> X ()
|
||||
-- ^ This is the default topic action.
|
||||
, defaultTopic :: Topic
|
||||
-- ^ This is the default topic.
|
||||
-- ^ This is the default (= fallback) topic.
|
||||
, maxTopicHistory :: Int
|
||||
-- ^ This setups the maximum depth of topic history, usually
|
||||
-- 10 is a good default since we can bind all of them using
|
||||
-- numeric keypad.
|
||||
-- ^ This specifies the maximum depth of the topic history;
|
||||
-- usually 10 is a good default since we can bind all of
|
||||
-- them using numeric keypad.
|
||||
}
|
||||
{-# DEPRECATED maxTopicHistory "This field will be removed in the future; history is now handled by XMonad.Hooks.WorkspaceHistory" #-}
|
||||
|
||||
instance Default TopicConfig where
|
||||
def = TopicConfig { topicDirs = M.empty
|
||||
, topicActions = M.empty
|
||||
, defaultTopicAction = const (ask >>= spawn . terminal . config)
|
||||
, defaultTopic = "1"
|
||||
, maxTopicHistory = 10
|
||||
}
|
||||
def = TopicConfig { topicDirs = M.empty
|
||||
, topicActions = M.empty
|
||||
, defaultTopicAction = const (ask >>= spawn . terminal . config)
|
||||
, defaultTopic = "1"
|
||||
, maxTopicHistory = 10
|
||||
}
|
||||
|
||||
{-# DEPRECATED defaultTopicConfig "Use def (from Data.Default, and re-exported by XMonad.Actions.TopicSpace) instead." #-}
|
||||
defaultTopicConfig :: TopicConfig
|
||||
defaultTopicConfig = def
|
||||
-- | Return the (possibly empty) list of last focused topics.
|
||||
getLastFocusedTopics :: X [Topic]
|
||||
getLastFocusedTopics = workspaceHistory
|
||||
{-# DEPRECATED getLastFocusedTopics "Use XMonad.Hooks.WorkspaceHistory.workspaceHistory (re-exported by this module) instead" #-}
|
||||
|
||||
newtype PrevTopics = PrevTopics { getPrevTopics :: [String] } deriving (Read,Show,Typeable)
|
||||
instance ExtensionClass PrevTopics where
|
||||
initialValue = PrevTopics []
|
||||
extensionType = PersistentExtension
|
||||
|
||||
-- | Returns the list of last focused workspaces the empty list otherwise.
|
||||
getLastFocusedTopics :: X [String]
|
||||
getLastFocusedTopics = XS.gets getPrevTopics
|
||||
|
||||
-- | Given a 'TopicConfig', the last focused topic, and a predicate that will
|
||||
-- select topics that one want to keep, this function will set the property
|
||||
-- of last focused topics.
|
||||
setLastFocusedTopic :: Topic -> (Topic -> Bool) -> X ()
|
||||
setLastFocusedTopic w predicate =
|
||||
XS.modify $ PrevTopics
|
||||
. seqList . nub . (w:) . filter predicate
|
||||
. getPrevTopics
|
||||
where seqList xs = length xs `seq` xs
|
||||
-- | Given a 'TopicConfig', a topic, and a predicate to select topics that one
|
||||
-- wants to keep, this function will cons the topic in front of the list of
|
||||
-- last focused topics and filter it according to the predicate. Note that we
|
||||
-- prune the list in case that its length exceeds 'maxTopicHistory'.
|
||||
setLastFocusedTopic :: TopicConfig -> Topic -> (Topic -> Bool) -> X ()
|
||||
setLastFocusedTopic tc w predicate = do
|
||||
sid <- gets $ W.screen . W.current . windowset
|
||||
workspaceHistoryModify $
|
||||
take (maxTopicHistory tc) . nub . filter (predicate . snd) . ((sid, w) :)
|
||||
{-# DEPRECATED setLastFocusedTopic "Use XMonad.Hooks.WorkspaceHistory instead" #-}
|
||||
|
||||
-- | Reverse the list of "last focused topics"
|
||||
reverseLastFocusedTopics :: X ()
|
||||
reverseLastFocusedTopics =
|
||||
XS.modify $ PrevTopics . reverse . getPrevTopics
|
||||
reverseLastFocusedTopics = workspaceHistoryModify reverse
|
||||
|
||||
-- | This function is a variant of 'DL.pprWindowSet' which takes a topic configuration
|
||||
-- and a pretty-printing record 'PP'. It will show the list of topics sorted historically
|
||||
-- and highlighting topics with urgent windows.
|
||||
-- | This function is a variant of 'SBPP.pprWindowSet' which takes a topic
|
||||
-- configuration and a pretty-printing record 'PP'. It will show the list of
|
||||
-- topics sorted historically and highlight topics with urgent windows.
|
||||
pprWindowSet :: TopicConfig -> PP -> X String
|
||||
pprWindowSet tg pp = do
|
||||
winset <- gets windowset
|
||||
urgents <- readUrgents
|
||||
let empty_workspaces = map W.tag $ filter (isNothing . W.stack) $ W.workspaces winset
|
||||
maxDepth = maxTopicHistory tg
|
||||
setLastFocusedTopic (W.tag . W.workspace . W.current $ winset)
|
||||
(`notElem` empty_workspaces)
|
||||
lastWs <- getLastFocusedTopics
|
||||
let depth topic = fromJust $ elemIndex topic (lastWs ++ [topic])
|
||||
add_depth proj topic = proj pp . (((topic++":")++) . show) . depth $ topic
|
||||
pp' = pp { ppHidden = add_depth ppHidden, ppVisible = add_depth ppVisible }
|
||||
sortWindows = take maxDepth . sortBy (comparing $ depth . W.tag)
|
||||
return $ DL.pprWindowSet sortWindows urgents pp' winset
|
||||
winset <- gets windowset
|
||||
urgents <- readUrgents
|
||||
let empty_workspaces = map W.tag $ filter (isNothing . W.stack) $ W.workspaces winset
|
||||
maxDepth = maxTopicHistory tg
|
||||
setLastFocusedTopic tg
|
||||
(W.tag . W.workspace . W.current $ winset)
|
||||
(`notElem` empty_workspaces)
|
||||
lastWs <- workspaceHistory
|
||||
let depth topic = fromJust $ elemIndex topic (lastWs ++ [topic])
|
||||
add_depth proj topic = proj pp . (((topic++":")++) . show) . depth $ topic
|
||||
pp' = pp { ppHidden = add_depth ppHidden, ppVisible = add_depth ppVisible }
|
||||
sortWindows = take maxDepth . sortOn (depth . W.tag)
|
||||
return $ SBPP.pprWindowSet sortWindows urgents pp' winset
|
||||
|
||||
-- | Given a prompt configuration and a topic configuration, triggers the action associated with
|
||||
-- | Given a prompt configuration and a topic configuration, trigger the action associated with
|
||||
-- the topic given in prompt.
|
||||
topicActionWithPrompt :: XPConfig -> TopicConfig -> X ()
|
||||
topicActionWithPrompt xp tg = workspacePrompt xp (liftM2 (>>) (switchTopic tg) (topicAction tg))
|
||||
topicActionWithPrompt xp tg = workspacePrompt xp (liftA2 (>>) (switchTopic tg) (topicAction tg))
|
||||
|
||||
-- | Given a configuration and a topic, triggers the action associated with the given topic.
|
||||
-- | Given a configuration and a topic, trigger the action associated with the given topic.
|
||||
topicAction :: TopicConfig -> Topic -> X ()
|
||||
topicAction tg topic = fromMaybe (defaultTopicAction tg topic) $ M.lookup topic $ topicActions tg
|
||||
|
||||
@@ -276,46 +318,94 @@ currentTopicAction tg = topicAction tg =<< gets (W.tag . W.workspace . W.current
|
||||
|
||||
-- | Switch to the given topic.
|
||||
switchTopic :: TopicConfig -> Topic -> X ()
|
||||
switchTopic tg topic = do
|
||||
switchTopic tc topic = do
|
||||
-- Switch to topic and add it to the last seen topics
|
||||
windows $ W.greedyView topic
|
||||
|
||||
-- If applicable, execute the topic action
|
||||
wins <- gets (W.integrate' . W.stack . W.workspace . W.current . windowset)
|
||||
when (null wins) $ topicAction tg topic
|
||||
when (null wins) $ topicAction tc topic
|
||||
|
||||
-- | Switch to the Nth last focused topic or failback to the 'defaultTopic'.
|
||||
-- | Switch to the Nth last focused topic or fall back to the 'defaultTopic'.
|
||||
switchNthLastFocused :: TopicConfig -> Int -> X ()
|
||||
switchNthLastFocused tg depth = do
|
||||
lastWs <- getLastFocusedTopics
|
||||
switchTopic tg $ (lastWs ++ repeat (defaultTopic tg)) !! depth
|
||||
switchNthLastFocused = switchNthLastFocusedExclude []
|
||||
|
||||
-- | Shift the focused window to the Nth last focused topic, or fallback to doing nothing.
|
||||
-- | Like 'switchNthLastFocused', but also filter out certain topics.
|
||||
switchNthLastFocusedExclude :: [Topic] -> TopicConfig -> Int -> X ()
|
||||
switchNthLastFocusedExclude excludes tc depth = do
|
||||
lastWs <- filter (`notElem` excludes) <$> workspaceHistory
|
||||
switchTopic tc $ (lastWs ++ repeat (defaultTopic tc)) !! depth
|
||||
|
||||
-- | Like 'switchNthLastFocused', but only consider topics that used to
|
||||
-- be on the current screen.
|
||||
--
|
||||
-- For example, the following function allows one to toggle between the
|
||||
-- currently focused and the last used topic, while treating different
|
||||
-- screens completely independently from one another.
|
||||
--
|
||||
-- > toggleTopicScreen = switchNthLastFocusedByScreen myTopicConfig 1
|
||||
switchNthLastFocusedByScreen :: TopicConfig -> Int -> X ()
|
||||
switchNthLastFocusedByScreen tc depth = do
|
||||
sid <- gets $ W.screen . W.current . windowset
|
||||
sws <- fromMaybe []
|
||||
. listToMaybe
|
||||
. map snd
|
||||
. filter ((== sid) . fst)
|
||||
<$> workspaceHistoryByScreen
|
||||
switchTopic tc $ (sws ++ repeat (defaultTopic tc)) !! depth
|
||||
|
||||
-- | Shift the focused window to the Nth last focused topic, or fall back to doing nothing.
|
||||
shiftNthLastFocused :: Int -> X ()
|
||||
shiftNthLastFocused n = do
|
||||
ws <- fmap (listToMaybe . drop n) getLastFocusedTopics
|
||||
ws <- fmap (listToMaybe . drop n) workspaceHistory
|
||||
whenJust ws $ windows . W.shift
|
||||
|
||||
-- | Returns the directory associated with current topic returns the empty string otherwise.
|
||||
currentTopicDir :: TopicConfig -> X String
|
||||
-- | Return the directory associated with the current topic, or return the empty
|
||||
-- string if the topic could not be found.
|
||||
currentTopicDir :: TopicConfig -> X FilePath
|
||||
currentTopicDir tg = do
|
||||
topic <- gets (W.tag . W.workspace . W.current . windowset)
|
||||
return . fromMaybe "" . M.lookup topic $ topicDirs tg
|
||||
|
||||
-- | Check the given topic configuration for duplicates topics or undefined topics.
|
||||
-- | Check the given topic configuration for duplicate or undefined topics.
|
||||
checkTopicConfig :: [Topic] -> TopicConfig -> IO ()
|
||||
checkTopicConfig tags tg = do
|
||||
-- tags <- gets $ map W.tag . workspaces . windowset
|
||||
-- tags <- gets $ map W.tag . workspaces . windowset
|
||||
|
||||
let
|
||||
seenTopics = nub $ sort $ M.keys (topicDirs tg) ++ M.keys (topicActions tg)
|
||||
dups = tags \\ nub tags
|
||||
diffTopic = seenTopics \\ sort tags
|
||||
check lst msg = unless (null lst) $ xmessage $ msg ++ " (tags): " ++ show lst
|
||||
let
|
||||
seenTopics = nub $ sort $ M.keys (topicDirs tg) ++ M.keys (topicActions tg)
|
||||
dups = tags \\ nub tags
|
||||
diffTopic = seenTopics \\ sort tags
|
||||
check lst msg = unless (null lst) $ xmessage $ msg ++ " (tags): " ++ show lst
|
||||
|
||||
check diffTopic "Seen but missing topics/workspaces"
|
||||
check dups "Duplicate topics/workspaces"
|
||||
check diffTopic "Seen but missing topics/workspaces"
|
||||
check dups "Duplicate topics/workspaces"
|
||||
|
||||
-- | Display the given message using the @xmessage@ program.
|
||||
xmessage :: String -> IO ()
|
||||
xmessage s = do
|
||||
h <- spawnPipe "xmessage -file -"
|
||||
hPutStr h s
|
||||
hClose h
|
||||
-- | Convenience type for specifying topics.
|
||||
data TopicItem = TI
|
||||
{ tiName :: !Topic -- ^ 'Topic' ≡ 'String'
|
||||
, tiDir :: !Dir -- ^ Directory associated with topic; 'Dir' ≡ 'String'
|
||||
, tiAction :: !(X ()) -- ^ Startup hook when topic is empty
|
||||
}
|
||||
|
||||
-- | Extract the names from a given list of 'TopicItem's.
|
||||
topicNames :: [TopicItem] -> [Topic]
|
||||
topicNames = map tiName
|
||||
|
||||
-- | From a list of 'TopicItem's, build a map that can be supplied as
|
||||
-- the 'topicDirs'.
|
||||
tiDirs :: [TopicItem] -> Map Topic Dir
|
||||
tiDirs = M.fromList . map (\TI{ tiName, tiDir } -> (tiName, tiDir))
|
||||
|
||||
-- | From a list of 'TopicItem's, build a map that can be supplied as
|
||||
-- the 'topicActions'.
|
||||
tiActions :: [TopicItem] -> Map Topic (X ())
|
||||
tiActions = M.fromList . map (\TI{ tiName, tiAction } -> (tiName, tiAction))
|
||||
|
||||
-- | Associate a directory with the topic, but don't spawn anything.
|
||||
noAction :: Topic -> Dir -> TopicItem
|
||||
noAction n d = TI n d (pure ())
|
||||
|
||||
-- | Topic with @tiDir = ~/@.
|
||||
inHome :: Topic -> X () -> TopicItem
|
||||
inHome n = TI n "."
|
||||
|
@@ -1,9 +1,12 @@
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
{-# LANGUAGE MultiWayIf #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.TreeSelect
|
||||
-- Description : Display workspaces or actions in a tree-like format.
|
||||
-- Copyright : (c) Tom Smeets <tom.tsmeets@gmail.com>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -39,6 +42,7 @@ module XMonad.Actions.TreeSelect
|
||||
|
||||
, TSConfig(..)
|
||||
, tsDefaultConfig
|
||||
, def
|
||||
|
||||
-- * Navigation
|
||||
-- $navigation
|
||||
@@ -60,16 +64,13 @@ module XMonad.Actions.TreeSelect
|
||||
, treeselectAt
|
||||
) where
|
||||
|
||||
import Control.Applicative
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.State
|
||||
import Data.List (find)
|
||||
import Data.Maybe
|
||||
import Data.Tree
|
||||
import Foreign
|
||||
import Foreign (shiftL, shiftR, (.&.))
|
||||
import System.IO
|
||||
import System.Posix.Process (forkProcess, executeFile)
|
||||
import XMonad hiding (liftX)
|
||||
import XMonad.Prelude
|
||||
import XMonad.StackSet as W
|
||||
import XMonad.Util.Font
|
||||
import XMonad.Util.NamedWindows
|
||||
@@ -113,10 +114,10 @@ import Graphics.X11.Xrender
|
||||
--
|
||||
-- Optionally, if you add 'workspaceHistoryHook' to your 'logHook' you can use the \'o\' and \'i\' keys to select from previously-visited workspaces
|
||||
--
|
||||
-- > xmonad $ defaultConfig { ...
|
||||
-- > , workspaces = toWorkspaces myWorkspaces
|
||||
-- > , logHook = workspaceHistoryHook
|
||||
-- > }
|
||||
-- > xmonad $ def { ...
|
||||
-- > , workspaces = toWorkspaces myWorkspaces
|
||||
-- > , logHook = workspaceHistoryHook
|
||||
-- > }
|
||||
--
|
||||
-- After that you still need to bind buttons to 'treeselectWorkspace' to start selecting a workspaces and moving windows
|
||||
--
|
||||
@@ -131,22 +132,22 @@ import Graphics.X11.Xrender
|
||||
-- $config
|
||||
-- The selection menu is very configurable, you can change the font, all colors and the sizes of the boxes.
|
||||
--
|
||||
-- The default config defined as 'tsDefaultConfig'
|
||||
-- The default config defined as 'def'
|
||||
--
|
||||
-- > tsDefaultConfig = TSConfig { ts_hidechildren = True
|
||||
-- > , ts_background = 0xc0c0c0c0
|
||||
-- > , ts_font = "xft:Sans-16"
|
||||
-- > , ts_node = (0xff000000, 0xff50d0db)
|
||||
-- > , ts_nodealt = (0xff000000, 0xff10b8d6)
|
||||
-- > , ts_highlight = (0xffffffff, 0xffff0000)
|
||||
-- > , ts_extra = 0xff000000
|
||||
-- > , ts_node_width = 200
|
||||
-- > , ts_node_height = 30
|
||||
-- > , ts_originX = 0
|
||||
-- > , ts_originY = 0
|
||||
-- > , ts_indent = 80
|
||||
-- > , ts_navigate = defaultNavigation
|
||||
-- > }
|
||||
-- > def = TSConfig { ts_hidechildren = True
|
||||
-- > , ts_background = 0xc0c0c0c0
|
||||
-- > , ts_font = "xft:Sans-16"
|
||||
-- > , ts_node = (0xff000000, 0xff50d0db)
|
||||
-- > , ts_nodealt = (0xff000000, 0xff10b8d6)
|
||||
-- > , ts_highlight = (0xffffffff, 0xffff0000)
|
||||
-- > , ts_extra = 0xff000000
|
||||
-- > , ts_node_width = 200
|
||||
-- > , ts_node_height = 30
|
||||
-- > , ts_originX = 0
|
||||
-- > , ts_originY = 0
|
||||
-- > , ts_indent = 80
|
||||
-- > , ts_navigate = defaultNavigation
|
||||
-- > }
|
||||
|
||||
-- $pixel
|
||||
--
|
||||
@@ -160,8 +161,8 @@ import Graphics.X11.Xrender
|
||||
-- white = 0xffffffff
|
||||
-- black = 0xff000000
|
||||
-- red = 0xffff0000
|
||||
-- blue = 0xff00ff00
|
||||
-- green = 0xff0000ff
|
||||
-- green = 0xff00ff00
|
||||
-- blue = 0xff0000ff
|
||||
-- transparent = 0x00000000
|
||||
-- @
|
||||
|
||||
@@ -258,6 +259,7 @@ defaultNavigation = M.fromList
|
||||
-- Using nice alternating blue nodes
|
||||
tsDefaultConfig :: TSConfig a
|
||||
tsDefaultConfig = def
|
||||
{-# DEPRECATED tsDefaultConfig "Use def (from Data.Default, and re-exported by XMonad.Actions.TreeSelect) instead." #-}
|
||||
|
||||
-- | Tree Node With a name and extra text
|
||||
data TSNode a = TSNode { tsn_name :: String
|
||||
@@ -316,7 +318,9 @@ treeselectAt conf@TSConfig{..} zipper hist = withDisplay $ \display -> do
|
||||
set_colormap attributes colormap
|
||||
set_background_pixel attributes ts_background
|
||||
set_border_pixel attributes 0
|
||||
createWindow display rootw rect_x rect_y rect_width rect_height 0 (visualInfo_depth vinfo) inputOutput (visualInfo_visual vinfo) (cWColormap .|. cWBorderPixel .|. cWBackPixel) attributes
|
||||
w <- createWindow display rootw rect_x rect_y rect_width rect_height 0 (visualInfo_depth vinfo) inputOutput (visualInfo_visual vinfo) (cWColormap .|. cWBorderPixel .|. cWBackPixel) attributes
|
||||
setClassHint display w (ClassHint "xmonad-tree_select" "xmonad")
|
||||
pure w
|
||||
|
||||
liftIO $ do
|
||||
-- TODO: move below?
|
||||
@@ -405,7 +409,7 @@ treeselectWorkspace c xs f = do
|
||||
, "XConfig.workspaces: "
|
||||
] ++ map tag ws
|
||||
hPutStrLn stderr msg
|
||||
_ <- forkProcess $ executeFile "xmessage" True [msg] Nothing
|
||||
xmessage msg
|
||||
return ()
|
||||
where
|
||||
mkNode n w = do
|
||||
@@ -448,8 +452,8 @@ splitPath i = case break (== '.') i of
|
||||
-- > ]
|
||||
-- > ]
|
||||
treeselectAction :: TSConfig (X a) -> Forest (TSNode (X a)) -> X ()
|
||||
treeselectAction c xs = treeselect c xs >>= \x -> case x of
|
||||
Just a -> a >> return ()
|
||||
treeselectAction c xs = treeselect c xs >>= \case
|
||||
Just a -> void a
|
||||
Nothing -> return ()
|
||||
|
||||
forMForest :: (Functor m, Applicative m, Monad m) => [Tree a] -> (a -> m b) -> m [Tree b]
|
||||
@@ -461,7 +465,7 @@ mapMTree f (Node x xs) = Node <$> f x <*> mapM (mapMTree f) xs
|
||||
|
||||
-- | Quit returning the currently selected node
|
||||
select :: TreeSelect a (Maybe a)
|
||||
select = Just <$> gets (tsn_value . cursor . tss_tree)
|
||||
select = gets (Just . (tsn_value . cursor . tss_tree))
|
||||
|
||||
-- | Quit without returning anything
|
||||
cancel :: TreeSelect a (Maybe a)
|
||||
@@ -523,18 +527,21 @@ moveWith f = do
|
||||
-- | wait for keys and run navigation
|
||||
navigate :: TreeSelect a (Maybe a)
|
||||
navigate = gets tss_display >>= \d -> join . liftIO . allocaXEvent $ \e -> do
|
||||
maskEvent d (exposureMask .|. keyPressMask .|. buttonReleaseMask) e
|
||||
maskEvent d (exposureMask .|. keyPressMask .|. buttonReleaseMask .|. buttonPressMask) e
|
||||
|
||||
ev <- getEvent e
|
||||
|
||||
if ev_event_type ev == keyPress
|
||||
then do
|
||||
(ks, _) <- lookupString $ asKeyEvent e
|
||||
return $ do
|
||||
mask <- liftX $ cleanMask (ev_state ev)
|
||||
f <- asks ts_navigate
|
||||
fromMaybe navigate $ M.lookup (mask, fromMaybe xK_VoidSymbol ks) f
|
||||
else return navigate
|
||||
if | ev_event_type ev == keyPress -> do
|
||||
(ks, _) <- lookupString $ asKeyEvent e
|
||||
return $ do
|
||||
mask <- liftX $ cleanMask (ev_state ev)
|
||||
f <- asks ts_navigate
|
||||
fromMaybe navigate $ M.lookup (mask, fromMaybe xK_VoidSymbol ks) f
|
||||
| ev_event_type ev == buttonPress -> do
|
||||
-- See XMonad.Prompt Note [Allow ButtonEvents]
|
||||
allowEvents d replayPointer currentTime
|
||||
return navigate
|
||||
| otherwise -> return navigate
|
||||
|
||||
-- | Request a full redraw
|
||||
redraw :: TreeSelect a ()
|
||||
@@ -599,7 +606,7 @@ drawNode ix iy TSNode{..} col = do
|
||||
colormap <- gets tss_colormap
|
||||
visual <- gets tss_visual
|
||||
liftIO $ drawWinBox window display visual colormap gc font col tsn_name ts_extra tsn_extra
|
||||
(ix * ts_indent) (iy * ts_node_height)
|
||||
(ix * ts_indent + ts_originX) (iy * ts_node_height + ts_originY)
|
||||
ts_node_width ts_node_height
|
||||
|
||||
-- TODO: draw extra text (transparent background? or ts_background)
|
||||
@@ -650,8 +657,16 @@ drawStringXMF display window visual colormap gc font col x y text = case font of
|
||||
--
|
||||
-- Note that it uses short to represent its components
|
||||
fromARGB :: Pixel -> XRenderColor
|
||||
fromARGB x = XRenderColor (fromIntegral $ 0xff00 .&. shiftR x 8) -- red
|
||||
(fromIntegral $ 0xff00 .&. x) -- green
|
||||
(fromIntegral $ 0xff00 .&. shiftL x 8) -- blue
|
||||
(fromIntegral $ 0xff00 .&. shiftR x 16) -- alpha
|
||||
fromARGB x =
|
||||
#if MIN_VERSION_X11_xft(0, 3, 3)
|
||||
XRenderColor r g b a
|
||||
#else
|
||||
-- swapped green/blue as a workaround for the faulty Storable instance in X11-xft < 0.3.3
|
||||
XRenderColor r b g a
|
||||
#endif
|
||||
where
|
||||
r = fromIntegral $ 0xff00 .&. shiftR x 8
|
||||
g = fromIntegral $ 0xff00 .&. x
|
||||
b = fromIntegral $ 0xff00 .&. shiftL x 8
|
||||
a = fromIntegral $ 0xff00 .&. shiftR x 16
|
||||
#endif
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.UpdateFocus
|
||||
-- Description : Updates the focus on mouse move in unfocused windows.
|
||||
-- Copyright : (c) Daniel Schoepe
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -16,13 +17,13 @@ module XMonad.Actions.UpdateFocus (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
focusOnMouseMove,
|
||||
adjustEventInput
|
||||
adjustEventInput,
|
||||
focusUnderPointer,
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude
|
||||
import qualified XMonad.StackSet as W
|
||||
import Control.Monad (when)
|
||||
import Data.Monoid
|
||||
|
||||
-- $usage
|
||||
-- To make the focus update on mouse movement within an unfocused window, add the
|
||||
@@ -40,7 +41,7 @@ import Data.Monoid
|
||||
|
||||
-- | Changes the focus if the mouse is moved within an unfocused window.
|
||||
focusOnMouseMove :: Event -> X All
|
||||
focusOnMouseMove (MotionEvent { ev_x = x, ev_y = y, ev_window = root }) = do
|
||||
focusOnMouseMove MotionEvent{ ev_x = x, ev_y = y, ev_window = root } = do
|
||||
-- check only every 15 px to avoid excessive calls to translateCoordinates
|
||||
when (x `mod` 15 == 0 || y `mod` 15 == 0) $ do
|
||||
dpy <- asks display
|
||||
@@ -58,3 +59,25 @@ adjustEventInput = withDisplay $ \dpy -> do
|
||||
io $ selectInput dpy rootw $ substructureRedirectMask .|. substructureNotifyMask
|
||||
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
|
||||
.|. buttonPressMask .|. pointerMotionMask
|
||||
|
||||
-- | Focus the window under the mouse pointer, unless we're currently changing
|
||||
-- focus with the mouse or dragging. This is the inverse to
|
||||
-- "XMonad.Actions.UpdatePointer": instead of moving the mouse pointer to
|
||||
-- match the focus, we change the focus to match the mouse pointer.
|
||||
--
|
||||
-- This is meant to be used together with
|
||||
-- 'XMonad.Actions.UpdatePointer.updatePointer' in individual key bindings.
|
||||
-- Bindings that change focus should invoke
|
||||
-- 'XMonad.Actions.UpdatePointer.updatePointer' at the end, bindings that
|
||||
-- switch workspaces or change layouts should call 'focusUnderPointer' at the
|
||||
-- end. Neither should go to 'logHook', as that would override the other.
|
||||
--
|
||||
-- This is more finicky to set up than 'focusOnMouseMove', but ensures that
|
||||
-- focus is updated immediately, without having to touch the mouse.
|
||||
focusUnderPointer :: X ()
|
||||
focusUnderPointer = whenX (not <$> (asks mouseFocused <||> gets (isJust . dragging))) $ do
|
||||
dpy <- asks display
|
||||
root <- asks theRoot
|
||||
(_, _, w', _, _, _, _, _) <- io $ queryPointer dpy root
|
||||
w <- gets (W.peek . windowset)
|
||||
when (w' /= none && Just w' /= w) (focus w')
|
||||
|
@@ -2,6 +2,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonadContrib.UpdatePointer
|
||||
-- Description : Causes the pointer to follow whichever window focus changes to.
|
||||
-- Copyright : (c) Robert Marlow <robreim@bobturf.org>, 2015 Evgeny Kurnevsky
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -24,12 +25,11 @@ module XMonad.Actions.UpdatePointer
|
||||
where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Util.XUtils (fi)
|
||||
import Control.Arrow
|
||||
import Control.Monad
|
||||
import XMonad.Prelude
|
||||
import XMonad.StackSet (member, peek, screenDetail, current)
|
||||
import Data.Maybe
|
||||
import Control.Exception
|
||||
|
||||
import Control.Exception (SomeException, try)
|
||||
import Control.Arrow ((&&&), (***))
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
@@ -61,6 +61,11 @@ import Control.Exception
|
||||
-- | Update the pointer's location to the currently focused
|
||||
-- window or empty screen unless it's already there, or unless the user was changing
|
||||
-- focus with the mouse
|
||||
--
|
||||
-- See also 'XMonad.Actions.UpdateFocus.focusUnderPointer' for an inverse
|
||||
-- operation that updates the focus instead. The two can be combined in a
|
||||
-- single config if neither goes into 'logHook' but are invoked explicitly in
|
||||
-- individual key bindings.
|
||||
updatePointer :: (Rational, Rational) -> (Rational, Rational) -> X ()
|
||||
updatePointer refPos ratio = do
|
||||
ws <- gets windowset
|
||||
@@ -105,6 +110,7 @@ lerp :: (RealFrac r, Real a, Real b) => r -> a -> b -> r
|
||||
lerp r a b = (1 - r) * realToFrac a + r * realToFrac b
|
||||
|
||||
clip :: Ord a => (a, a) -> a -> a
|
||||
clip (lower, upper) x = if x < lower then lower
|
||||
else if x > upper then upper else x
|
||||
|
||||
clip (lower, upper) x
|
||||
| x < lower = lower
|
||||
| x > upper = upper
|
||||
| otherwise = x
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.Warp
|
||||
-- Description : Warp the pointer to a given window or screen.
|
||||
-- Copyright : (c) daniel@wagner-home.com
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -22,7 +23,7 @@ module XMonad.Actions.Warp (
|
||||
warpToWindow
|
||||
) where
|
||||
|
||||
import Data.List
|
||||
import XMonad.Prelude
|
||||
import XMonad
|
||||
import XMonad.StackSet as W
|
||||
|
||||
@@ -101,7 +102,7 @@ warpToWindow h v =
|
||||
warpToScreen :: ScreenId -> Rational -> Rational -> X ()
|
||||
warpToScreen n h v = do
|
||||
root <- asks theRoot
|
||||
(StackSet {current = x, visible = xs}) <- gets windowset
|
||||
StackSet{current = x, visible = xs} <- gets windowset
|
||||
whenJust (fmap (screenRect . W.screenDetail) . find ((n==) . W.screen) $ x : xs)
|
||||
$ \r ->
|
||||
warp root (rect_x r + fraction h (rect_width r))
|
||||
|
@@ -2,6 +2,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.WindowBringer
|
||||
-- Description : Dmenu operations to bring windows to you, and bring you to windows.
|
||||
-- Copyright : Devin Mullins <me@twifkak.com>
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
@@ -21,17 +22,17 @@ module XMonad.Actions.WindowBringer (
|
||||
WindowBringerConfig(..),
|
||||
gotoMenu, gotoMenuConfig, gotoMenu', gotoMenuArgs, gotoMenuArgs',
|
||||
bringMenu, bringMenuConfig, bringMenu', bringMenuArgs, bringMenuArgs',
|
||||
windowMap, windowMap', bringWindow, actionMenu
|
||||
windowMap, windowAppMap, windowMap', bringWindow, actionMenu
|
||||
) where
|
||||
|
||||
import Control.Applicative((<$>))
|
||||
import Control.Monad
|
||||
import qualified Data.Map as M
|
||||
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad
|
||||
import qualified XMonad as X
|
||||
import XMonad.Util.Dmenu (menuMapArgs)
|
||||
import XMonad.Util.NamedWindows (getName)
|
||||
import XMonad.Util.NamedWindows (getName, getNameWMClass)
|
||||
|
||||
-- $usage
|
||||
--
|
||||
@@ -51,12 +52,14 @@ data WindowBringerConfig = WindowBringerConfig
|
||||
{ menuCommand :: String -- ^ The shell command that will handle window selection
|
||||
, menuArgs :: [String] -- ^ Arguments to be passed to menuCommand
|
||||
, windowTitler :: X.WindowSpace -> Window -> X String -- ^ A function that produces window titles given a workspace and a window
|
||||
, windowFilter :: Window -> X Bool -- ^ Filter function to decide which windows to consider
|
||||
}
|
||||
|
||||
instance Default WindowBringerConfig where
|
||||
def = WindowBringerConfig{ menuCommand = "dmenu"
|
||||
, menuArgs = ["-i"]
|
||||
, windowTitler = decorateName
|
||||
, windowFilter = \_ -> return True
|
||||
}
|
||||
|
||||
-- | Pops open a dmenu with window titles. Choose one, and you will be
|
||||
@@ -123,11 +126,8 @@ bringWindow w ws = W.shiftWin (W.currentTag ws) w ws
|
||||
-- | Calls dmenuMap to grab the appropriate Window, and hands it off to action
|
||||
-- if found.
|
||||
actionMenu :: WindowBringerConfig -> (Window -> X.WindowSet -> X.WindowSet) -> X ()
|
||||
actionMenu WindowBringerConfig{ menuCommand = cmd
|
||||
, menuArgs = args
|
||||
, windowTitler = titler
|
||||
} action
|
||||
= windowMap' titler >>= menuMapFunction >>= flip X.whenJust (windows . action)
|
||||
actionMenu c@WindowBringerConfig{ menuCommand = cmd, menuArgs = args } action =
|
||||
windowMap' c >>= menuMapFunction >>= flip X.whenJust (windows . action)
|
||||
where
|
||||
menuMapFunction :: M.Map String a -> X (Maybe a)
|
||||
menuMapFunction = menuMapArgs cmd args
|
||||
@@ -135,15 +135,20 @@ actionMenu WindowBringerConfig{ menuCommand = cmd
|
||||
|
||||
-- | A map from window names to Windows.
|
||||
windowMap :: X (M.Map String Window)
|
||||
windowMap = windowMap' decorateName
|
||||
windowMap = windowMap' def
|
||||
|
||||
-- | A map from application executable names to Windows.
|
||||
windowAppMap :: X (M.Map String Window)
|
||||
windowAppMap = windowMap' def { windowTitler = decorateAppName }
|
||||
|
||||
-- | A map from window names to Windows, given a windowTitler function.
|
||||
windowMap' :: (X.WindowSpace -> Window -> X String) -> X (M.Map String Window)
|
||||
windowMap' titler = do
|
||||
ws <- gets X.windowset
|
||||
M.fromList . concat <$> mapM keyValuePairs (W.workspaces ws)
|
||||
where keyValuePairs ws = mapM (keyValuePair ws) $ W.integrate' (W.stack ws)
|
||||
keyValuePair ws w = flip (,) w <$> titler ws w
|
||||
windowMap' :: WindowBringerConfig -> X (M.Map String Window)
|
||||
windowMap' WindowBringerConfig{ windowTitler = titler, windowFilter = include } = do
|
||||
windowSet <- gets X.windowset
|
||||
M.fromList . concat <$> mapM keyValuePairs (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
|
||||
|
||||
-- | Returns the window name as will be listed in dmenu.
|
||||
-- Tagged with the workspace ID, to guarantee uniqueness, and to let the user
|
||||
@@ -152,3 +157,11 @@ decorateName :: X.WindowSpace -> Window -> X String
|
||||
decorateName ws w = do
|
||||
name <- show <$> getName w
|
||||
return $ name ++ " [" ++ W.tag ws ++ "]"
|
||||
|
||||
-- | Returns the window name as will be listed in dmenu. This will
|
||||
-- return the executable name of the window along with it's workspace
|
||||
-- ID.
|
||||
decorateAppName :: X.WindowSpace -> Window -> X String
|
||||
decorateAppName ws w = do
|
||||
name <- show <$> getNameWMClass w
|
||||
return $ name ++ " [" ++ W.tag ws ++ "]"
|
||||
|
@@ -1,5 +1,6 @@
|
||||
{- |
|
||||
Module : XMonad.Actions.WindowGo
|
||||
Description : Operations for raising (traveling to) windows.
|
||||
License : Public domain
|
||||
|
||||
Maintainer : <gwern0@gmail.com>
|
||||
@@ -36,10 +37,8 @@ module XMonad.Actions.WindowGo (
|
||||
module XMonad.ManageHook
|
||||
) where
|
||||
|
||||
import Control.Monad
|
||||
import Data.Char (toLower)
|
||||
import qualified Data.List as L (nub,sortBy)
|
||||
import Data.Monoid
|
||||
import XMonad.Prelude
|
||||
import XMonad (Query(), X(), ManageHook, WindowSet, withWindowSet, runQuery, liftIO, ask)
|
||||
import Graphics.X11 (Window)
|
||||
import XMonad.ManageHook
|
||||
|
@@ -1,6 +1,7 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.WindowMenu
|
||||
-- Description : Display window management actions in the center of the focused window.
|
||||
-- Copyright : (c) Jan Vornberger 2009
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -29,7 +30,7 @@ import qualified XMonad.StackSet as W
|
||||
import XMonad.Actions.GridSelect
|
||||
import XMonad.Layout.Maximize
|
||||
import XMonad.Actions.Minimize
|
||||
import XMonad.Util.XUtils (fi)
|
||||
import XMonad.Prelude (fi)
|
||||
|
||||
-- $usage
|
||||
--
|
||||
@@ -68,7 +69,7 @@ windowMenu = withFocused $ \w -> do
|
||||
| tag <- tags ]
|
||||
runSelectedAction gsConfig actions
|
||||
|
||||
getSize :: Window -> X (Rectangle)
|
||||
getSize :: Window -> X Rectangle
|
||||
getSize w = do
|
||||
d <- asks display
|
||||
wa <- io $ getWindowAttributes d w
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.WindowNavigation
|
||||
-- Description : Experimental rewrite of "XMonad.Layout.WindowNavigation".
|
||||
-- Copyright : (c) 2007 David Roundy <droundy@darcs.net>,
|
||||
-- Devin Mullins <me@twifkak.com>
|
||||
-- Maintainer : Devin Mullins <me@twifkak.com>
|
||||
@@ -40,17 +41,14 @@ module XMonad.Actions.WindowNavigation (
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude (catMaybes, fromMaybe, listToMaybe, sortOn)
|
||||
import XMonad.Util.Types (Direction2D(..))
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import Control.Applicative ((<$>))
|
||||
import Control.Arrow (second)
|
||||
import Data.IORef
|
||||
import Data.List (sortBy)
|
||||
import Data.Map (Map())
|
||||
import qualified Data.Map as M
|
||||
import Data.Maybe (catMaybes, fromMaybe, listToMaybe)
|
||||
import Data.Ord (comparing)
|
||||
import qualified Data.Set as S
|
||||
|
||||
-- $usage
|
||||
@@ -65,6 +63,11 @@ import qualified Data.Set as S
|
||||
-- > $ def { ... }
|
||||
-- > xmonad config
|
||||
--
|
||||
-- Or, for the brave souls:
|
||||
--
|
||||
-- > main = xmonad =<< withWindowNavigation (xK_w, xK_a, xK_s, xK_d)
|
||||
-- > $ def { ... }
|
||||
--
|
||||
-- Here, we pass in the keys for navigation in counter-clockwise order from up.
|
||||
-- It creates keybindings for @modMask@ to move to window, and @modMask .|. shiftMask@
|
||||
-- to swap windows.
|
||||
@@ -125,9 +128,12 @@ swap = withTargetWindow swapWithFocused
|
||||
mapWindows (swapWin currentWin targetWin) winSet
|
||||
Nothing -> winSet
|
||||
mapWindows f ss = W.mapWorkspace (mapWindows' f) ss
|
||||
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)
|
||||
swapWin win1 win2 win = if win == win1 then win2 else if win == win2 then win1 else win
|
||||
swapWin win1 win2 win
|
||||
| win == win1 = win2
|
||||
| win == win2 = win1
|
||||
| otherwise = win
|
||||
|
||||
withTargetWindow :: (Window -> WindowSet -> WindowSet) -> IORef WNState -> Direction2D -> X ()
|
||||
withTargetWindow adj posRef dir = fromCurrentPoint posRef $ \win pos -> do
|
||||
@@ -137,11 +143,11 @@ withTargetWindow adj posRef dir = fromCurrentPoint posRef $ \win pos -> do
|
||||
setPosition posRef pos targetRect
|
||||
|
||||
trackMovement :: IORef WNState -> X ()
|
||||
trackMovement posRef = fromCurrentPoint posRef $ \win pos -> do
|
||||
trackMovement posRef = fromCurrentPoint posRef $ \win pos ->
|
||||
windowRect win >>= flip whenJust (setPosition posRef pos . snd)
|
||||
|
||||
fromCurrentPoint :: IORef WNState -> (Window -> Point -> X ()) -> X ()
|
||||
fromCurrentPoint posRef f = withFocused $ \win -> do
|
||||
fromCurrentPoint posRef f = withFocused $ \win ->
|
||||
currentPosition posRef >>= f win
|
||||
|
||||
-- Gets the current position from the IORef passed in, or if nothing (say, from
|
||||
@@ -193,7 +199,7 @@ windowRects = fmap catMaybes . mapM windowRect . S.toList =<< gets mapped
|
||||
windowRect :: Window -> X (Maybe (Window, Rectangle))
|
||||
windowRect win = withDisplay $ \dpy -> do
|
||||
(_, x, y, w, h, bw, _) <- io $ getGeometry dpy win
|
||||
return $ Just $ (win, Rectangle x y (w + 2 * bw) (h + 2 * bw))
|
||||
return $ Just (win, Rectangle x y (w + 2 * bw) (h + 2 * bw))
|
||||
`catchX` return Nothing
|
||||
|
||||
-- Modified from droundy's implementation of WindowNavigation:
|
||||
@@ -209,7 +215,7 @@ 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)]
|
||||
sortby D = sortBy $ comparing (rect_y . snd)
|
||||
sortby R = sortBy $ comparing (rect_x . snd)
|
||||
sortby D = sortOn (rect_y . snd)
|
||||
sortby R = sortOn (rect_x . snd)
|
||||
sortby U = reverse . sortby D
|
||||
sortby L = reverse . sortby R
|
||||
|
@@ -1,21 +1,23 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.WithAll
|
||||
-- Description : Perform a given action on all or certain groups of windows.
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Provides functions for performing a given action on all windows of
|
||||
-- the current workspace.
|
||||
-- Provides functions for performing a given action on all or certain
|
||||
-- groups of windows on the current workspace.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.WithAll (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
sinkAll, withAll,
|
||||
withAll', killAll) where
|
||||
withAll', killAll,
|
||||
killOthers) where
|
||||
|
||||
import Data.Foldable hiding (foldr)
|
||||
import XMonad.Prelude hiding (foldr)
|
||||
|
||||
import XMonad
|
||||
import XMonad.StackSet
|
||||
@@ -49,4 +51,8 @@ withAll f = withWindowSet $ \ws -> let all' = integrate' . stack . workspace . c
|
||||
|
||||
-- | Kill all the windows on the current workspace.
|
||||
killAll :: X()
|
||||
killAll = withAll killWindow
|
||||
killAll = withAll killWindow
|
||||
|
||||
-- | Kill all the unfocused windows on the current workspace.
|
||||
killOthers :: X ()
|
||||
killOthers = withUnfocused killWindow
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.Workscreen
|
||||
-- Description: Display a set of workspaces on several screens.
|
||||
-- Copyright : (c) 2012 kedals0
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -20,7 +21,6 @@
|
||||
-- This also permits to see all workspaces of a workscreen even if just
|
||||
-- one screen is present, and to move windows from workspace to workscreen.
|
||||
-----------------------------------------------------------------------------
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
|
||||
module XMonad.Actions.Workscreen (
|
||||
-- * Usage
|
||||
@@ -58,23 +58,23 @@ import XMonad.Actions.OnScreen
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
|
||||
data Workscreen = Workscreen{workscreenId::Int,workspaces::[WorkspaceId]} deriving (Show,Typeable)
|
||||
data Workscreen = Workscreen{workscreenId::Int,workspaces::[WorkspaceId]} deriving (Show)
|
||||
type WorkscreenId=Int
|
||||
|
||||
data WorkscreenStorage = WorkscreenStorage WorkscreenId [Workscreen] deriving (Show,Typeable)
|
||||
data WorkscreenStorage = WorkscreenStorage WorkscreenId [Workscreen] deriving (Show)
|
||||
instance ExtensionClass WorkscreenStorage where
|
||||
initialValue = WorkscreenStorage 0 []
|
||||
|
||||
-- | Helper to group workspaces. Multiply workspace by screens number.
|
||||
expandWorkspace :: Int -> [WorkspaceId] -> [WorkspaceId]
|
||||
expandWorkspace nscr ws = concat $ map expandId ws
|
||||
expandWorkspace nscr = concatMap expandId
|
||||
where expandId wsId = let t = wsId ++ "_"
|
||||
in map ((++) t . show ) [1..nscr]
|
||||
|
||||
-- | Create workscreen list from workspace list. Group workspaces to
|
||||
-- packets of screens number size.
|
||||
fromWorkspace :: Int -> [WorkspaceId] -> [Workscreen]
|
||||
fromWorkspace n ws = map (\(a,b) -> Workscreen a b) $ zip [0..] (fromWorkspace' n ws)
|
||||
fromWorkspace n ws = zipWith Workscreen [0..] (fromWorkspace' n ws)
|
||||
fromWorkspace' :: Int -> [WorkspaceId] -> [[WorkspaceId]]
|
||||
fromWorkspace' _ [] = []
|
||||
fromWorkspace' n ws = take n ws : fromWorkspace' n (drop n ws)
|
||||
|
@@ -1,7 +1,8 @@
|
||||
{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses #-}
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.WorkspaceCursors
|
||||
-- Description : Like "XMonad.Actions.Plane" for an arbitrary number of dimensions.
|
||||
-- Copyright : (c) 2009 Adam Vogt <vogt.adam@gmail.com>
|
||||
-- License : BSD
|
||||
--
|
||||
@@ -46,14 +47,10 @@ import qualified XMonad.StackSet as W
|
||||
import XMonad.Actions.FocusNth(focusNth')
|
||||
import XMonad.Layout.LayoutModifier(ModifiedLayout(..),
|
||||
LayoutModifier(handleMess, redoLayout))
|
||||
import XMonad(Typeable, Message, WorkspaceId, X, XState(windowset),
|
||||
import XMonad(Message, WorkspaceId, X, XState(windowset),
|
||||
fromMessage, sendMessage, windows, gets)
|
||||
import Control.Monad((<=<), guard, liftM, liftM2, when)
|
||||
import Control.Applicative((<$>))
|
||||
import Data.Foldable(Foldable(foldMap), toList)
|
||||
import Data.Maybe(fromJust, listToMaybe)
|
||||
import Data.Monoid(Monoid(mappend, mconcat))
|
||||
import Data.Traversable(sequenceA)
|
||||
import XMonad.Util.Stack (reverseS)
|
||||
import XMonad.Prelude (find, fromJust, guard, liftA2, toList, when, (<=<))
|
||||
|
||||
-- $usage
|
||||
--
|
||||
@@ -61,13 +58,10 @@ import Data.Traversable(sequenceA)
|
||||
--
|
||||
-- > import XMonad
|
||||
-- > import XMonad.Actions.WorkspaceCursors
|
||||
-- > import XMonad.Hooks.DynamicLog
|
||||
-- > import XMonad.Util.EZConfig
|
||||
-- > import qualified XMonad.StackSet as W
|
||||
-- >
|
||||
-- > main = do
|
||||
-- > x <- xmobar conf
|
||||
-- > xmonad x
|
||||
-- > main = xmonad conf
|
||||
-- >
|
||||
-- > conf = additionalKeysP def
|
||||
-- > { layoutHook = workspaceCursors myCursors $ layoutHook def
|
||||
@@ -92,7 +86,8 @@ import Data.Traversable(sequenceA)
|
||||
-- workspaces. Or change it such that workspaces are created when you try to
|
||||
-- view it.
|
||||
--
|
||||
-- * Function for pretty printing for DynamicLog that groups workspaces by
|
||||
-- * Function for pretty printing for "XMonad.Hooks.StatusBar.PP" that groups
|
||||
-- workspaces by
|
||||
-- common prefixes
|
||||
--
|
||||
-- * Examples of adding workspaces to the cursors, having them appear multiple
|
||||
@@ -118,7 +113,7 @@ end = Cons . fromJust . W.differentiate . map End
|
||||
|
||||
data Cursors a
|
||||
= Cons (W.Stack (Cursors a))
|
||||
| End a deriving (Eq,Show,Read,Typeable)
|
||||
| End a deriving (Eq,Show,Read)
|
||||
|
||||
instance Foldable Cursors where
|
||||
foldMap f (End x) = f x
|
||||
@@ -144,7 +139,7 @@ getFocus (End x) = x
|
||||
|
||||
-- This could be made more efficient, if the fact that the suffixes are grouped
|
||||
focusTo :: (Eq t) => t -> Cursors t -> Maybe (Cursors t)
|
||||
focusTo x = listToMaybe . filter ((x==) . getFocus) . changeFocus (const True)
|
||||
focusTo x = find ((x==) . getFocus) . changeFocus (const True)
|
||||
|
||||
-- | non-wrapping version of 'W.focusUp''
|
||||
noWrapUp :: W.Stack t -> W.Stack t
|
||||
@@ -153,20 +148,19 @@ noWrapUp x@(W.Stack _ [] _ ) = x
|
||||
|
||||
-- | non-wrapping version of 'W.focusDown''
|
||||
noWrapDown :: W.Stack t -> W.Stack t
|
||||
noWrapDown = reverseStack . noWrapUp . reverseStack
|
||||
where reverseStack (W.Stack t ls rs) = W.Stack t rs ls
|
||||
noWrapDown = reverseS . noWrapUp . reverseS
|
||||
|
||||
focusDepth :: Cursors t -> Int
|
||||
focusDepth (Cons x) = 1 + focusDepth (W.focus x)
|
||||
focusDepth (End _) = 0
|
||||
|
||||
descend :: Monad m =>(W.Stack (Cursors a) -> m (W.Stack (Cursors a)))-> Int-> Cursors a-> m (Cursors a)
|
||||
descend f 1 (Cons x) = Cons `liftM` f x
|
||||
descend f n (Cons x) | n > 1 = liftM Cons $ descend f (pred n) `onFocus` x
|
||||
descend f 1 (Cons x) = Cons <$> f x
|
||||
descend f n (Cons x) | n > 1 = fmap Cons $ descend f (pred n) `onFocus` x
|
||||
descend _ _ x = return x
|
||||
|
||||
onFocus :: (Monad m) => (a1 -> m a1) -> W.Stack a1 -> m (W.Stack a1)
|
||||
onFocus f st = (\x -> st { W.focus = x}) `liftM` f (W.focus st)
|
||||
onFocus f st = (\x -> st { W.focus = x}) <$> f (W.focus st)
|
||||
|
||||
-- | @modifyLayer@ is used to change the focus at a given depth
|
||||
modifyLayer :: (W.Stack (Cursors String) -> W.Stack (Cursors String)) -> Int -> X ()
|
||||
@@ -192,10 +186,10 @@ modifyLayer' :: (W.Stack (Cursors String) -> X (W.Stack (Cursors String))) -> In
|
||||
modifyLayer' f depth = modifyCursors (descend f depth)
|
||||
|
||||
modifyCursors :: (Cursors String -> X (Cursors String)) -> X ()
|
||||
modifyCursors = sendMessage . ChangeCursors . (liftM2 (>>) updateXMD return <=<)
|
||||
modifyCursors = sendMessage . ChangeCursors . (liftA2 (>>) updateXMD return <=<)
|
||||
|
||||
data WorkspaceCursors a = WorkspaceCursors (Cursors String)
|
||||
deriving (Typeable,Read,Show)
|
||||
newtype WorkspaceCursors a = WorkspaceCursors (Cursors String)
|
||||
deriving (Read,Show)
|
||||
|
||||
-- | The state is stored in the 'WorkspaceCursors' layout modifier. Put this as
|
||||
-- your outermost modifier, unless you want different cursors at different
|
||||
@@ -203,8 +197,7 @@ data WorkspaceCursors a = WorkspaceCursors (Cursors String)
|
||||
workspaceCursors :: Cursors String -> l a -> ModifiedLayout WorkspaceCursors l a
|
||||
workspaceCursors = ModifiedLayout . WorkspaceCursors
|
||||
|
||||
data ChangeCursors = ChangeCursors { unWrap :: Cursors String -> X (Cursors String) }
|
||||
deriving (Typeable)
|
||||
newtype ChangeCursors = ChangeCursors { unWrap :: Cursors String -> X (Cursors String) }
|
||||
|
||||
instance Message ChangeCursors
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.WorkspaceNames
|
||||
-- Description : Persistently rename workspace and swap them along with their names.
|
||||
-- Copyright : (c) Tomas Janousek <tomi@nomi.cz>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -8,22 +9,19 @@
|
||||
-- Stability : experimental
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Provides bindings to rename workspaces, show these names in DynamicLog and
|
||||
-- Provides bindings to rename workspaces, show these names in a status bar and
|
||||
-- swap workspaces along with their names. These names survive restart.
|
||||
-- Together with "XMonad.Layout.WorkspaceDir" this provides for a fully
|
||||
-- dynamic topic space workflow.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
|
||||
module XMonad.Actions.WorkspaceNames (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
|
||||
-- * Workspace naming
|
||||
renameWorkspace,
|
||||
workspaceNamesPP,
|
||||
getWorkspaceNames',
|
||||
getWorkspaceNames,
|
||||
getWorkspaceName,
|
||||
@@ -37,23 +35,27 @@ module XMonad.Actions.WorkspaceNames (
|
||||
swapWithCurrent,
|
||||
|
||||
-- * Workspace prompt
|
||||
workspaceNamePrompt
|
||||
workspaceNamePrompt,
|
||||
|
||||
-- * StatusBar, EwmhDesktops integration
|
||||
workspaceNamesPP,
|
||||
workspaceNamesEwmh,
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Prelude (fromMaybe, isInfixOf, (<&>), (>=>))
|
||||
import qualified XMonad.StackSet as W
|
||||
import qualified XMonad.Util.ExtensibleState as XS
|
||||
|
||||
import XMonad.Actions.CycleWS (findWorkspace, WSType(..), Direction1D(..))
|
||||
import XMonad.Actions.CycleWS (findWorkspace, WSType(..), Direction1D(..), anyWS)
|
||||
import qualified XMonad.Actions.SwapWorkspaces as Swap
|
||||
import XMonad.Hooks.DynamicLog (PP(..))
|
||||
import XMonad.Hooks.StatusBar.PP (PP(..))
|
||||
import XMonad.Hooks.EwmhDesktops (addEwmhWorkspaceRename)
|
||||
import XMonad.Prompt (mkXPrompt, XPConfig)
|
||||
import XMonad.Prompt.Workspace (Wor(Wor))
|
||||
import XMonad.Util.WorkspaceCompare (getSortByIndex)
|
||||
|
||||
import qualified Data.Map as M
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.List (isInfixOf)
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
|
||||
@@ -64,10 +66,17 @@ import Data.List (isInfixOf)
|
||||
--
|
||||
-- > , ((modm .|. shiftMask, xK_r ), renameWorkspace def)
|
||||
--
|
||||
-- and apply workspaceNamesPP to your DynamicLog pretty-printer:
|
||||
-- and apply workspaceNamesPP to your pretty-printer:
|
||||
--
|
||||
-- > myLogHook =
|
||||
-- > workspaceNamesPP xmobarPP >>= dynamicLogString >>= xmonadPropLog
|
||||
-- > myPP = workspaceNamesPP xmobarPP
|
||||
--
|
||||
-- Check "XMonad.Hooks.StatusBar" for more information on how to incorprate
|
||||
-- this into your status bar.
|
||||
--
|
||||
-- To expose workspace names to pagers and other EWMH clients, integrate this
|
||||
-- with "XMonad.Hooks.EwmhDesktops":
|
||||
--
|
||||
-- > main = xmonad $ … . workspaceNamesEwmh . ewmh . … $ def{…}
|
||||
--
|
||||
-- We also provide a modification of "XMonad.Actions.SwapWorkspaces"\'s
|
||||
-- functionality, which may be used this way:
|
||||
@@ -85,7 +94,7 @@ import Data.List (isInfixOf)
|
||||
|
||||
-- | Workspace names container.
|
||||
newtype WorkspaceNames = WorkspaceNames (M.Map WorkspaceId String)
|
||||
deriving (Typeable, Read, Show)
|
||||
deriving (Read, Show)
|
||||
|
||||
instance ExtensionClass WorkspaceNames where
|
||||
initialValue = WorkspaceNames M.empty
|
||||
@@ -97,21 +106,20 @@ getWorkspaceNames' = do
|
||||
WorkspaceNames m <- XS.get
|
||||
return (`M.lookup` m)
|
||||
|
||||
-- | Returns a function that maps workspace tag @\"t\"@ to @\"t:name\"@ for
|
||||
-- workspaces with a name, and to @\"t\"@ otherwise.
|
||||
getWorkspaceNames :: X (WorkspaceId -> String)
|
||||
getWorkspaceNames = do
|
||||
lookup <- getWorkspaceNames'
|
||||
return $ \wks -> wks ++ maybe "" (':' :) (lookup wks)
|
||||
-- | Returns a function for 'ppRename' that appends @sep@ and the workspace
|
||||
-- name, if set.
|
||||
getWorkspaceNames :: String -> X (String -> WindowSpace -> String)
|
||||
getWorkspaceNames sep = ren <$> getWorkspaceNames'
|
||||
where
|
||||
ren name s w = s ++ maybe "" (sep ++) (name (W.tag w))
|
||||
|
||||
-- | Gets the name of a workspace, if set, otherwise returns nothing.
|
||||
getWorkspaceName :: WorkspaceId -> X (Maybe String)
|
||||
getWorkspaceName w = ($ w) `fmap` getWorkspaceNames'
|
||||
getWorkspaceName w = ($ w) <$> getWorkspaceNames'
|
||||
|
||||
-- | Gets the name of the current workspace. See 'getWorkspaceName'
|
||||
getCurrentWorkspaceName :: X (Maybe String)
|
||||
getCurrentWorkspaceName = do
|
||||
getWorkspaceName =<< gets (W.currentTag . windowset)
|
||||
getCurrentWorkspaceName = getWorkspaceName =<< gets (W.currentTag . windowset)
|
||||
|
||||
-- | Sets the name of a workspace. Empty string makes the workspace unnamed
|
||||
-- again.
|
||||
@@ -129,27 +137,13 @@ setCurrentWorkspaceName name = do
|
||||
|
||||
-- | Prompt for a new name for the current workspace and set it.
|
||||
renameWorkspace :: XPConfig -> X ()
|
||||
renameWorkspace conf = do
|
||||
renameWorkspace conf =
|
||||
mkXPrompt pr conf (const (return [])) setCurrentWorkspaceName
|
||||
where pr = Wor "Workspace name: "
|
||||
|
||||
-- | Modify "XMonad.Hooks.DynamicLog"\'s pretty-printing format to show
|
||||
-- workspace names as well.
|
||||
workspaceNamesPP :: PP -> X PP
|
||||
workspaceNamesPP pp = do
|
||||
names <- getWorkspaceNames
|
||||
return $
|
||||
pp {
|
||||
ppCurrent = ppCurrent pp . names,
|
||||
ppVisible = ppVisible pp . names,
|
||||
ppHidden = ppHidden pp . names,
|
||||
ppHiddenNoWindows = ppHiddenNoWindows pp . names,
|
||||
ppUrgent = ppUrgent pp . names
|
||||
}
|
||||
|
||||
-- | See 'XMonad.Actions.SwapWorkspaces.swapTo'. This is the same with names.
|
||||
swapTo :: Direction1D -> X ()
|
||||
swapTo dir = swapTo' dir AnyWS
|
||||
swapTo dir = swapTo' dir anyWS
|
||||
|
||||
-- | Swap with the previous or next workspace of the given type.
|
||||
swapTo' :: Direction1D -> WSType -> X ()
|
||||
@@ -169,19 +163,27 @@ swapNames w1 w2 = do
|
||||
WorkspaceNames m <- XS.get
|
||||
let getname w = fromMaybe "" $ M.lookup w m
|
||||
set w name m' = if null name then M.delete w m' else M.insert w name m'
|
||||
XS.put $ WorkspaceNames $ set w1 (getname w2) $ set w2 (getname w1) $ m
|
||||
XS.put $ WorkspaceNames $ set w1 (getname w2) $ set w2 (getname w1) m
|
||||
|
||||
-- | Same behavior than 'XMonad.Prompt.Workspace.workspacePrompt' excepted it acts on the workspace name provided by this module.
|
||||
workspaceNamePrompt :: XPConfig -> (String -> X ()) -> X ()
|
||||
workspaceNamePrompt :: XPConfig -> (WorkspaceId -> X ()) -> X ()
|
||||
workspaceNamePrompt conf job = do
|
||||
myWorkspaces <- gets $ map W.tag . W.workspaces . windowset
|
||||
myWorkspacesName <- getWorkspaceNames >>= \f -> return $ map f myWorkspaces
|
||||
let pairs = zip myWorkspacesName myWorkspaces
|
||||
myWorkspaces <- gets $ W.workspaces . windowset
|
||||
myWorkspacesName <- getWorkspaceNames ":" <&> \n -> [n (W.tag w) w | w <- myWorkspaces]
|
||||
let pairs = zip myWorkspacesName (map W.tag myWorkspaces)
|
||||
mkXPrompt (Wor "Select workspace: ") conf
|
||||
(contains myWorkspacesName)
|
||||
(job . toWsId pairs)
|
||||
where toWsId pairs name = case lookup name pairs of
|
||||
Nothing -> ""
|
||||
Just i -> i
|
||||
where toWsId pairs name = fromMaybe "" (lookup name pairs)
|
||||
contains completions input =
|
||||
return $ filter (Data.List.isInfixOf input) completions
|
||||
return $ filter (isInfixOf input) completions
|
||||
|
||||
-- | Modify 'XMonad.Hooks.StatusBar.PP.PP'\'s pretty-printing format to show
|
||||
-- workspace names as well.
|
||||
workspaceNamesPP :: PP -> X PP
|
||||
workspaceNamesPP pp = getWorkspaceNames ":" <&> \ren -> pp{ ppRename = ppRename pp >=> ren }
|
||||
|
||||
-- | Tell "XMonad.Hooks.EwmhDesktops" to append workspace names to desktop
|
||||
-- names.
|
||||
workspaceNamesEwmh :: XConfig l -> XConfig l
|
||||
workspaceNamesEwmh = addEwmhWorkspaceRename $ getWorkspaceNames ":"
|
||||
|
@@ -3,6 +3,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Arossato
|
||||
-- Description : Andrea Rossato's xmonad configuration.
|
||||
-- Copyright : (c) Andrea Rossato 2007
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -22,7 +23,7 @@ module XMonad.Config.Arossato
|
||||
|
||||
import qualified Data.Map as M
|
||||
|
||||
import XMonad hiding ( (|||) )
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import XMonad.Actions.CycleWS
|
||||
@@ -30,7 +31,6 @@ import XMonad.Hooks.DynamicLog hiding (xmobar)
|
||||
import XMonad.Hooks.ManageDocks
|
||||
import XMonad.Hooks.ServerMode
|
||||
import XMonad.Layout.Accordion
|
||||
import XMonad.Layout.LayoutCombinators
|
||||
import XMonad.Layout.Magnifier
|
||||
import XMonad.Layout.NoBorders
|
||||
import XMonad.Layout.SimpleFloat
|
||||
@@ -147,8 +147,8 @@ arossatoConfig = do
|
||||
, ((modMask x , xK_F3 ), shellPrompt def )
|
||||
, ((modMask x , xK_F4 ), sshPrompt def )
|
||||
, ((modMask x , xK_F5 ), themePrompt def )
|
||||
, ((modMask x , xK_F6 ), windowPromptGoto def )
|
||||
, ((modMask x , xK_F7 ), windowPromptBring def )
|
||||
, ((modMask x , xK_F6 ), windowPrompt def Goto allWindows )
|
||||
, ((modMask x , xK_F7 ), windowPrompt def Bring allWindows )
|
||||
, ((modMask x , xK_comma ), prevWS )
|
||||
, ((modMask x , xK_period), nextWS )
|
||||
, ((modMask x , xK_Right ), windows W.focusDown )
|
||||
|
@@ -3,6 +3,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Azerty
|
||||
-- Description : Fix some keybindings for users of French keyboard layouts.
|
||||
-- Copyright : (c) Devin Mullins <me@twifkak.com>
|
||||
-- License : BSD
|
||||
--
|
||||
@@ -46,7 +47,7 @@ azertyKeys = azertyKeysTop [0x26,0xe9,0x22,0x27,0x28,0x2d,0xe8,0x5f,0xe7,0xe0]
|
||||
|
||||
belgianKeys = azertyKeysTop [0x26,0xe9,0x22,0x27,0x28,0xa7,0xe8,0x21,0xe7,0xe0]
|
||||
|
||||
azertyKeysTop topRow conf@(XConfig {modMask = modm}) = M.fromList $
|
||||
azertyKeysTop topRow conf@XConfig{modMask = modm} = M.fromList $
|
||||
[((modm, xK_semicolon), sendMessage (IncMasterN (-1)))]
|
||||
++
|
||||
[((m .|. modm, k), windows $ f i)
|
||||
|
@@ -3,6 +3,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Bepo
|
||||
-- Description : Fix keybindings for the BEPO keyboard layout.
|
||||
-- Copyright : (c) Yorick Laupa <yo.eight@gmail.com>
|
||||
-- License : BSD
|
||||
--
|
||||
@@ -39,9 +40,8 @@ import qualified Data.Map as M
|
||||
|
||||
bepoConfig = def { keys = bepoKeys <+> keys def }
|
||||
|
||||
bepoKeys conf@(XConfig { modMask = modm }) = M.fromList $
|
||||
[((modm, xK_semicolon), sendMessage (IncMasterN (-1)))]
|
||||
++
|
||||
[((m .|. modm, k), windows $ f i)
|
||||
| (i, k) <- zip (workspaces conf) [0x22,0xab,0xbb,0x28,0x29,0x40,0x2b,0x2d,0x2f,0x2a],
|
||||
(f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
|
||||
bepoKeys conf@XConfig { modMask = modm } = M.fromList $
|
||||
((modm, xK_semicolon), sendMessage (IncMasterN (-1)))
|
||||
: [((m .|. modm, k), windows $ f i)
|
||||
| (i, k) <- zip (workspaces conf) [0x22,0xab,0xbb,0x28,0x29,0x40,0x2b,0x2d,0x2f,0x2a],
|
||||
(f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
|
||||
|
@@ -3,6 +3,7 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Bluetile
|
||||
-- Description : Default configuration of [Bluetile](http://projects.haskell.org/bluetile/).
|
||||
-- Copyright : (c) Jan Vornberger 2009
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
@@ -25,7 +26,7 @@ module XMonad.Config.Bluetile (
|
||||
bluetileConfig
|
||||
) where
|
||||
|
||||
import XMonad hiding ( (|||) )
|
||||
import XMonad
|
||||
|
||||
import XMonad.Layout.BorderResize
|
||||
import XMonad.Layout.BoringWindows
|
||||
@@ -33,7 +34,6 @@ import XMonad.Layout.ButtonDecoration
|
||||
import XMonad.Layout.Decoration
|
||||
import XMonad.Layout.DecorationAddons
|
||||
import XMonad.Layout.DraggingVisualizer
|
||||
import XMonad.Layout.LayoutCombinators
|
||||
import XMonad.Layout.Maximize
|
||||
import XMonad.Layout.Minimize
|
||||
import XMonad.Layout.MouseResizableTile
|
||||
@@ -62,8 +62,7 @@ import qualified XMonad.StackSet as W
|
||||
import qualified Data.Map as M
|
||||
|
||||
import System.Exit
|
||||
import Data.Monoid
|
||||
import Control.Monad(when)
|
||||
import XMonad.Prelude(when)
|
||||
|
||||
-- $usage
|
||||
-- To use this module, start with the following @~\/.xmonad\/xmonad.hs@:
|
||||
@@ -82,7 +81,7 @@ bluetileWorkspaces :: [String]
|
||||
bluetileWorkspaces = ["1","2","3","4","5","6","7","8","9","0"]
|
||||
|
||||
bluetileKeys :: XConfig Layout -> M.Map (KeyMask, KeySym) (X ())
|
||||
bluetileKeys conf@(XConfig {XMonad.modMask = modMask'}) = M.fromList $
|
||||
bluetileKeys conf@XConfig{XMonad.modMask = modMask'} = M.fromList $
|
||||
-- launching and killing programs
|
||||
[ ((modMask' , xK_Return), spawn $ XMonad.terminal conf) -- %! Launch terminal
|
||||
, ((modMask', xK_p ), gnomeRun) -- %! Launch Gnome "Run application" dialog
|
||||
@@ -113,14 +112,14 @@ bluetileKeys conf@(XConfig {XMonad.modMask = modMask'}) = M.fromList $
|
||||
|
||||
-- floating layer support
|
||||
, ((modMask', xK_t ), withFocused $ windows . W.sink) -- %! Push window back into tiling
|
||||
, ((modMask' .|. shiftMask, xK_t ), withFocused $ float ) -- %! Float window
|
||||
, ((modMask' .|. shiftMask, xK_t ), withFocused float ) -- %! Float window
|
||||
|
||||
-- increase or decrease number of windows in the master area
|
||||
, ((modMask' , xK_comma ), sendMessage (IncMasterN 1)) -- %! Increment the number of windows in the master area
|
||||
, ((modMask' , xK_period), sendMessage (IncMasterN (-1))) -- %! Deincrement the number of windows in the master area
|
||||
|
||||
-- quit, or restart
|
||||
, ((modMask' .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit
|
||||
, ((modMask' .|. shiftMask, xK_q ), io exitSuccess) -- %! Quit
|
||||
, ((modMask' , xK_q ), restart "xmonad" True) -- %! Restart
|
||||
|
||||
-- Metacity-like workspace switching
|
||||
@@ -160,19 +159,19 @@ bluetileKeys conf@(XConfig {XMonad.modMask = modMask'}) = M.fromList $
|
||||
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
|
||||
|
||||
bluetileMouseBindings :: XConfig Layout -> M.Map (KeyMask, Button) (Window -> X ())
|
||||
bluetileMouseBindings (XConfig {XMonad.modMask = modMask'}) = M.fromList $
|
||||
bluetileMouseBindings XConfig{XMonad.modMask = modMask'} = M.fromList
|
||||
-- mod-button1 %! Move a floated window by dragging
|
||||
[ ((modMask', button1), (\w -> isFloating w >>= \isF -> when (isF) $
|
||||
focus w >> mouseMoveWindow w >> windows W.shiftMaster))
|
||||
[ ((modMask', button1), \w -> isFloating w >>= \isF -> when isF $
|
||||
focus w >> mouseMoveWindow w >> windows W.shiftMaster)
|
||||
-- mod-button2 %! Switch to next and first layout
|
||||
, ((modMask', button2), (\_ -> sendMessage NextLayout))
|
||||
, ((modMask' .|. shiftMask, button2), (\_ -> sendMessage $ JumpToLayout "Floating"))
|
||||
, ((modMask', button2), \_ -> sendMessage NextLayout)
|
||||
, ((modMask' .|. shiftMask, button2), \_ -> sendMessage $ JumpToLayout "Floating")
|
||||
-- mod-button3 %! Resize a floated window by dragging
|
||||
, ((modMask', button3), (\w -> isFloating w >>= \isF -> when (isF) $
|
||||
focus w >> mouseResizeWindow w >> windows W.shiftMaster))
|
||||
, ((modMask', button3), \w -> isFloating w >>= \isF -> when isF $
|
||||
focus w >> mouseResizeWindow w >> windows W.shiftMaster)
|
||||
]
|
||||
|
||||
isFloating :: Window -> X (Bool)
|
||||
isFloating :: Window -> X Bool
|
||||
isFloating w = do
|
||||
ws <- gets windowset
|
||||
return $ M.member w (W.floating ws)
|
||||
@@ -183,31 +182,28 @@ bluetileManageHook = composeAll
|
||||
, className =? "MPlayer" --> doFloat
|
||||
, isFullscreen --> doFullFloat]
|
||||
|
||||
bluetileLayoutHook = avoidStruts $ minimize $ boringWindows $ (
|
||||
bluetileLayoutHook = avoidStruts $ minimize $ boringWindows $
|
||||
named "Floating" floating |||
|
||||
named "Tiled1" tiled1 |||
|
||||
named "Tiled2" tiled2 |||
|
||||
named "Fullscreen" fullscreen
|
||||
)
|
||||
where
|
||||
floating = floatingDeco $ maximize $ borderResize $ positionStoreFloat
|
||||
tiled1 = tilingDeco $ maximize $ mouseResizableTileMirrored
|
||||
tiled2 = tilingDeco $ maximize $ mouseResizableTile
|
||||
floating = floatingDeco $ maximize $ borderResize positionStoreFloat
|
||||
tiled1 = tilingDeco $ maximize mouseResizableTileMirrored
|
||||
tiled2 = tilingDeco $ maximize mouseResizableTile
|
||||
fullscreen = tilingDeco $ maximize $ smartBorders Full
|
||||
|
||||
tilingDeco l = windowSwitcherDecorationWithButtons shrinkText defaultThemeWithButtons (draggingVisualizer l)
|
||||
floatingDeco l = buttonDeco shrinkText defaultThemeWithButtons l
|
||||
|
||||
bluetileConfig =
|
||||
docks $
|
||||
docks . ewmhFullscreen . ewmh $
|
||||
def
|
||||
{ modMask = mod4Mask, -- logo key
|
||||
manageHook = bluetileManageHook,
|
||||
layoutHook = bluetileLayoutHook,
|
||||
logHook = currentWorkspaceOnTop >> ewmhDesktopsLogHook,
|
||||
handleEventHook = ewmhDesktopsEventHook
|
||||
`mappend` fullscreenEventHook
|
||||
`mappend` minimizeEventHook
|
||||
logHook = currentWorkspaceOnTop,
|
||||
handleEventHook = minimizeEventHook
|
||||
`mappend` serverModeEventHook' bluetileCommands
|
||||
`mappend` positionStoreEventHook,
|
||||
workspaces = bluetileWorkspaces,
|
||||
|
@@ -3,6 +3,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Desktop
|
||||
-- Description : Core settings for interfacing with desktop environments.
|
||||
-- Copyright : (c) Spencer Janssen <spencerjanssen@gmail.com>
|
||||
-- License : BSD
|
||||
--
|
||||
@@ -57,6 +58,7 @@ module XMonad.Config.Desktop (
|
||||
import XMonad
|
||||
import XMonad.Hooks.ManageDocks
|
||||
import XMonad.Hooks.EwmhDesktops
|
||||
import XMonad.Layout.LayoutModifier (ModifiedLayout)
|
||||
import XMonad.Util.Cursor
|
||||
|
||||
import qualified Data.Map as M
|
||||
@@ -164,13 +166,16 @@ import qualified Data.Map as M
|
||||
-- > adjustEventInput
|
||||
--
|
||||
|
||||
desktopConfig :: XConfig (ModifiedLayout AvoidStruts
|
||||
(Choose Tall (Choose (Mirror Tall) Full)))
|
||||
desktopConfig = docks $ ewmh def
|
||||
{ startupHook = setDefaultCursor xC_left_ptr <+> startupHook def
|
||||
, layoutHook = desktopLayoutModifiers $ layoutHook def
|
||||
, keys = desktopKeys <+> keys def }
|
||||
|
||||
desktopKeys (XConfig {modMask = modm}) = M.fromList $
|
||||
desktopKeys :: XConfig l -> M.Map (KeyMask, KeySym) (X ())
|
||||
desktopKeys XConfig{modMask = modm} = M.fromList
|
||||
[ ((modm, xK_b), sendMessage ToggleStruts) ]
|
||||
|
||||
desktopLayoutModifiers layout = avoidStruts layout
|
||||
|
||||
desktopLayoutModifiers :: LayoutClass l a => l a -> ModifiedLayout AvoidStruts l a
|
||||
desktopLayoutModifiers = avoidStruts
|
||||
|
@@ -1,14 +1,16 @@
|
||||
-- boilerplate {{{
|
||||
{-# LANGUAGE ExistentialQuantification, NoMonomorphismRestriction, TypeSynonymInstances #-}
|
||||
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-type-defaults #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Dmwit
|
||||
-- Description : Daniel Wagner's xmonad configuration.
|
||||
--
|
||||
------------------------------------------------------------------------
|
||||
module XMonad.Config.Dmwit where
|
||||
|
||||
-- system imports
|
||||
import Control.Applicative
|
||||
import Control.Monad
|
||||
import Control.Monad.Trans
|
||||
import Data.Char
|
||||
import Data.List
|
||||
import Data.Map (Map, fromList)
|
||||
import Data.Ratio
|
||||
import Data.Word
|
||||
@@ -29,9 +31,10 @@ import XMonad.Hooks.DynamicLog
|
||||
import XMonad.Hooks.ManageDocks
|
||||
import XMonad.Hooks.ManageHelpers
|
||||
import XMonad.Layout.Grid
|
||||
import XMonad.Layout.IndependentScreens
|
||||
import XMonad.Layout.IndependentScreens hiding (withScreen)
|
||||
import XMonad.Layout.Magnifier
|
||||
import XMonad.Layout.NoBorders
|
||||
import XMonad.Prelude
|
||||
import XMonad.Util.Dzen hiding (x, y)
|
||||
import XMonad.Util.SpawnOnce
|
||||
-- }}}
|
||||
@@ -112,7 +115,6 @@ fullscreenMPlayer = className =? "MPlayer" --> do
|
||||
Just (16 :% 9) -> viewFullOn 1 "5" win
|
||||
_ -> doFloat
|
||||
where
|
||||
fi = fromIntegral :: Dimension -> Double
|
||||
approx (n, d) = approxRational (fi n / fi d) (1/100)
|
||||
|
||||
operationOn f s n w = do
|
||||
@@ -236,7 +238,7 @@ keyBindings conf = let m = modMask conf in fromList . anyMask $ [
|
||||
((m .|. shiftMask , xK_p ), spawnHere termLauncher),
|
||||
((m .|. shiftMask , xK_c ), kill),
|
||||
((m , xK_q ), restart "xmonad" True),
|
||||
((m .|. shiftMask , xK_q ), io (exitWith ExitSuccess)),
|
||||
((m .|. shiftMask , xK_q ), io exitSuccess),
|
||||
((m , xK_grave ), sendMessage NextLayout),
|
||||
((m .|. shiftMask , xK_grave ), setLayout $ layoutHook conf),
|
||||
((m , xK_o ), sendMessage Toggle),
|
||||
|
@@ -2,19 +2,20 @@
|
||||
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-orphans #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Droundy
|
||||
-- Description : David Roundy's xmonad config.
|
||||
-- Copyright : (c) Spencer Janssen 2007
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Config.Droundy ( config, mytab ) where
|
||||
|
||||
import XMonad hiding (keys, config, (|||))
|
||||
import XMonad hiding (keys, config)
|
||||
import qualified XMonad (keys)
|
||||
|
||||
import qualified XMonad.StackSet as W
|
||||
import qualified Data.Map as M
|
||||
import System.Exit ( exitWith, ExitCode(ExitSuccess) )
|
||||
import System.Exit ( exitSuccess )
|
||||
|
||||
import XMonad.Layout.Tabbed ( tabbed,
|
||||
shrinkText, Shrinker, shrinkIt, CustomShrink(CustomShrink) )
|
||||
@@ -39,8 +40,8 @@ import XMonad.Prompt.Shell ( shellPrompt )
|
||||
import XMonad.Actions.CopyWindow ( kill1, copy )
|
||||
import XMonad.Actions.DynamicWorkspaces ( withNthWorkspace, withWorkspace,
|
||||
selectWorkspace, renameWorkspace, removeWorkspace )
|
||||
import XMonad.Actions.CycleWS ( moveTo, WSType( HiddenNonEmptyWS ),
|
||||
Direction1D( Prev, Next) )
|
||||
import XMonad.Actions.CycleWS ( moveTo, hiddenWS, emptyWS,
|
||||
Direction1D( Prev, Next), WSType ((:&:), Not) )
|
||||
|
||||
import XMonad.Hooks.ManageDocks ( avoidStruts, docks )
|
||||
import XMonad.Hooks.EwmhDesktops ( ewmh )
|
||||
@@ -77,11 +78,11 @@ keys x = M.fromList $
|
||||
, ((modMask x, xK_t ), withFocused $ windows . W.sink) -- %! Push window back into tiling
|
||||
|
||||
-- quit, or restart
|
||||
, ((modMask x .|. shiftMask, xK_Escape), io (exitWith ExitSuccess)) -- %! Quit xmonad
|
||||
, ((modMask x .|. shiftMask, xK_Escape), io exitSuccess) -- %! Quit xmonad
|
||||
, ((modMask x , xK_Escape), restart "xmonad" True) -- %! Restart xmonad
|
||||
|
||||
, ((modMask x .|. shiftMask, xK_Right), moveTo Next HiddenNonEmptyWS)
|
||||
, ((modMask x .|. shiftMask, xK_Left), moveTo Prev HiddenNonEmptyWS)
|
||||
, ((modMask x .|. shiftMask, xK_Right), moveTo Next $ hiddenWS :&: Not emptyWS)
|
||||
, ((modMask x .|. shiftMask, xK_Left), moveTo Prev $ hiddenWS :&: Not emptyWS)
|
||||
, ((modMask x, xK_Right), sendMessage $ Go R)
|
||||
, ((modMask x, xK_Left), sendMessage $ Go L)
|
||||
, ((modMask x, xK_Up), sendMessage $ Go U)
|
||||
|
@@ -29,8 +29,9 @@ main = do
|
||||
xmonad $ desktopConfig
|
||||
{ modMask = mod4Mask -- Use the "Win" key for the mod key
|
||||
, manageHook = myManageHook <+> manageHook desktopConfig
|
||||
, layoutHook = desktopLayoutModifiers $ myLayouts
|
||||
, logHook = dynamicLogString def >>= xmonadPropLog
|
||||
, layoutHook = desktopLayoutModifiers myLayouts
|
||||
, logHook = (dynamicLogString def >>= xmonadPropLog)
|
||||
<+> logHook desktopConfig
|
||||
}
|
||||
|
||||
`additionalKeysP` -- Add some extra key bindings:
|
||||
@@ -68,11 +69,11 @@ myXPConfig = def
|
||||
-- Use the `xprop' tool to get the info you need for these matches.
|
||||
-- For className, use the second value that xprop gives you.
|
||||
myManageHook = composeOne
|
||||
[ className =? "Pidgin" -?> doFloat
|
||||
, className =? "XCalc" -?> doFloat
|
||||
, className =? "mpv" -?> doFloat
|
||||
-- Handle floating windows:
|
||||
[ transience -- move transient windows to their parent
|
||||
, isDialog -?> doCenterFloat
|
||||
|
||||
-- Move transient windows to their parent:
|
||||
, transience
|
||||
] <+> composeAll
|
||||
[ className =? "Pidgin" --> doFloat
|
||||
, className =? "XCalc" --> doFloat
|
||||
, className =? "mpv" --> doFloat
|
||||
]
|
||||
|
@@ -3,6 +3,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Gnome
|
||||
-- Description : Config for integrating xmonad with GNOME.
|
||||
-- Copyright : (c) Spencer Janssen <spencerjanssen@gmail.com>
|
||||
-- License : BSD
|
||||
--
|
||||
@@ -45,7 +46,7 @@ gnomeConfig = desktopConfig
|
||||
, keys = gnomeKeys <+> keys desktopConfig
|
||||
, startupHook = gnomeRegister >> startupHook desktopConfig }
|
||||
|
||||
gnomeKeys (XConfig {modMask = modm}) = M.fromList $
|
||||
gnomeKeys XConfig{modMask = modm} = M.fromList
|
||||
[ ((modm, xK_p), gnomeRun)
|
||||
, ((modm .|. shiftMask, xK_q), spawn "gnome-session-quit --logout") ]
|
||||
|
||||
@@ -72,7 +73,7 @@ gnomeRun = withDisplay $ \dpy -> do
|
||||
-- > gconftool-2 -s /desktop/gnome/session/required_components/windowmanager xmonad --type string
|
||||
gnomeRegister :: MonadIO m => m ()
|
||||
gnomeRegister = io $ do
|
||||
x <- lookup "DESKTOP_AUTOSTART_ID" `fmap` getEnvironment
|
||||
x <- lookup "DESKTOP_AUTOSTART_ID" <$> getEnvironment
|
||||
whenJust x $ \sessionId -> safeSpawn "dbus-send"
|
||||
["--session"
|
||||
,"--print-reply=literal"
|
||||
|
@@ -3,6 +3,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Kde
|
||||
-- Description : Config for integrating xmonad with KDE.
|
||||
-- Copyright : (c) Spencer Janssen <spencerjanssen@gmail.com>
|
||||
-- License : BSD
|
||||
--
|
||||
@@ -47,12 +48,12 @@ kde4Config = desktopConfig
|
||||
{ terminal = "konsole"
|
||||
, 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 .|. shiftMask, xK_q), spawn "dcop kdesktop default logout")
|
||||
]
|
||||
|
||||
kde4Keys (XConfig {modMask = modm}) = M.fromList $
|
||||
kde4Keys XConfig{modMask = modm} = M.fromList
|
||||
[ ((modm, xK_p), spawn "krunner")
|
||||
, ((modm .|. shiftMask, xK_q), spawn "dbus-send --print-reply --dest=org.kde.ksmserver /KSMServer org.kde.KSMServerInterface.logout int32:1 int32:0 int32:1")
|
||||
]
|
||||
|
46
XMonad/Config/LXQt.hs
Normal file
46
XMonad/Config/LXQt.hs
Normal file
@@ -0,0 +1,46 @@
|
||||
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.LXQt
|
||||
-- Description : Config for integrating xmonad with LXQt.
|
||||
-- Copyright : (c) Petr Shevtsov <petr.shevtsov@gmail.com>
|
||||
-- License : BSD
|
||||
--
|
||||
-- Maintainer : none
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- This module provides a config suitable for use with the LXQt desktop
|
||||
-- environment.
|
||||
|
||||
module XMonad.Config.LXQt (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
lxqtConfig,
|
||||
desktopLayoutModifiers
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Config.Desktop
|
||||
|
||||
import qualified Data.Map as M
|
||||
|
||||
-- $usage
|
||||
-- To use this module, start with the following @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad
|
||||
-- > import XMonad.Config.LXQt
|
||||
-- >
|
||||
-- > main = xmonad lxqtConfig
|
||||
--
|
||||
-- For example of how to further customize @lxqtConfig@ see "XMonad.Config.Desktop".
|
||||
|
||||
lxqtConfig = desktopConfig
|
||||
{ terminal = "qterminal"
|
||||
, keys = lxqtKeys <+> keys desktopConfig }
|
||||
|
||||
lxqtKeys XConfig{modMask = modm} = M.fromList
|
||||
[ ((modm, xK_p), spawn "lxqt-runner")
|
||||
, ((modm .|. shiftMask, xK_q), spawn "lxqt-leave")
|
||||
]
|
@@ -3,6 +3,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Mate
|
||||
-- Description : Config for integrating xmonad with MATE.
|
||||
-- Copyright : (c) Brandon S Allbery KF8NH, 2014
|
||||
-- License : BSD
|
||||
--
|
||||
@@ -20,13 +21,18 @@ module XMonad.Config.Mate (
|
||||
-- $usage
|
||||
mateConfig,
|
||||
mateRun,
|
||||
matePanel,
|
||||
mateRegister,
|
||||
mateLogout,
|
||||
mateShutdown,
|
||||
desktopLayoutModifiers
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Config.Desktop
|
||||
import XMonad.Util.Run (safeSpawn)
|
||||
import XMonad.Util.Ungrab
|
||||
import XMonad.Prelude (toUpper)
|
||||
|
||||
import qualified Data.Map as M
|
||||
|
||||
@@ -47,21 +53,29 @@ mateConfig = desktopConfig
|
||||
, keys = mateKeys <+> keys desktopConfig
|
||||
, startupHook = mateRegister >> startupHook desktopConfig }
|
||||
|
||||
mateKeys (XConfig {modMask = modm}) = M.fromList $
|
||||
mateKeys XConfig{modMask = modm} = M.fromList
|
||||
[ ((modm, xK_p), mateRun)
|
||||
, ((modm .|. shiftMask, xK_q), spawn "mate-session-save --logout-dialog") ]
|
||||
, ((modm, xK_d), unGrab >> matePanel "MAIN_MENU")
|
||||
, ((modm .|. shiftMask, xK_q), mateLogout) ]
|
||||
|
||||
-- | Launch the "Run Application" dialog. mate-panel must be running for this
|
||||
-- to work.
|
||||
-- to work. partial application for existing keybinding compatibility.
|
||||
mateRun :: X ()
|
||||
mateRun = withDisplay $ \dpy -> do
|
||||
mateRun = matePanel "RUN_DIALOG"
|
||||
|
||||
-- | Launch a panel action. Either the "Run Application" dialog ("run_dialog" parameter,
|
||||
-- see above) or the main menu ("main_menu" parameter). mate-panel must be running
|
||||
-- for this to work.
|
||||
matePanel :: String -> X ()
|
||||
matePanel action = withDisplay $ \dpy -> do
|
||||
let panel = "_MATE_PANEL_ACTION"
|
||||
rw <- asks theRoot
|
||||
mate_panel <- getAtom "_MATE_PANEL_ACTION"
|
||||
panel_run <- getAtom "_MATE_PANEL_ACTION_RUN_DIALOG"
|
||||
mate_panel <- getAtom panel
|
||||
panel_action <- getAtom (panel ++ "_" ++ map toUpper action)
|
||||
|
||||
io $ allocaXEvent $ \e -> do
|
||||
setEventType e clientMessage
|
||||
setClientMessageEvent e rw mate_panel 32 panel_run 0
|
||||
setClientMessageEvent e rw mate_panel 32 panel_action 0
|
||||
sendEvent dpy rw False structureNotifyMask e
|
||||
sync dpy False
|
||||
|
||||
@@ -77,7 +91,7 @@ mateRun = withDisplay $ \dpy -> do
|
||||
-- (the extra quotes are required by dconf)
|
||||
mateRegister :: MonadIO m => m ()
|
||||
mateRegister = io $ do
|
||||
x <- lookup "DESKTOP_AUTOSTART_ID" `fmap` getEnvironment
|
||||
x <- lookup "DESKTOP_AUTOSTART_ID" <$> getEnvironment
|
||||
whenJust x $ \sessionId -> safeSpawn "dbus-send"
|
||||
["--session"
|
||||
,"--print-reply=literal"
|
||||
@@ -86,3 +100,12 @@ mateRegister = io $ do
|
||||
,"org.mate.SessionManager.RegisterClient"
|
||||
,"string:xmonad"
|
||||
,"string:"++sessionId]
|
||||
|
||||
-- | Display MATE logout dialog. This is the default mod-q action.
|
||||
mateLogout :: MonadIO m => m ()
|
||||
mateLogout = spawn "mate-session-save --logout-dialog"
|
||||
|
||||
-- | Display MATE shutdown dialog. You can override mod-q to invoke this, or bind it
|
||||
-- to another key if you prefer.
|
||||
mateShutdown :: MonadIO m => m ()
|
||||
mateShutdown = spawn "mate-session-save --shutdown-dialog"
|
||||
|
@@ -20,7 +20,7 @@ module XMonad.Config.Monad where
|
||||
import XMonad hiding (terminal, keys)
|
||||
import qualified XMonad as X
|
||||
import Control.Monad.Writer
|
||||
import Data.Monoid
|
||||
import XMonad.Prelude
|
||||
import Data.Accessor
|
||||
import Data.Accessor.Basic hiding (set)
|
||||
|
||||
@@ -45,5 +45,5 @@ add r x = tell (mkW (r ^: mappend x))
|
||||
--
|
||||
example :: Config ()
|
||||
example = do
|
||||
add layout $ LL [Layout $ Full] -- make this better
|
||||
add layout $ LL [Layout Full] -- make this better
|
||||
set terminal "urxvt"
|
||||
|
@@ -1,8 +1,9 @@
|
||||
{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, KindSignatures, MultiParamTypeClasses, UndecidableInstances #-}
|
||||
{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, KindSignatures, UndecidableInstances #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Prime
|
||||
-- Description : Draft of a brand new config syntax for xmonad.
|
||||
-- Copyright : Devin Mullins <devin.mullins@gmail.com>
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
@@ -115,7 +116,7 @@ ifThenElse,
|
||||
import Prelude hiding ((>>), mod)
|
||||
import qualified Prelude as P ((>>=), (>>))
|
||||
|
||||
import Data.Monoid (All)
|
||||
import XMonad.Prelude (All)
|
||||
|
||||
import XMonad hiding (xmonad, XConfig(..))
|
||||
import XMonad (XConfig(XConfig))
|
||||
@@ -478,7 +479,7 @@ wsActions = Summable wsActions_ (\x c -> c { wsActions_ = x }) (++)
|
||||
-- > wsSetName 1 "mail"
|
||||
-- > wsSetName 2 "web"
|
||||
wsSetName :: Int -> String -> Arr WorkspaceConfig WorkspaceConfig
|
||||
wsSetName index newName = wsNames =. (map maybeSet . zip [0..])
|
||||
wsSetName index newName = wsNames =. zipWith (curry maybeSet) [0..]
|
||||
where maybeSet (i, oldName) | i == (index - 1) = newName
|
||||
| otherwise = oldName
|
||||
|
||||
@@ -497,8 +498,8 @@ withScreens :: Arr ScreenConfig ScreenConfig -> Prime l l
|
||||
withScreens sarr xconf = (P.>>=) (sarr def) $ \sconf -> sprime sconf xconf
|
||||
where sprime :: ScreenConfig -> Prime l l
|
||||
sprime sconf =
|
||||
(keys =+ [(mod ++ key, action sid) | (sid, key) <- zip [0..] (sKeys_ sconf),
|
||||
(mod, action) <- sActions_ sconf])
|
||||
keys =+ [(mod ++ key, action sid) | (sid, key) <- zip [0..] (sKeys_ sconf),
|
||||
(mod, action) <- sActions_ sconf]
|
||||
|
||||
data ScreenConfig = ScreenConfig {
|
||||
sKeys_ :: [String],
|
||||
|
@@ -58,7 +58,7 @@ myStartupHook = do
|
||||
spawnOnOnce "emacs" "emacs"
|
||||
spawnNOnOnce 4 "xterms" "xterm"
|
||||
|
||||
myLayoutHook = smartBorders $ avoidStruts $ standardLayouts
|
||||
myLayoutHook = smartBorders $ avoidStruts standardLayouts
|
||||
where standardLayouts = tiled ||| mosaic 2 [3,2] ||| Mirror tiled ||| Full
|
||||
tiled = ResizableTall nmaster delta ratio []
|
||||
nmaster = 1
|
||||
@@ -68,7 +68,7 @@ myLayoutHook = smartBorders $ avoidStruts $ standardLayouts
|
||||
myLogHook p = do
|
||||
copies <- wsContainingCopies
|
||||
let check ws | ws == "NSP" = "" -- Hide the scratchpad workspace
|
||||
| ws `elem` copies = xmobarColor "red" "black" $ ws -- Workspaces with copied windows are red on black
|
||||
| ws `elem` copies = xmobarColor "red" "black" ws -- Workspaces with copied windows are red on black
|
||||
| otherwise = ws
|
||||
dynamicLogWithPP $ xmobarPP { ppHidden = check
|
||||
, ppOutput = hPutStrLn p
|
||||
@@ -76,4 +76,3 @@ myLogHook p = do
|
||||
, ppTitle = xmobarColor "green" "" . shorten 180
|
||||
}
|
||||
fadeInactiveLogHook 0.6
|
||||
|
||||
|
@@ -1,4 +1,10 @@
|
||||
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Sjanssen
|
||||
-- Description : Spencer Janssen's xmonad config.
|
||||
--
|
||||
------------------------------------------------------------------------
|
||||
module XMonad.Config.Sjanssen (sjanssenConfig) where
|
||||
|
||||
import XMonad hiding (Tall(..))
|
||||
@@ -24,10 +30,10 @@ sjanssenConfig =
|
||||
docks $ ewmh $ def
|
||||
{ terminal = "exec urxvt"
|
||||
, workspaces = ["irc", "web"] ++ map show [3 .. 9 :: Int]
|
||||
, mouseBindings = \(XConfig {modMask = modm}) -> M.fromList $
|
||||
[ ((modm, button1), (\w -> focus w >> mouseMoveWindow w))
|
||||
, ((modm, button2), (\w -> focus w >> windows W.swapMaster))
|
||||
, ((modm.|. shiftMask, button1), (\w -> focus w >> mouseResizeWindow w)) ]
|
||||
, mouseBindings = \XConfig {modMask = modm} -> M.fromList
|
||||
[ ((modm, button1), \w -> focus w >> mouseMoveWindow w)
|
||||
, ((modm, button2), \w -> focus w >> windows W.swapMaster)
|
||||
, ((modm.|. shiftMask, button1), \w -> focus w >> mouseResizeWindow w) ]
|
||||
, keys = \c -> mykeys c `M.union` keys def c
|
||||
, logHook = dynamicLogString sjanssenPP >>= xmonadPropLog
|
||||
, layoutHook = modifiers layouts
|
||||
@@ -50,12 +56,12 @@ sjanssenConfig =
|
||||
, "trayer --transparent true --expand true --align right "
|
||||
++ "--edge bottom --widthtype request" ]
|
||||
|
||||
mykeys (XConfig {modMask = modm}) = M.fromList $
|
||||
mykeys XConfig{modMask = modm} = M.fromList
|
||||
[((modm, xK_p ), shellPromptHere myPromptConfig)
|
||||
,((modm .|. shiftMask, xK_Return), spawnHere =<< asks (terminal . config))
|
||||
,((modm .|. shiftMask, xK_c ), kill1)
|
||||
,((modm .|. shiftMask .|. controlMask, xK_c ), kill)
|
||||
,((modm .|. shiftMask, xK_0 ), windows $ copyToAll)
|
||||
,((modm .|. shiftMask, xK_0 ), windows copyToAll)
|
||||
,((modm, xK_z ), layoutScreens 2 $ TwoPane 0.5 0.5)
|
||||
,((modm .|. shiftMask, xK_z ), rescreen)
|
||||
, ((modm , xK_b ), sendMessage ToggleStruts)
|
||||
|
@@ -3,6 +3,7 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Xfce
|
||||
-- Description : Config for integrating xmonad with Xfce.
|
||||
-- Copyright : (c) Ivan Miljenovic <Ivan.Miljenovic@gmail.com>
|
||||
-- License : BSD
|
||||
--
|
||||
@@ -36,10 +37,10 @@ import qualified Data.Map as M
|
||||
-- For examples of how to further customize @xfceConfig@ see "XMonad.Config.Desktop".
|
||||
|
||||
xfceConfig = desktopConfig
|
||||
{ terminal = "Terminal"
|
||||
{ terminal = "xfce4-terminal"
|
||||
, keys = xfceKeys <+> keys desktopConfig }
|
||||
|
||||
xfceKeys (XConfig {modMask = modm}) = M.fromList $
|
||||
xfceKeys XConfig{modMask = modm} = M.fromList
|
||||
[ ((modm, xK_p), spawn "xfrun4")
|
||||
, ((modm .|. shiftMask, xK_p), spawn "xfce4-appfinder")
|
||||
, ((modm .|. shiftMask, xK_q), spawn "xfce4-session-logout")
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user