1098 Commits
v0.4 ... master

Author SHA1 Message Date
Nikolas Nyby
8113e0fe55 Fix a few minor spelling errors 2025-06-14 13:53:09 -04:00
Tomáš Janoušek
fa8f34596d Merge pull request #532 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci
2025-05-12 18:44:00 +02:00
Tomas Janousek
799b0dc199 ci/nix: Drop weird magic URLs
I don't know what these are but I hope reverting to what
cachix/install-nix-action's documentation recommends will fix the errors
we're getting with presumably old Nix on new Ubuntu GitHub Actions
runners.
2025-05-12 17:34:47 +01:00
Tony Zorman
8ff0fb7b52 ci/nix: Switch to ubuntu-latest 2025-05-12 17:34:47 +01:00
github-actions[bot]
7a93f11bfd ci: Regenerate haskell-ci 2025-05-10 03:31:13 +00:00
github-actions[bot]
c605d09681 ci: Bump GHC patch versions in tested-with 2025-05-10 03:31:13 +00:00
github-actions[bot]
ebf265a84c ci: Regenerate haskell-ci 2025-04-13 16:57:14 +01:00
github-actions[bot]
da4ef9ea37 ci: Bump GHC patch versions in tested-with 2025-04-13 16:57:14 +01:00
github-actions[bot]
0d90d183a7 ci: Regenerate haskell-ci 2025-04-05 08:36:03 +01:00
Tony Zorman
756643c30e Merge pull request #528 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci
2025-03-31 08:24:37 +00:00
github-actions[bot]
ca1ba91360 ci: Regenerate haskell-ci 2025-03-29 03:18:46 +00:00
Tony Zorman
f2d1efbb6f Merge pull request #527 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci
2025-03-22 05:40:39 +00:00
github-actions[bot]
4173227e04 ci: Regenerate haskell-ci 2025-03-22 03:22:24 +00:00
github-actions[bot]
3a37d4c1f8 ci: Bump GHC patch versions in tested-with 2025-03-22 03:22:23 +00:00
Tony Zorman
bd99fd5f34 Merge pull request #526 from xmonad/dependabot/github_actions/cachix/install-nix-action-31
build(deps): bump cachix/install-nix-action from 30 to 31
2025-03-17 06:40:57 +00:00
dependabot[bot]
e9e1ad3190 build(deps): bump cachix/install-nix-action from 30 to 31
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 30 to 31.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v30...v31)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-17 00:32:32 +00:00
github-actions[bot]
07d95ad8fc ci: Regenerate haskell-ci 2025-02-22 11:12:16 +00:00
Tomas Janousek
39ae48970c ci: Add GHC 9.8 (Stackage LTS 23) to Stack test matrix 2025-01-26 22:51:37 +00:00
github-actions[bot]
eae89e25f9 ci: Regenerate haskell-ci 2025-01-25 09:36:33 +00:00
github-actions[bot]
ab6648ca8f ci: Bump GHC patch versions in tested-with 2025-01-25 09:36:33 +00:00
github-actions[bot]
5d73d294d4 man: Update 2025-01-02 21:10:55 +00:00
Tony Zorman
1c57ed4c9a Merge pull request #523 from sol/cabal
Add recompilation support via `cabal`
2025-01-02 22:08:36 +01:00
Simon Hengel
30d3f7159b Add recompilation support via cabal 2024-12-21 21:57:58 +07:00
github-actions[bot]
81cf71d7c6 ci: Regenerate haskell-ci 2024-11-16 05:44:27 +00:00
dependabot[bot]
eba9e97794 build(deps): bump cachix/install-nix-action from 29 to 30
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 29 to 30.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v29...v30)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-07 18:20:22 +02:00
Tony Zorman
aadb8df59b Merge pull request #516 from xmonad/dependabot/github_actions/cachix/install-nix-action-v29
build(deps): bump cachix/install-nix-action from V28 to 29
2024-09-30 08:56:21 +02:00
dependabot[bot]
053c798085 build(deps): bump cachix/install-nix-action from V28 to 29
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from V28 to 29. This release includes the previously tagged commit.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/V28...v29)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-30 00:21:37 +00:00
dependabot[bot]
a4140b9349 build(deps): bump cachix/install-nix-action from V27 to 28
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from V27 to 28. This release includes the previously tagged commit.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/V27...V28)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 02:49:24 +01:00
Christina Sørensen
dad56b04f5 fix: deprecated url literals
Signed-off-by: Christina Sørensen <christina@cafkafk.com>
2024-09-15 19:09:07 -04:00
brandon s allbery kf8nh
a58ccac7ba restore the main loop
The ghc versions affected by the join point bug are long obsolete
and should not be being used by anyone.

This partially reverts #404.
2024-07-29 14:20:35 -04:00
github-actions[bot]
b1d9884d2d ci: Regenerate haskell-ci 2024-07-13 06:21:17 +01:00
Tomas Janousek
2ba1258cc1 Bump lower bound for base
We only test with GHC 8.6+ and the bounds should reflect that.

Related: a16541b834 ("ci: Drop support for GHC 8.4")
Related: 2e89e5ed23 ("Bump lower bound for base")
2024-07-07 15:07:49 +01:00
Tony Zorman
bcf3bf6c77 Merge pull request #508 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci
2024-07-07 07:35:41 +02:00
github-actions[bot]
8b54c11558 ci: Regenerate haskell-ci 2024-07-06 03:15:57 +00:00
github-actions[bot]
7de2d3e969 ci: Bump GHC patch versions in tested-with 2024-07-06 03:15:57 +00:00
Tomas Janousek
cfbf1ad51d ci: Bump actions/upload-artifact to v4
v3 is deprecated:
https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/
2024-05-21 22:40:50 +01:00
Tomáš Janoušek
21028ad648 Merge pull request #505 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci (add GHC 9.10)
2024-05-21 21:32:58 +01:00
github-actions[bot]
4be171810c ci: Regenerate haskell-ci 2024-05-21 21:19:05 +01:00
github-actions[bot]
edadb427e7 ci: Bump GHC patch versions in tested-with 2024-05-21 21:19:05 +01:00
Tomas Janousek
38a303ea3e ci: gh-workflow-keepalive no longer needs a GITHUB_TOKEN input 2024-05-20 21:12:38 +01:00
Tony Zorman
4620a705b4 Merge pull request #506 from xmonad/dependabot/github_actions/cachix/install-nix-action-27
build(deps): bump cachix/install-nix-action from 26 to 27
2024-05-20 15:38:34 +02:00
dependabot[bot]
7d588210e2 build(deps): bump cachix/install-nix-action from 26 to 27
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 26 to 27.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v26...V27)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-20 01:00:14 +00:00
Tomas Janousek
f09d7aa641 ci: Expand stack test matrix 2024-05-13 11:36:40 +01:00
Markus Kurtz
cde1a25bca Add details to ManageHook.hs docs (#504)
* Add details to ManageHook.hs docs

* Link specialised variants in `stringProperty` doc

* Fix typo

Co-authored-by: Tomáš Janoušek <tomi@nomi.cz>

---------

Co-authored-by: Tomáš Janoušek <tomi@nomi.cz>
2024-04-27 16:51:25 +02:00
Tomáš Janoušek
383e3d6f47 Merge pull request #502 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci
2024-04-21 12:44:05 +02:00
github-actions[bot]
81a80b8a5d ci: Regenerate haskell-ci 2024-04-21 07:50:50 +00:00
github-actions[bot]
ce22dbf7db ci: Bump GHC patch versions in tested-with 2024-04-21 07:50:50 +00:00
Tony Zorman
88102c0afb Merge pull request #501 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci
2024-03-23 07:45:36 +01:00
github-actions[bot]
804bef7bcf ci: Regenerate haskell-ci 2024-03-23 03:16:30 +00:00
Tony Zorman
746880e9b9 Merge pull request #499 from xmonad/dependabot/github_actions/cachix/install-nix-action-26
build(deps): bump cachix/install-nix-action from 25 to 26
2024-03-11 20:10:05 +01:00
dependabot[bot]
82a7762f81 build(deps): bump cachix/install-nix-action from 25 to 26
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 25 to 26.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v25...v26)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 01:10:19 +00:00
Tony Zorman
459f6afeca TUTORIAL: Mention X.U.Ungrab being unnecessary with 0.18.0
Closes: https://github.com/xmonad/xmonad/issues/496
2024-03-10 16:42:24 -04:00
Tony Zorman
88b4ad3c13 Merge pull request #495 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci
2024-03-02 07:53:23 +01:00
github-actions[bot]
7cb14762cb ci: Regenerate haskell-ci 2024-03-02 03:14:55 +00:00
github-actions[bot]
0ed6a2377e ci: Bump GHC patch versions in tested-with 2024-03-02 03:14:54 +00:00
Tony Zorman
e5548547e3 .mailmap: Update 2024-02-28 09:08:16 +01:00
Tomas Janousek
4b9ef59706 ci: Adopt the liskin/gh-workflow-keepalive action
This avoids hardcoding the workflow filename and thus makes
refactoring/renaming less error-prone.
2024-02-15 11:02:14 +00:00
Tony Zorman
5cf87c75cd flake: Use types.str instead of types.string
types.string is deprecated.

Related: https://github.com/NixOS/nixpkgs/pull/66346
2024-02-08 12:37:04 +01:00
Tony Zorman
c3acee78d0 Bump version to 0.18.0.9 2024-02-03 18:41:41 +01:00
Tony Zorman
1396343a58 Bump version to 0.18.0 2024-02-03 16:01:11 +01:00
dependabot[bot]
c9334fbae7 build(deps): bump actions/cache from 3 to 4
Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)
2024-01-22 11:54:43 +00:00
Tomáš Janoušek
c496c31158 Merge pull request #489 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci
2024-01-17 12:08:06 +00:00
github-actions[bot]
31f63bb162 ci: Regenerate haskell-ci 2024-01-17 11:52:56 +00:00
github-actions[bot]
ddb4292d5a ci: Bump GHC patch versions in tested-with 2024-01-17 11:52:56 +00:00
dependabot[bot]
765e059470 build(deps): bump cachix/install-nix-action from 24 to 25
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 24 to 25.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v24...v25)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-14 20:06:43 -05:00
Tomas Janousek
c4cf4715f7 Bump version number (pre-release 0.17.9)
Adding `unGrab` to X.Operations is potentially a user-config breaking
change (Ambiguous occurrence ‘unGrab’ …), so we need xmonad-contrib to
conditionally reexport this (instead of just deprecating the now
duplicated definition). Thus we need a version bump here.

Related: 0156e2963b ("X.Operations: Add unGrab")
2023-12-18 10:29:08 +00:00
Tony Zorman
fa124f5658 Merge pull request #482 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci
2023-12-09 07:39:02 +01:00
Tony Zorman
855ff2f73c Merge pull request #479 from iogrt/ungrab
Move `unGrab` to `XMonad.Operations`
2023-12-09 07:34:58 +01:00
github-actions[bot]
5a04fa185d ci: Regenerate haskell-ci 2023-12-09 03:13:35 +00:00
Tony Zorman
706f54862c Merge pull request #481 from xmonad/dependabot/github_actions/cachix/install-nix-action-24
build(deps): bump cachix/install-nix-action from 23 to 24
2023-12-04 07:09:53 +01:00
dependabot[bot]
1d43cd203f build(deps): bump cachix/install-nix-action from 23 to 24
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 23 to 24.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v23...v24)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-04 01:10:17 +00:00
Tony Zorman
8421b100dd flake: Allow users to modify the dev shell
Copy of xmonad/xmonad-contrib@f1ea1e533d
Documentation for this is already inside of contrib's NIX.md.

Co-authored-by: Ivan Malison <IvanMalison@gmail.com>
2023-11-20 19:37:31 +01:00
iogrt
0156e2963b X.Operations: Add unGrab 2023-11-20 19:30:47 +01:00
Tony Zorman
32afd5e7e8 Merge pull request #480 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci
2023-11-18 08:06:22 +01:00
github-actions[bot]
404e50d560 ci: Regenerate haskell-ci 2023-11-18 03:15:27 +00:00
github-actions[bot]
05d6037f53 ci: Bump GHC patch versions in tested-with 2023-11-18 03:15:26 +00:00
Tomáš Janoušek
2f58567912 Merge pull request #478 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci
2023-11-13 00:06:03 +00:00
Tomas Janousek
f2aa84e102 ci: Only build haddock for hackage with the latest GHC
Cabal 3.10.2.0 exposes a bug in Haddock shipped with GHC 9.0 and 9.2, so
we need to work around it by bumping the version of GHC/Haddock we use
for building/uploading docs to Hackage, and to prevent build failures we
don't ever try to build haddocks for Hackage with older versions of
GHC/Haddock.

Related: https://github.com/haskell/haddock/issues/1582#issuecomment-1611412223
Related: https://github.com/haskell/cabal/issues/8326
Related: https://github.com/haskell/cabal/issues/9060
Related: https://github.com/haskell/cabal/pull/9073
Related: https://github.com/haskell/cabal/pull/9049
2023-11-12 23:57:40 +00:00
github-actions[bot]
2b7e278f7f ci: Regenerate haskell-ci 2023-11-11 03:17:55 +00:00
Tomáš Janoušek
bd69d20d01 Merge pull request #477 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci
2023-10-24 17:40:39 +01:00
Tomas Janousek
35fa7bf4a2 ci: Disable x-partial warnings in the testsuite
Related: https://github.com/xmonad/xmonad/pull/477
2023-10-24 17:27:34 +01:00
github-actions[bot]
a239a00977 ci: Regenerate haskell-ci 2023-10-24 12:30:27 +00:00
github-actions[bot]
afb66dd55c ci: Bump GHC patch versions in tested-with 2023-10-24 12:30:27 +00:00
Tony Zorman
1b7dea7acc Merge pull request #476 from xmonad/haskell-ci-update
ci: Regenerate haskell-ci
2023-10-24 13:57:11 +02:00
github-actions[bot]
8e820945f4 ci: Regenerate haskell-ci 2023-10-24 11:22:30 +00:00
Tony Zorman
21cc6ebd93 Merge pull request #330 from colonelpanic8/nixRecompilationSupport
Add nix recompilation support
2023-10-07 12:00:39 +02:00
Tony Zorman
050ba6420d Export ifM from X.Core
This was added as a local function in
1e17c1c1bc26cdb93e23d4aa75e57fe48ff4c951, but it's actually present in
X.ManageHook already. However, since that module imports X.Core, we
can't use this as-is.
2023-10-07 11:43:27 +02:00
Ivan Malison
67b5510dde Add nix recompilation support 2023-10-07 11:41:58 +02:00
Tony Zorman
327c2cf0c1 stack: Bump resolver to lts-21.12
This also bumps the default GHC version from 9.4.6 to 9.4.7.
2023-09-23 14:19:05 +02:00
Tomas Janousek
96b3628b54 ci: Replace deprecated haskell/actions/hlint-*
See https://github.com/haskell/actions/pull/301#issuecomment-1722353522
2023-09-18 15:40:18 +01:00
Tomas Janousek
dc4d304802 Avoid use of partial function (head)
GHC 9.8 will produce warnings about it. (And HLS already does.)

Fixes: https://github.com/xmonad/xmonad/issues/468
2023-09-18 00:04:03 +01:00
Tomas Janousek
c264e4cdb3 ci: Replace deprecated haskell/actions/setup
See https://github.com/haskell/actions/pull/301 and
https://github.com/haskell/actions/pull/301/files
2023-09-17 23:16:39 +01:00
Tomáš Janoušek
bb895d8415 Merge pull request #467 from rnwst/patch-1
Fix typo in TUTORIAL.md
2023-09-17 21:31:01 +01:00
Tomas Janousek
897597463a ci: Drop unnecessary cmd from "Install packdeps"
Not needed since https://github.com/haskell/actions/pull/165 (Jan 2023).
2023-09-17 21:24:00 +01:00
R. N. West
937493256d Fix typo in TUTORIAL.md 2023-09-17 13:54:24 +01:00
dependabot[bot]
8ec512b437 build(deps): bump actions/checkout from 3 to 4 (except haskell-ci workflow)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-17 10:56:15 +01:00
Tony Zorman
e563e01a5f Merge pull request #466 from xmonad/dependabot/github_actions/cachix/install-nix-action-23
build(deps): bump cachix/install-nix-action from 22 to 23
2023-09-11 05:51:07 +02:00
dependabot[bot]
0e4c1e6837 build(deps): bump cachix/install-nix-action from 22 to 23
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 22 to 23.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v22...v23)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-11 01:13:39 +00:00
Tony Zorman
5c2ba06902 stack: Bump resolver to 21.6
This is the newest lts resolver for 9.4.5.
2023-08-12 14:22:17 +02:00
Tony Zorman
efffa8946a Merge pull request #463 from slotThe/tut/bits
TUTORIAL: Some Bits
2023-07-26 19:00:59 +02:00
Tony Zorman
431bb4b57c TUTORIAL: Add a subsection about the startupHook
This is especially relevant for people who use display managers and thus
probably do not want to use .xinitrc directly.
2023-07-22 11:47:42 +02:00
Tony Zorman
3c80296733 TUTORIAL: Mention the need for a wallpaper 2023-07-22 11:44:08 +02:00
Tony Zorman
f289b3b134 Merge pull request #462 from slotThe/ci/bump
Bump cabal/stack CI
2023-07-18 09:35:29 +02:00
Tony Zorman
0932779f15 ci/cabal: Bump GHC versions 2023-07-16 17:24:30 +02:00
Tony Zorman
9138046ec5 ci/stack: Add lts-21 resolver 2023-07-16 17:19:58 +02:00
Tomas Janousek
a24e9b4c7f ci: Use better github-actions user name/email
This one links to the GitHub Actions app.
2023-07-16 14:47:58 +01:00
brandon s allbery kf8nh
1aac6611d8 Correctly cite Conor McBride's paper
Per #461 which was brought up on `#haskell` recently after I mentioned our practical discussion of zippers as an alternative to the theoretical and mathematical ones usually brought up.
2023-06-28 14:28:54 -04:00
Tony Zorman
51e507e953 Merge pull request #460 from xmonad/geekosaur-patch-1
Update INSTALL.md
2023-06-26 08:38:44 +02:00
brandon s allbery kf8nh
10abd059b7 Update INSTALL.md
Patchup for cabal 3.10, which broke environment files: you now have to install `base` and any other dependencies explicitly.
2023-06-25 17:24:43 -04:00
Tony Zorman
d4c607c4f9 Merge pull request #457 from xmonad/dependabot/github_actions/cachix/install-nix-action-22
build(deps): bump cachix/install-nix-action from 21 to 22
2023-06-19 07:43:55 +02:00
dependabot[bot]
28bc7dacde build(deps): bump cachix/install-nix-action from 21 to 22
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 21 to 22.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v21...v22)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 01:19:54 +00:00
Tomáš Janoušek
b7a76a5e8c Merge pull request #456 from xmonad/dependabot/github_actions/cachix/install-nix-action-21
build(deps): bump cachix/install-nix-action from 20 to 21
2023-05-29 14:06:07 +02:00
dependabot[bot]
58f3e8c6f1 build(deps): bump cachix/install-nix-action from 20 to 21
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 20 to 21.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v20...v21)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-29 01:33:09 +00:00
Tomáš Janoušek
00045cfc2a Merge pull request #446 from liskin/floatloc-size-hints
X.Operations: Apply size hints in floatLocation
2023-05-20 07:43:02 +01:00
Tomas Janousek
69134f9e8a X.Operations: Apply size hints in floatLocation
This is hopefully harmless with well-behaved clients and ordinary xmonad
configs, and it enables using re-floating to apply size hints to
existing windows. The only visible behaviour change I can foresee is
that tiled windows (which ignore hints by default, unless
X.L.LayoutHints is used) will now snap to size hints whenever they're
floated (either via a keybinding or on mouseMoveWindow), whereas
previously they'd only do so on mouseResizeWindow.

My use-case for this is the following: when I change fonts in my
terminal, it updates its size hints and then sends a
ConfigureRequestEvent to change its size to keep the number of rows and
columns the same, and it also happens to reset the position to 0, 0. If
it's tiled, that request is just ignored and hintsEventHook handles the
layout refresh. If it happens to be floating, I want neither the move to
0, 0 nor the window size change to keep rows/colums, so I have a
handleEventHook that ignores that ConfigureRequestEvent and just
refloats the window, but I need a way to reapply size hints.

I could add a separate function that applies these hints to the floating
RationalRect, but that seems like a lossy operation due to the Doubles
in there. So I'd probably end up replicating most of the code from
floatLocation, and then I might just improve that instead… :-)

(I'll submit that custom ConfigureRequestEvent-ignoring hook to
xmonad-contrib later.)
2023-05-20 08:34:07 +02:00
Tony Zorman
025a78508c Merge pull request #455 from geekosaur/454-project-file
Clarify the use of `cabal.project`
2023-05-14 12:27:25 +02:00
brandon s allbery kf8nh
b03fa7a67b Clarify the use of cabal.project
We've had a user show up in IRC who missed the parenthetical at the
end of the `cabal.project` step and got an error trying to build
from hackage. I've rephrased that part of the instructions to make
it clear that `cabal.project` is only needed when building from git.
2023-05-12 13:19:45 -04:00
brandon s allbery kf8nh
64fbf6a09d Move willFloat to 0.17.1
Since it's a short doc change, I'm committing directly after verifying the preview is correct.
2023-05-01 10:00:00 -04:00
Tony Zorman
eeac754ac7 Merge pull request #445 from LSLeary/current-wins
Let the current screen win in the fight for dupes
2023-04-03 14:48:50 +02:00
L. S. Leary
5ee76ca48f Let the current screen win in the fight for dupes
`windows` generates mappings one screen at a time, starting with the
current. Tracking the windows it's already generated mappings for,
it excludes them from the tiles under consideration, hence supporting
window duplication in a first-biased manner. This allows the current
screen to win against any contenders and keep duplicated tiles within
reach.

However, it neglects to extend this treatment to floats; they end up
mapped in a last-biased manner. Consequently, duplicated floats become
very slippery, escaping to any inactive screen they can. This change
rectifies that issue.

See: xmonad/xmonad-contrib#797
2023-04-04 00:26:44 +12:00
Tony Zorman
aa9dd2696a cabal: Bump version to 0.17.2.9 2023-04-02 15:18:47 +02:00
Tony Zorman
19cba6b25f Merge branch 'release-v0.17.2' of https://github.com/xmonad/xmonad into after-0.17.2 2023-04-02 15:17:55 +02:00
Tony Zorman
a09ca446fb cabal: Update tested GHC versions
This syncs the cabal file with the reality of our CI.
2023-04-02 13:27:59 +02:00
Tomas Janousek
5641038500 CHANGES: Fix typo 2023-04-02 13:26:11 +02:00
Tony Zorman
990555c8ab Bump version to 0.17.2 2023-04-02 10:39:43 +02:00
Tony Zorman
a207e30751 CHANGES.md: Add changes for 0.17.2 2023-04-02 10:39:43 +02:00
Tony Zorman
970893f556 cabal: Update tested GHC versions
This syncs the cabal file with the reality of our CI.
2023-04-01 15:37:47 +02:00
Tony Zorman
460096dfc5 ci: Bump 9.2 and 9.4 minor versions
The stack CI already tests against 9.2.7 instead of 9.2.5 (as the cabal
file said), since we just specify the major version of the LTS resolver,
and lts-20 is up to 9.2.7 by now. Not much has changed since 9.2.5, but
update the haskell-ci workflow regardless. Likewise, 9.4 can be bumped
one minor version, so do that as well.

Related: xmonad/xmonad-contrib@1d84db959c
2023-04-01 15:18:23 +02:00
Tony Zorman
0817c6a7ff ci: Test against GHC 9.6.1
+ Regenerate haskell-ci and adjust relevant patches.
  - Remove haskell-ci-dependabot.patch, as it is no longer necessary.

Related: xmonad/xmonad-contrib@635711e994
2023-04-01 15:18:23 +02:00
Tomas Janousek
b59473b016 ci: Unpin runner in hlint workflow; bump hlint to 3.5
hlint 3.5 is built against libtinfo6 and works on Ubuntu 22.04 without
needing to install libtinfo5.

Fixes: e6329968ff ("ci: Pin runner in hlint, nix workflows to ubuntu-20.04")
2023-04-01 15:18:08 +02:00
Tomas Janousek
c2aeaffc03 ci: Cabal install packdeps outside of project dir
Invoking cabal in the project directory with a cabal.project file might
fail on unrelated dependencies missing, see
4f539734be

Doesn't fail in this repo, but let's keep the workflows in sync.
2023-04-01 15:18:08 +02:00
Tomas Janousek
7f95f5ef07 ci: Pin runner in hlint, nix workflows to ubuntu-20.04
Both are now broken with ubuntu-22.04. Nix:

    error: could not set permissions on '/nix/var/nix/profiles/per-user' to 755: Operation not permitted

hlint:

    /opt/hostedtoolcache/hlint/3.4.1/x64/hlint: error while loading shared libraries: libtinfo.so.5: cannot open shared object file: No such file or directory
2023-04-01 15:18:08 +02:00
github-actions
57b715972b man: Update 2023-04-01 15:18:08 +02:00
Tomas Janousek
a9866836d9 ci: Drop ppa:hvr/ghc
Breaks `apt update` on the ubuntu-22.04 runner, which is what
ubuntu-latest resolves to these days.

Fixes: 3977a7a4e2 ("ci: Drop GHC installation via apt")
2023-04-01 15:18:08 +02:00
dependabot[bot]
099233812e build(deps): bump haskell/actions from 1 to 2
Bumps [haskell/actions](https://github.com/haskell/actions) from 1 to 2.
- [Release notes](https://github.com/haskell/actions/releases)
- [Commits](https://github.com/haskell/actions/compare/v1...v2)
2023-04-01 15:18:08 +02:00
Tomáš Janoušek
9cb13bdd3d ci: Configure dependabot to keep GH Actions up to date 2023-04-01 15:18:08 +02:00
Tomas Janousek
86bb4d2a21 ci: Bump actions/* to v3 in haskell-ci workflow
This gets rid of the deprecation warnings about
https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2023-04-01 15:18:08 +02:00
Tomas Janousek
30103efbc8 ci: Update to GHC 9.4.3 2023-04-01 15:18:08 +02:00
Tony Zorman
a16541b834 ci: Drop support for GHC 8.4
Debian stable is not on 8.6, which was always our guide as to which GHC
versions we want to support.

In particular, this lets us get rid of all the quickcheck-classes
special treatment.

Related: 400730fe3b
2023-04-01 15:17:45 +02:00
Tony Zorman
202fecf7ba ci: Update to GHC 9.2.5
+ A new stackage LTS is out with GHC 9.2.5, so test for this.
+ Update the haskell-ci jobs accordingly from 9.2.4.
2023-04-01 15:17:09 +02:00
Tomas Janousek
dd1e02555e ci: Fix docs tarball
haskell-ci by default passes --haddock-all to cabal v2-haddock, which
builds docs for all components and the …-docs.tar.gz tarball ends up
containing the docs for the last component, which happens to be tests.
We need the tarball to contain the library docs, for upload to Hackage.

Fixes: 25a4ed69da ("ci: Bump haskell-ci")
2023-04-01 15:17:01 +02:00
Tomas Janousek
4931bc4e41 X.Config: Appease -Werror=type-equality-requires-operators
GHC 9.4 complains:

    The use of ‘~’ without TypeOperators
    will become an error in a future GHC release.
2023-04-01 15:17:01 +02:00
Tomas Janousek
67267b7346 ci: Add GHC 9.4 to tested-with matrix (haskell-ci workflow)
(Also bump 9.2 to 9.2.4)
2023-04-01 15:17:01 +02:00
Tomas Janousek
4806b51a23 ci: Bump cachix/install-nix-action to v18
This gets rid of the deprecation warnings about
https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2023-04-01 15:17:01 +02:00
Tomas Janousek
98e5d1583d ci: Bump actions/{checkout,cache} to v3
This gets rid of the deprecation warnings about
https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2023-04-01 15:17:01 +02:00
Tomas Janousek
dc48e9e9c9 ci: Drop GHC installation via apt
The apt repo hosting these packages has been unmaintained for quite a
while, and we've excluded ~/.stack/programs from the cache long time
ago, so we can just let stack handle the installation. It's not much
slower than installing via apt, and even if it was, some matrix jobs
need to use the slow path anyway.

Related: 9fce3805fc ("ci: Use system GHC in Stack to not waste GH Actions cache space")
Related: 7d10e470d7 ("ci: Avoid caching GHC")
2023-04-01 15:17:01 +02:00
Tomas Janousek
572d02d8e8 ci: Replace deprecated GHA set-output command
See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2023-04-01 15:17:01 +02:00
Tomas Janousek
f53db04285 ci: Drop hlint from haskell-ci workflow
As we have a separate hlint workflow now, this is no longer needed.

Related: https://github.com/xmonad/xmonad-contrib/issues/669
Related: 7cee191516
2023-04-01 15:17:01 +02:00
Tomas Janousek
2324d21202 ci: Bump haskell-ci 2023-04-01 15:17:01 +02:00
Tomas Janousek
f2c9c75f67 ci: Add hlint workflow
The "haskell/actions/hlint-run" GitHub Action produces annotations which
GitHub shows in the code diff, in addition to just failing the workflow
when there are any suggestions. Also, now it runs in parallel to the
other workflows.

Related: https://github.com/xmonad/xmonad-contrib/issues/669
Related: dd26fcc3f1
2023-04-01 15:17:01 +02:00
Tony Zorman
1364ee4b1f stack: Bump resolver to lts-20.16
We test against 9.2.7 now, so we should make our stack config reflect
that—plus, it's more convenient for users, since one is more likely to
already have the latest minor version of GHC installed.

Related: xmonad/xmonad-contrib@d5c86dd17e
2023-03-31 07:49:53 +02:00
Tony Zorman
a17fa0d28b ci: Bump 9.2 and 9.4 minor versions
The stack CI already tests against 9.2.7 instead of 9.2.5 (as the cabal
file said), since we just specify the major version of the LTS resolver,
and lts-20 is up to 9.2.7 by now. Not much has changed since 9.2.5, but
update the haskell-ci workflow regardless. Likewise, 9.4 can be bumped
one minor version, so do that as well.

Related: xmonad/xmonad-contrib@1d84db959c
2023-03-31 07:48:46 +02:00
Tony Zorman
b394435443 ci: Test against GHC 9.6.1
+ Regenerate haskell-ci and adjust relevant patches.
  - Remove haskell-ci-dependabot.patch, as it is no longer necessary.

Related: xmonad/xmonad-contrib@635711e994
2023-03-31 07:46:37 +02:00
a5ob7r
faf5cf7b27 Fix compatibilities with unix 2.8.0.0
The version of unix removes the 3rd Maybe FileMode argument of openFd,
so we need to handle this breaking change using a MIN_VERSION macro. The
argument is integrated with the OpenFileFlags argument and the
integrated value in defaultFileFlags is Nothing, so there's no
difference between the two function calls.
2023-03-28 19:33:14 +02:00
a5ob7r
9d0fd62cb2 Fix compatibilities with mtl 2.3.1
No longer the version of mtl re-exports Control.Monad, Control.Monad.Fix
and Data.Monoid modules, so we need to import them directly instead.
2023-03-28 19:33:07 +02:00
Tony Zorman
386d4e6295 Merge pull request #441 from Equwece/suggest-to-use-ghcup
Suggest GHCup more intensively in INSTALL.md
2023-03-16 13:01:47 +01:00
Equwece
ea295dabcc INSTALL: Suggest to use ghcup more intensively
Closes: https://github.com/xmonad/xmonad/issues/373
2023-03-16 13:01:02 +01:00
Tony Zorman
4b3e5e0d07 Merge pull request #440 from xmonad/dependabot/github_actions/cachix/install-nix-action-20
build(deps): bump cachix/install-nix-action from 19 to 20
2023-03-06 08:02:38 +01:00
dependabot[bot]
4b2107a07a build(deps): bump cachix/install-nix-action from 19 to 20
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 19 to 20.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v19...v20)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-06 01:38:12 +00:00
Tony Zorman
3ae5f46052 Merge pull request #439 from xmonad/dependabot/github_actions/cachix/install-nix-action-19
build(deps): bump cachix/install-nix-action from 18 to 19
2023-02-13 07:23:09 +01:00
dependabot[bot]
f734f19c1a build(deps): bump cachix/install-nix-action from 18 to 19
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 18 to 19.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v18...v19)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-13 02:11:18 +00:00
Tomas Janousek
391c0fc0c9 TUTORIAL: Update xmobar URL
https://xmobar.org/ is no more.
2023-02-11 14:00:51 +00:00
Tony Zorman
5ecdf7591d Merge pull request #437 from TheMC47/master
README: update badge URLs
2023-01-06 19:03:24 +01:00
Yecine Megdiche
301428e5df README: update badge URLs
See https://github.com/badges/shields/issues/8671
2023-01-06 13:49:07 +01:00
Tomas Janousek
63f73e18f9 ci: Unpin runner in hlint workflow; bump hlint to 3.5
hlint 3.5 is built against libtinfo6 and works on Ubuntu 22.04 without
needing to install libtinfo5.

Fixes: e6329968ff ("ci: Pin runner in hlint, nix workflows to ubuntu-20.04")
2023-01-02 20:46:56 +01:00
Tomas Janousek
57c3a13125 ci: Cabal install packdeps outside of project dir
Invoking cabal in the project directory with a cabal.project file might
fail on unrelated dependencies missing, see
4f539734be

Doesn't fail in this repo, but let's keep the workflows in sync.
2022-12-03 13:15:30 +00:00
Tomas Janousek
e6329968ff ci: Pin runner in hlint, nix workflows to ubuntu-20.04
Both are now broken with ubuntu-22.04. Nix:

    error: could not set permissions on '/nix/var/nix/profiles/per-user' to 755: Operation not permitted

hlint:

    /opt/hostedtoolcache/hlint/3.4.1/x64/hlint: error while loading shared libraries: libtinfo.so.5: cannot open shared object file: No such file or directory
2022-12-01 12:02:23 +00:00
github-actions
c1670303c0 man: Update 2022-12-01 11:57:12 +00:00
Tomas Janousek
1d1c012cb9 ci: Drop ppa:hvr/ghc
Breaks `apt update` on the ubuntu-22.04 runner, which is what
ubuntu-latest resolves to these days.

Fixes: 3977a7a4e2 ("ci: Drop GHC installation via apt")
2022-12-01 11:55:06 +00:00
dependabot[bot]
a19ffb0404 build(deps): bump haskell/actions from 1 to 2
Bumps [haskell/actions](https://github.com/haskell/actions) from 1 to 2.
- [Release notes](https://github.com/haskell/actions/releases)
- [Commits](https://github.com/haskell/actions/compare/v1...v2)
2022-11-27 17:39:20 +00:00
Tomáš Janoušek
5aa70bd88a ci: Configure dependabot to keep GH Actions up to date 2022-11-27 17:39:20 +00:00
Tomas Janousek
2502fd8d55 ci: Bump actions/* to v3 in haskell-ci workflow
This gets rid of the deprecation warnings about
https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2022-11-27 17:38:41 +00:00
Tomas Janousek
d0942e37ad ci: Update to GHC 9.4.3 2022-11-27 17:37:40 +00:00
Tomas Janousek
40f8246080 MAINTAINERS: Add xmonad-docs build step to the release process 2022-11-25 19:32:43 +00:00
Tony Zorman
be8fd7fdc9 X.Core: Simplify DerivingVia instances
Fixes: cd86480ff7

Co-authored-by: L. S. Leary <LSLeary@users.noreply.github.com>
2022-11-19 11:21:50 +01:00
Tomáš Janoušek
6e35910b62 Merge pull request #430 from slotThe/drop-8.4
Drop support for GHC 8.4
2022-11-19 09:55:42 +00:00
Tony Zorman
2f2d105098 stack: Bump resolver to lts-20.0 2022-11-19 09:50:02 +01:00
Tony Zorman
cd86480ff7 Use DerivingVia for obvious instances
Certain instance definitions are so automatic, they should be derivable.
Starting with GHC 8.6, they are!
2022-11-19 09:50:00 +01:00
Tony Zorman
5c7c28060c ci: Drop support for GHC 8.4
Debian stable is not on 8.6, which was always our guide as to which GHC
versions we want to support.

In particular, this lets us get rid of all the quickcheck-classes
special treatment.

Related: 400730fe3b
2022-11-19 09:29:37 +01:00
Tony Zorman
78719507a9 ci: Update to GHC 9.2.5
+ A new stackage LTS is out with GHC 9.2.5, so test for this.
+ Update the haskell-ci jobs accordingly from 9.2.4.
2022-11-18 19:12:57 +01:00
Tony Zorman
f4d25fcef4 Merge pull request #426 from damhiya/improve-tall
improve Tall
2022-11-10 15:53:40 +01:00
damhiya
314390937c X.Layout: Don't draw zero-area windows in Tall 2022-11-10 15:52:39 +01:00
Tony Zorman
cf4d6f31b1 Merge pull request #428 from a5ob7r/compatible_with_unix_2.8.0
Fix compatibilities with unix 2.8.0.0
2022-11-05 08:13:37 +01:00
a5ob7r
044d9244e5 Fix compatibilities with unix 2.8.0.0
The version of unix removes the 3rd Maybe FileMode argument of openFd,
so we need to handle this breaking change using a MIN_VERSION macro. The
argument is integrated with the OpenFileFlags argument and the
integrated value in defaultFileFlags is Nothing, so there's no
difference between the two function calls.
2022-11-05 12:54:50 +09:00
Tony Zorman
ab99c17a68 Merge pull request #427 from a5ob7r/compatible_with_mtl_2.3
Fix compatibilities with mtl 2.3.1
2022-11-03 08:10:31 +01:00
a5ob7r
d170e99bc5 Fix compatibilities with mtl 2.3.1
No longer the version of mtl re-exports Control.Monad, Control.Monad.Fix
and Data.Monoid modules, so we need to import them directly instead.
2022-11-03 14:39:28 +09:00
Tomáš Janoušek
96452213f4 Merge pull request #425 from xmonad/hlint
CI maintenance (hlint workflow, GHA deprecations, GHC 9.4)
2022-10-31 11:45:21 +00:00
Tomas Janousek
c19eb06807 ci: Fix docs tarball
haskell-ci by default passes --haddock-all to cabal v2-haddock, which
builds docs for all components and the …-docs.tar.gz tarball ends up
containing the docs for the last component, which happens to be tests.
We need the tarball to contain the library docs, for upload to Hackage.

Fixes: 25a4ed69da ("ci: Bump haskell-ci")
2022-10-30 23:05:31 +00:00
Tomas Janousek
6d7da8dc25 X.Config: Appease -Werror=type-equality-requires-operators
GHC 9.4 complains:

    The use of ‘~’ without TypeOperators
    will become an error in a future GHC release.
2022-10-30 21:48:40 +00:00
Tomas Janousek
f96b3f0398 ci: Add GHC 9.4 to tested-with matrix (haskell-ci workflow)
(Also bump 9.2 to 9.2.4)
2022-10-30 21:37:37 +00:00
Tomas Janousek
34f257ad6f ci: Bump cachix/install-nix-action to v18
This gets rid of the deprecation warnings about
https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2022-10-30 21:29:49 +00:00
Tomas Janousek
f94ad61a27 ci: Bump actions/{checkout,cache} to v3
This gets rid of the deprecation warnings about
https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2022-10-30 21:18:05 +00:00
Tomas Janousek
3977a7a4e2 ci: Drop GHC installation via apt
The apt repo hosting these packages has been unmaintained for quite a
while, and we've excluded ~/.stack/programs from the cache long time
ago, so we can just let stack handle the installation. It's not much
slower than installing via apt, and even if it was, some matrix jobs
need to use the slow path anyway.

Related: 9fce3805fc ("ci: Use system GHC in Stack to not waste GH Actions cache space")
Related: 7d10e470d7 ("ci: Avoid caching GHC")
2022-10-30 20:59:39 +00:00
Tomas Janousek
3cd839f0ac ci: Replace deprecated GHA set-output command
See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2022-10-30 20:39:15 +00:00
Tomas Janousek
a9abc4e09c ci: Drop hlint from haskell-ci workflow
As we have a separate hlint workflow now, this is no longer needed.

Related: https://github.com/xmonad/xmonad-contrib/issues/669
Related: 7cee191516
2022-10-30 20:31:04 +00:00
Tomas Janousek
25a4ed69da ci: Bump haskell-ci 2022-10-30 20:31:04 +00:00
Tomas Janousek
262dc4779f ci: Add hlint workflow
The "haskell/actions/hlint-run" GitHub Action produces annotations which
GitHub shows in the code diff, in addition to just failing the workflow
when there are any suggestions. Also, now it runs in parallel to the
other workflows.

Related: https://github.com/xmonad/xmonad-contrib/issues/669
Related: dd26fcc3f1
2022-10-30 20:30:57 +00:00
Tony Zorman
a2259bb309 Merge pull request #421 from slotThe/expose-internals
Expose buildLaunch, sendRestart, and sendReplace
2022-09-18 16:27:14 +02:00
Tony Zorman
3d8238b35d Expose buildLaunch, sendRestart, and sendReplace
+ Move sendRestart and sendRestart to X.Operations, as this seems like a
  better fit.

Closes: https://github.com/xmonad/xmonad/issues/416
2022-09-10 10:27:26 +02:00
Tony Zorman
fd9de8903f TUTORIAL: Add separators for < 0.17.0 instructions
At least one of the instructions spans multiple paragraphs, which makes
it difficult to make out when this kind of interlude ends and the rest
of the text picks back up.  Introducing separators, while slightly
visually jarring, solves this problem.  As such, make sure that these
sections are reasonably far apart and group ones that are close
together.
2022-09-08 19:20:35 +02:00
Tony Zorman
fb1f33258e TUTORIAL: Remove ewmhFullscreen from < 0.17.0 instructions
This is a new function in 0.17.0, despite it being around for a long
time.
2022-09-08 19:20:35 +02:00
Tony Zorman
d0f12af1ae Merge pull request #420 from LSLeary/nixflake
Fixes and Workarounds in flake.nix
2022-09-05 17:50:49 +02:00
L. S. Leary
df6b40c980 flake.nix:
* Avoid mangling unused parts of pkgs.haskell in `fromHOL`.
  * Create 'defComp', specifying the default compiler, customisable
    through ./comp.nix.
  * Include `modernise` module as a workaround for NIX_GHC -> XMONAD_GHC.
.gitignore:
  * Include nix 'result' symlink.
2022-09-05 20:14:46 +12:00
Tomas Janousek
b771abeadc Bump version to 0.17.1.9 and prepare CHANGES.md sections 2022-09-03 16:21:52 +01:00
Tony Zorman
2db596fbe8 Merge pull request #419 from ErnestKz/master
CHANGES.md: Fix date.
2022-09-03 16:51:25 +02:00
ernestkz
0f31b24bd2 CHANGES.md: Fix date. 2022-09-03 15:37:32 +01:00
Tony Zorman
5cdddab1f1 INSTALL: Suggest newer tagged releases 2022-09-03 14:52:45 +02:00
Tony Zorman
488b52ffaa Bump version to 0.17.1 2022-09-03 14:47:26 +02:00
Tony Zorman
83a8bb8d51 TUTORIAL: Fix link to xmobar.hs example
In [1] xmobar renamed the "examples" directory to "etc".

[1]: 6330397707
2022-08-13 06:27:17 +02:00
Tony Zorman
b06d885e76 X.ManageHook: Clarify {app,class}Name documentation
Users often mistakenly use className to query the application (resource)
name of a window; probably since WM_CLASS even has "class" in its name,
so confusion ensues.  Improve the documentation to explicitly state
which of the two strings in WM_CLASS the respective functions match.
2022-08-08 11:03:25 +02:00
Tony Zorman
a13a1dcee8 Merge pull request #404 from geekosaur/forever-away
attempt to work around the join point bug (#389)
2022-08-04 08:48:43 +02:00
brandon s allbery kf8nh
8965e41d06 attempt to work around the join point bug (#389) 2022-08-04 08:44:50 +02:00
Tony Zorman
28afc9bdc6 Merge pull request #405 from slotThe/modal-util-to-core
X.Operations: Export setNumlockMask, grabKeys
2022-08-02 11:21:50 +02:00
Tony Zorman
23f36d7e23 Merge pull request #409 from alternateved/fix-nix-build
Fix Nix builds
2022-08-02 08:03:30 +02:00
Tomasz Hołubowicz
117583e473 flake.nix: Fix build
As advised in [1], provide a way to set the path for the xmessage and
ghc binaries via XMONAD_XMESSAGE and XMONAD_GHC environment variables.

[1]: 36d5761b3e
2022-08-02 08:02:40 +02:00
Tony Zorman
bf6e66b100 X.Operations: Export setNumlockMask, grabKeys
As discussed in xmonad/xmonad-contrib/#703, certain functions that
X.U.Grab has vendored should really just be exported from core.

Related: https://github.com/xmonad/xmonad-contrib/pull/703
2022-08-01 11:59:37 +02:00
Tomas Janousek
366c09b3d7 TUTORIAL: Fix links to xmobar 2022-07-30 14:59:14 +01:00
Tomas Janousek
ed5c8667b1 Revert "Revert "Merge pull request #350 from Thiago4532/opaque-window-border""
This reverts commit e5a258f19c.

https://github.com/xmonad/xmonad-contrib/pull/731 fixes the
WindowNavigation issue with alpha channels, so we can re-enable alpha
support.
2022-07-05 23:09:10 +01:00
brandon s allbery kf8nh
1695aeb28a Revert "Revert "CHANGES: Add missing entry for #350""
This reverts commit 29475fa7f8.

xmonad/xmonad-contrib#731 fixes the WindowNavigation issue with alpha
channels, so we can re-enable alpha support. A subsequent commit will
bump versions/dependencies to keep things in sync, although strictly
speaking that's not necessary here.
2022-07-05 17:50:17 -04:00
Tony Zorman
521e8356fc X.ManageHook: Define infix operators in an infix way
As discussed in #401, while hlint complains on a definition like

    (<||>) x y = ifM x (pure True) y

because it could be eta reduced, actually doing that and writing

    (<||>) x = ifM x (pure True)

feels a bit awkward.  The solution is to always actually define these
kinds of infix operators in an infix way; i.e., we write

    x <||> y = ifM x (pure True) y

instead.  For the sake of consistency, it now seems prudent to define
all infix operators in this way (with exceptions for literal aliases,
like `(<+>) = mappend`).

Related: https://github.com/xmonad/xmonad/pull/401
2022-06-29 09:40:55 +02:00
Tomas Janousek
45a89130d9 Merge branch 'hlint' 2022-06-25 18:49:02 +01:00
Tomas Janousek
79602bfec5 tests: Apply hlint hints 2022-06-25 18:49:02 +01:00
Tomas Janousek
711b28f494 Apply hlint hints
Makes src/ hlint-clean.
2022-06-25 18:49:02 +01:00
Tomas Janousek
0edb65107b Clean up LANGUAGE pragmas
Make it git-friendly and drop some extra ones as suggested by hlint.
2022-06-25 18:49:02 +01:00
Tomas Janousek
845d770f35 man: Apply hlint hints
These were applied to src/XMonad/Config.hs back in 2010 (77b3f62610) … :-)
2022-06-25 18:49:02 +01:00
Tomas Janousek
3f1a37f216 ci: Enable hlint in haskell-ci
Closes: https://github.com/xmonad/xmonad/pull/402
Related: https://github.com/xmonad/xmonad-contrib/issues/669
2022-06-25 18:49:02 +01:00
Andrew Lushin
165e25f9e0 Lint some pieces of code
Closes: https://github.com/xmonad/xmonad/pull/401
2022-06-25 18:49:02 +01:00
Tomas Janousek
9189d002dd ci: Prevent scheduled workflows from being auto-disabled by GitHub 2022-06-19 17:04:43 +01:00
Tomas Janousek
29475fa7f8 Revert "CHANGES: Add missing entry for #350"
This reverts commit c537a0658a.

A discussion of why, as well as a new way forward, in being discussed in

    https://github.com/xmonad/xmonad/issues/395

However, since we would like to release a new minor version soon-ish,
it's better to revert this for now.

Fixes: e5a258f19c ("Revert "Merge pull request #350 from Thiago4532/opaque-window-border"")
2022-06-19 16:54:29 +01:00
Tony Zorman
e5a258f19c Revert "Merge pull request #350 from Thiago4532/opaque-window-border"
This reverts commit dbe9c4f799, reversing
changes made to f6e4e278b5.

A discussion of why, as well as a new way forward, in being discussed in

    https://github.com/xmonad/xmonad/issues/395

However, since we would like to release a new minor version soon-ish,
it's better to revert this for now.
2022-06-18 17:40:25 +02:00
Tomáš Janoušek
0c8ed88d8a Merge pull request #342 from TheMC47/contributing-changes
CONTRIBUTING: Update to reflect current practices
2022-06-13 11:39:34 +02:00
Yecine Megdiche
9442871016 CONTRIBUTING: Document expectations about maintenance and getting involved
Related: https://github.com/xmonad/xmonad/issues/341
2022-06-13 11:05:25 +02:00
Tomas Janousek
adb363a480 CONTRIBUTING: Add missing periods
Related: https://github.com/xmonad/xmonad/issues/341
2022-06-13 11:03:28 +02:00
Yecine Megdiche
3d65a37c7e CONTRIBUTING: Drop "Rebasing and Squashing Commits" in favor of online docs
Single-commit pull requests are discouraged by the core team now, so
drop that section and just refer to cbeams a kernel docs instead.

Related: https://github.com/xmonad/xmonad/issues/341
2022-06-13 10:59:41 +02:00
Tony Zorman
54d921c5a6 TUTORIAL: Only user letter keys for the bindings
On IRC an issue came up where a user couldn't press M-] since they were
using a German keyboard layout and ] is put behind some AltGr
combination.  The correct way to specify that would be something along
the lines of M-M5-9, but we don't really want to go into that in the
tutorial.  Thus, only use the obvious modifiers, as well as letters,
which should work on every layout.
2022-05-17 18:31:01 +02:00
brandon s allbery kf8nh
f61fdbaf0c MAINTAINERS: Add geekosaur's GPG key 2022-05-15 22:08:38 +02:00
Tomas Janousek
d88643c639 TUTORIAL: Fix broken link to liskin's xmobarrc
I switched to compiled config as well now:
d816717dee
2022-05-09 11:59:21 +01:00
Tony Zorman
eaaf0aafcd stack: Bump default resolver to 19.6
No impact on CI, just makes it easier for contributors to use the latest
9.0 GHC.
2022-05-07 09:40:56 +02:00
Tony Zorman
23df88d778 Merge pull request #392 from LSLeary/flake-module
Apply Patch in Nix Flake; Enable Configuration
2022-05-04 20:46:41 +02:00
L. S. Leary
90d0ca4a2e flake.nix: Point to xmonad-contrib/NIX.md. Assume maintainership. 2022-05-04 19:18:11 +12:00
L. S. Leary
f3f0c712d8 flake.nix: Configure the flake via NixOS module. 2022-04-25 01:58:46 +12:00
L. S. Leary
a5b708ba00 flake.nix: Provide the hoverlay and a version of fromHOL taking a
compiler argument, as well as the hpath function that does the work.
2022-04-25 01:46:39 +12:00
L. S. Leary
6fc90cd9d3 flake.nix: Bring in the patch from unstable.
Separate the noise from the overlay, exporting it as lib.fromHOL for reuse
in the xmonad-contrib flake.
2022-04-25 01:40:53 +12:00
Tony Zorman
3009304352 Merge pull request #391 from geekosaur/document-build-scripts
document build scripts
2022-04-19 19:39:54 +02:00
brandon s allbery kf8nh
5dd964e109 document build scripts
Requires https://github.com/xmonad/xmonad-contrib/pull/710
2022-04-18 17:24:51 -04:00
Tomas Janousek
90c719148b ci: Discard old caches to fix build failures
We're getting "undefined reference" errors during linking, suggesting
some build artifacts in the cache are stale and need to be rebuilt.
2022-04-18 23:24:09 +02:00
slotThe
831ca49331 X.StackSet: Add links to references 2022-04-12 12:48:35 +02:00
slotThe
2c9e24e0f6 X.StackSet: Give all functions their own Haddock comments
So far, some functions like focus{Up,Down} and swap{Up,Down} had
combined Haddock comments, since giving each their own would result in
some duplication of information.  This is nicer when reading the source,
but Haddock can't really handle this when generating the HTML page,
which will come out a bit garbled in that case.  Since a lot of users
may be only reading the Haddocks, we should prefer this to a source with
as few redundancies as possible.

Closes: https://github.com/xmonad/xmonad/issues/387
2022-04-12 11:42:01 +02:00
slotThe
a854cdaf9b ci: Update supported GHC versions
+ Prefer GHC 8.10.7 to 8.10.4, as versions seem to have stabilised now.
+ Add support for Stackage LTS 19; this ships with GHC 9.0.2.
+ Since a new version of 9.2 has been released, prefer 9.2.2 over 9.2.1.

Related: https://github.com/xmonad/xmonad-contrib/pull/694
2022-04-05 14:33:23 +02:00
slotThe
c2904425e9 TUTORIAL: Fix typos
Mostly capitalising names that ought to be capitalised.
2022-03-11 15:49:01 +01:00
slotThe
89ea1356c1 INSTALL.md: Add Void dependencies
Since things like `ncurses-libtinfo` are needed due to [1], it makes
sense to have all dependencies spelled out explicitly.

Related: https://github.com/xmonad/xmonad-web/issues/57
[1]: https://github.com/void-linux/void-packages/issues/7403
2022-03-08 09:23:41 +01:00
Tomas Janousek
f4a5b88e64 MAINTAINERS: Tweak the release procedure a bit
* Clarify what needs to be done with CHANGES.md
* Early release announcement preparation
2022-02-14 18:15:53 +00:00
Tomas Janousek
906b9d34b3 X.Operations: Whitespace cleanup after #371 2022-02-01 18:12:05 +00:00
Tomas Janousek
c537a0658a CHANGES: Add missing entry for #350 2022-02-01 18:09:12 +00:00
Tony Zorman
8546ea095b Merge pull request #371 from andrea-berling/will-float
Add function to detect floating windows in ManageHook
2022-01-26 11:22:53 +01:00
Andrea Berlingieri
c2e632a2b9 Factor our common logic for floating windows
Factour out the code used to detect whether a window should be floating
in Operations.hs in a new function named isFixedSizeOrTransient

Modify willFloat to use the factored out code from Opeartions.hs
2022-01-22 23:00:41 +01:00
Andrea Berlingieri
b6af6bb86a Add function to detect floating windows in ManageHook
Add a willFloat function to deteect whether the managed window will be
floating or not

Add description of added change to CHANGES.md
2022-01-22 21:56:53 +01:00
Tomas Janousek
eee0a0dc39 flake.nix: Use upstream gitignore.nix instead of Ivan's fork 2022-01-15 12:03:35 +00:00
Mike Nrafter
0f5b5c2297 Fixed flake.nix's use of GitIgnore. 2022-01-12 19:58:52 -07:00
slotThe
e25d090112 cabal: Add myself to authors
About time, I suppose :)
2021-12-11 19:54:05 +01:00
Tony Zorman
eb2ee340e4 Merge pull request #352 from slotThe/custom-cursor
X.Operations: Use custom cursor for dragging/resizing
2021-12-01 12:11:29 +01:00
slotThe
79278d9475 X.Operations: Use custom cursor for dragging/resizing
When dragging and resizing windows, users may expect the cursor to
change to indicate the respective behaviour.  In particular, many other
window managers already do this [1] [2].

Thus, introduce a new (non-exported) `mouseDragCursor` function that
takes a cursor shape and change the generic resize and move functions to
use that.  The reason that we don't change `mouseDrag` itself (for now)
is that this is exported and quite a few contrib modules use it—breaking
compatibility with xmonad-0.17.0 so soon after the release seems unwise.

Fixes: https://github.com/xmonad/xmonad/issues/348

[1]: https://git.suckless.org/dwm/file/dwm.c.html#l1567
[2]: 7a8fa9d27a/lib/awful/mouse/resize.lua (L23)
2021-11-25 09:12:22 +01:00
Tony Zorman
dbe9c4f799 Merge pull request #350 from Thiago4532/opaque-window-border
X.Operations: Make window borders opaque
2021-11-22 17:49:44 +01:00
Tomas Janousek
f6e4e278b5 README: Make spaces _not_ part of hyperlinks 2021-11-22 11:28:19 +00:00
Tomas Janousek
673de33436 README: Add badges for IRC and Matrix 2021-11-22 11:26:20 +00:00
slotThe
a5b6e09985 INSTALL.md: Remind users to check which xmonad
We have had this situation happen a few times now: users update
xmonad (say, to 0.17.0) but forget that they still have an older version
installed via the distributions repositories.  Features that depend on
the "bootstrap" xmonad executable to be updated (like the improved XDG
support) then fail badly.

Thus, remind users to check whether the right executable is present.
2021-11-20 11:26:18 +01:00
Thiago Mota
bb448cc293 X.Operations: Make window borders opaque 2021-11-18 19:53:05 -03:00
slotThe
ae4c5e26be MAINTAINERS.md: Add PGP key for slotThe
Also, move the bullet point to the end so as to keep the list
alphabetically ordered.
2021-11-17 19:34:38 +01:00
slotThe
54df2e9acd INSTALL.md: Mention more packages as dependencies for Arch
At least on Arch, none of the listed packages necessarily require that
the user has a working Xorg setup—this has already caused some confusion
for people.  In particular, xmessage is very much needed in order to
show warnings and compilation errors.
2021-11-14 13:26:34 +01:00
Tomas Janousek
7f6d758ce5 MAINTAINERS: Update the Hackage release step 2021-11-08 18:15:07 +00:00
Tomas Janousek
9f64c2ca90 ci: Show error body from Hackage when it fails
Prevents having to upload the candidate manually to see what's wrong.
2021-11-08 18:14:52 +00:00
Tomas Janousek
9849800dc5 ci: Swap candidate/final release logic
During the release of xmonad 0.17.0, I realized that we need to be able
to upload candidates before tagging the release on GitHub, because there
might be issues with the tarball and Hackage may reject it. When that
happened, I had to remove the release, delete the tag, upload the
candidate manually to see what's wrong with it, try to fix it, upload it
manually again, and so on.

This commit swaps the logic: when the workflow is invoked manually, it
uploads the candidate. This can be done multiple times, and once
everything is fine, the release can finally be tagged and it's released
to Hackage proper. The only disadvantage is that we need to remember to
try uploading the candidate. Not sure if there's a perfect solution…
2021-11-08 18:13:23 +00:00
Tomáš Janoušek
a902fefaf1 Merge pull request #346 from liskin/ghc92
Test against GHC 9.2.1; fix new warnings
2021-11-04 11:05:14 +00:00
Tomas Janousek
0f708e76b1 Fix -Wnoncanonical-monad-instances, -Wnoncanonical-monoid-instances 2021-10-31 11:53:42 +00:00
Tomas Janousek
6e6f562b0d Fix Pattern match(es) are non-exhaustive warnings
Many of these are legitimate, like the one in rescreen where it really
can be empty and xmonad might crash. Or the one in Main, where using an
irrefutable pattern means a pattern-match failure isn't reported using
the MonadFail instance of IO, but is left to crash later when the thunk
is evaluated.

Others are just GHC not knowing it won't crash, and we can use
Data.List.NonEmpty to tell it.
2021-10-31 11:41:53 +00:00
Tomas Janousek
12d1b31d6c ci: Test against GHC 9.2 2021-10-31 11:04:27 +00:00
Tomas Janousek
b92bd28d97 ci: Update haskell-ci 2021-10-31 10:52:58 +00:00
Tomáš Janoušek
e1daf46c75 Merge pull request #344 from slotThe/dont-print-getWindowAttributes
X.Operations: Silently catch in setWindowBorderWithFallback
2021-10-29 12:24:30 +01:00
slotThe
a8e1249ba7 stack: Bump default resolver to 18.14
No impact on CI, just makes it easier for contributors to use the latest
8.10 GHC.

Related: xmonad-contrib@f5f6ef41cb6cce3ba14957c31640f10b5751c90c
2021-10-29 11:00:04 +02:00
slotThe
e3824687c7 X.Operations: Silently catch in setWindowBorderWithFallback
While we catch the exception that `getWindowAttributes` can throw in
`setWindowBorderWithFallback`, we immediately turn around and print the
error to stderr.  Since this exception is raised every time a window is
closed[1] , it clutters stderr and may even confuse users as to why
xmonad is throwing these exceptions.

[1]: Depending on how the window is closed, we either have no way of
running `windows` on our own (say, the window is closed by a keybinding
of the program itself), or the focus change (and thus the call to
`windows`) runs before we can handle the DestroyWindowEvent.
2021-10-29 09:43:13 +02:00
Tomas Janousek
c979ee67c0 MAINTAINERS: Update release procedure (dev version bump, …) 2021-10-28 18:06:13 +01:00
Tomas Janousek
6c92dd22ad Bump version to 0.17.0.9 and prepare CHANGES.md sections
We need to bump the version early to avoid overwriting
https://xmonad.github.io/xmonad-docs/xmonad-0.17.0/
2021-10-28 18:05:15 +01:00
slotThe
66ac855959 TUTORIAL.md: Fix link for Libera webchat
The kiwiirc webchat was superseded by one hosted directly by the Libera
project.
2021-10-27 19:22:30 +02:00
Tomas Janousek
7fab71f5f0 xmonad.cabal: Specify default-language
Required for cabal-version >= 1.10
2021-10-27 17:55:00 +01:00
Tomas Janousek
055dce10af Bump cabal-version in xmonad.cabal
Hackage won't accept the release otherwise.
2021-10-27 17:48:44 +01:00
github-actions
292f19eab8 man: Update 2021-10-27 15:57:55 +00:00
Tomas Janousek
a204c9ed04 Bump version number (0.17.0), update date in changelog/manpage 2021-10-27 16:57:14 +01:00
Yecine Megdiche
78f1a8e716 CONTRIBUTING: Drop xmonad-testing
xmonad-testing isn't really being used, so the wording needs to be
changed in CONTRIBUTING.md and in the PR template. The important thing:
we want an example for new modules/functionality, and appropriate tests.

Related: https://github.com/xmonad/xmonad/issues/341
2021-10-27 16:50:10 +01:00
slotThe
72794d92b1 TUTORIAL.md: Rename versions 0.17 -> 0.17.0 2021-10-27 12:03:14 +02:00
slotThe
546d0b5ddb TUTORIAL.md: Remove note about 0.17.0 not being released
:)
2021-10-27 12:01:06 +02:00
slotThe
a03b6e86de CONTRIBUTING.md: Refer to pedantic
Instead of manually compiling with `-Wall -Werror` and the like, we have
a `pedantic` flag in xmonad's cabal file with which to run the CI.
Since contributors are very unlikely to not use either stack or cabal to
build xmonad, we should refer them to this flag instead of listing all
of the options they would need to compile xmonad with in order to check
their work.

Fixes: bc8f7ff133
2021-10-25 09:14:06 +02:00
Tomas Janousek
856c8b2c8d Merge branch 'packdeps' 2021-10-24 17:19:36 +01:00
Tomas Janousek
dbd441cc1b MAINTAINERS: Update release procedure 2021-10-24 17:08:10 +01:00
Tomas Janousek
2e89e5ed23 Bump lower bound for base
We only test with GHC 8.4+ so it's unlikely this builds with older base
(Semigroup stuff …).
2021-10-24 17:08:10 +01:00
Tomas Janousek
0ebedbb533 ci: Check package bounds using packdeps once a week
This was a part of the release procedure but we should do it proactively
instead.

Related: https://github.com/xmonad/xmonad/issues/264
2021-10-24 17:08:10 +01:00
Tomas Janousek
c2a1a3c0a6 ci: Add Stackage LTS 18 to Stack test matrix 2021-10-24 17:08:10 +01:00
Tomas Janousek
7d10e470d7 ci: Avoid caching GHC
We install GHC using apt, so stack shouldn't install it, but should it
ever end up installing it anyway (version mismatch, version unavailable
in hvr/ghc ppa, …), we don't want it wasting valuable cache space.
2021-10-24 17:08:10 +01:00
slotThe
6608f0012b CONTRIBUTING.md: Mention IRC/matrix before mailing list
Nowadays, the IRC and (since it is linked to it) matrix channels are
much faster ways to get into contact with us than the (very low traffic)
mailing list.  CONTRIBUTING should reflect that, since especially new
contributors might not know where to go to get a feeling for the people
working on the project—or worse, think we only communicate via pull
requests and issues!
2021-10-24 17:43:45 +02:00
slotThe
bc8f7ff133 Move STYLE to CONTRIBUTING.md
Style guidelines should be where most people will see them.  Since we
already ask people to read CONTIRBUTING.md when they submit a pull
request, putting them in there seems like the best place.

We can also drop the curious "or freer" clause from the licensing point,
much like xmonad-contrib@f39218ddb5ffeddce90f620910d5ef2c14f2b43d
already did.
2021-10-24 17:43:45 +02:00
slotThe
7845145706 Delete CONFIG
CONFIG has been completely replaced by TUTORIAL.md and it might be quite
confusing for new users if we keep two tutorials around.
2021-10-24 17:38:13 +02:00
slotThe
79afdfbbb9 CHANGES.md: Sectionise 0.17.0 changes
Create three sections (enhancements, bug fixes, breaking changes) in
order to organise the changes for the v0.17.0 release.

As discussed in [1], the breaking changes really aren't breaking changes
to normal users.  The only exception could be the GenerateManpage
change, but even that is only important for distro maintainers.  Hence,
move all of these to the very back, such that we can mention exciting
features like the stack integration at the top instead.

Related: https://github.com/xmonad/xmonad/pull/340

[1]: https://github.com/xmonad/xmonad/pull/340#discussion_r734956547
2021-10-24 08:18:42 +02:00
Tomáš Janoušek
8774081c15 Merge pull request #340 from liskin/readme
README: Long overdue update
2021-10-23 16:06:42 +01:00
Tomas Janousek
60f36e78ba CHANGES: Prepare version number and approx. date for release 2021-10-23 16:06:06 +01:00
Tomas Janousek
b198b80559 README: Long overdue update
* add logo and badges to make it prettier
* revamp installation sections
* update xmonad-contrib reference and move upwards
* drop other useful programs, this is in download.html
* add new Contributing section

Fixes: https://github.com/xmonad/xmonad/issues/199
2021-10-23 00:17:30 +01:00
Tomas Janousek
6bbd8b869e Update .mailmap with even more info from xmonad-contrib
The lesson here is that stuff like this should not be done manually.
For future reference, here's the machinery I ended up using:

    https://gist.github.com/7b78eba396024d4f56fa217867e2a3a8
2021-10-22 23:05:48 +01:00
Tomas Janousek
97aeb8577c LICENSE: Fix wording to make it detectable by GitHub 2021-10-22 22:55:47 +01:00
Tomas Janousek
0be6d2bec5 Update .mailmap with additional info from xmonad-contrib's
+ some Googling.

If the archaeologists ever come, we're ready for them. :-)
2021-10-21 15:33:12 +01:00
Tomas Janousek
b1fef9b18c Add .mailmap to clean git shortlog 2021-10-21 11:39:46 +01:00
slotThe
b5b95e27ce Merge pull request #337 from slotThe/lazy-lifted-boolean-ops
Make `(<&&>)` and `(<||>)` lazy in their second argument
2021-10-17 10:14:02 +02:00
slotThe
4e30ef13a7 Update CHANGES.md 2021-10-17 10:13:00 +02:00
slotThe
d92125485a X.ManageHook: Make (<&&>) and (<||>) lazy in their second argument
While an implementation of `liftM2 (&&)` may seem like a straightforward
lift of `(&&)` into a monadic setting, it actually expands to

    (<&&>) :: Monad m => m Bool -> m Bool -> m Bool
    mb <&&> mb' = do
      a <- mb
      b <- mb'
      return (a && b)

which runs both monadic effects first and then applies `(&&)`.

This is fixed by introducing a monadic version of `if-then-else` (which
is also exported due to its usefulness) that checks the second result
only if this is explicitly necessary.
2021-10-16 16:33:04 +02:00
Tomas Janousek
33a86c0cdb X.Operations: Fix mouse in Tabbed decorations (revert broadcastMessage refactor)
Turns out there was another aspect of `broadcastMessage` behaviour that
I missed in my review of the refactor: X.L.Tabbed updates the windowset
during handleMessage (via `X.O.focus`) and expects that change to
persist (by returning `Nothing` and hoping no other layout or layout
modifier returns `Just`). That's quite a hack, but the LayoutClass
interface doesn't allow a cleaner way to do this (well, some extensible
state plus a custom event hook might work, but then the layout isn't
self-contained any more).

And since rereading workspace layouts during `modifyLayouts` would force
this back into O(n²), we might as well revert the whole refactor. :-/

Fixes: https://github.com/xmonad/xmonad/issues/329
2021-09-14 11:25:18 +01:00
Tomáš Janoušek
3bb653bf9c Merge pull request #327 from ssbothwell/feature/withFocused-function
Adds withUnfocused function to XMonad.Operations
2021-09-14 11:09:42 +01:00
Tomas Janousek
ebce32d891 Minor cleanups of Solomon's PR 2021-09-14 11:08:48 +01:00
Solomon Bothwell
b3bd9c90d1 Adds withUnfocused function to XMonad.Operations 2021-09-13 23:00:37 -07:00
Tomáš Janoušek
5da25c5413 Merge pull request #291 from wygulmage/refactor_message_handling
added export list to Operations, refactored message handling.
2021-09-04 18:11:10 +01:00
Tomas Janousek
11d76e284c Adjust runOnWorkspaces processing order
We now use this in `broadcastMessage`, so to not change which workspaces
get the message first, we need to change the order here. This wouldn't
normally be safe to do either, but there are no other uses of
`runOnWorkspaces` neither here nor in xmonad-contrib, so it should
actually be fine.
2021-09-04 18:04:49 +01:00
Tomas Janousek
6d661203d3 Rename updateLayoutsBy to modifyLayouts
More consistent with our existing naming.
2021-09-04 18:01:36 +01:00
Tomas Janousek
30c2eeeeb3 Revert "changed order of broadcastMessage to match original."
We'll adjust runOnWorkspaces instead.

This reverts commit 5eff329fc6.
2021-09-04 17:54:17 +01:00
Tomas Janousek
d66e71d464 Revert "generalized signature of updateLayoutsBy"
This reverts commit eb48bb4aef.
2021-09-04 17:54:06 +01:00
Tomas Janousek
5c7e61def2 X.Operations: Small cleanup of module header 2021-09-04 17:52:01 +01:00
Keith
eb48bb4aef generalized signature of updateLayoutsBy 2021-09-04 17:52:01 +01:00
Keith
5eff329fc6 changed order of broadcastMessage to match original.
This uses two new functions (not exported):
`workspacesA` traverses the workspaces in a StackSet.
`runOnWorkspaces` runs `workspacesA` with the X state.
2021-09-04 17:52:01 +01:00
Keith
183e14725f changed broadcastMessage to be O(workspaces).
The current definition of broadcastMessage seems to be O(n^2) in the number of workspaces because it uses sendMessageWithNoRefresh and sendMessageWithNoRefresh uses updateLayout and updateLayout uses runOnWorkspaces.

This changes broadCastMessage and sendMessageWithNoRefresh to each use a single
call to runOnWorkspaces.
2021-09-04 17:52:01 +01:00
Keith
0ab42d4228 added export list to Operations.
This includes Haddock sections that will make the documentation render differently.
2021-09-04 17:52:01 +01:00
Keith
52a5e7ca8c updated documentation of Operations.
Added missing documentation for type alias `D`.

Moved misplaced documentation.

Edited to a consistent style that will play well with Haddock.
2021-09-04 17:46:05 +01:00
Tomas Janousek
f89df98f40 Drop unused import System.Environment (setEnv)
Fixes: 30719202b9 ("Fix xmessage in non-UTF-8 locales")
2021-08-30 18:04:23 +01:00
Tomas Janousek
30719202b9 Fix xmessage in non-UTF-8 locales
`executeFile` encodes the arguments with the current locale's encoding,
and GHC as invoked during recompilation quite likely also outputs any
errors in the locale encoding, which we then read using `readFile` again
decoding in the locale encoding, so it really makes no sense to force a
specific encoding here. What was I thinking? :-)

Related: https://github.com/xmonad/xmonad/issues/322#issuecomment-900503386
Fixes: https://github.com/xmonad/xmonad/issues/324
Fixes: aa35ea1856 ("Make xmessage handle UTF-8 and export it")
2021-08-30 17:45:47 +01:00
Tomáš Janoušek
aa18707c3e Merge pull request #323 from MuhammedZakir/master
Add `--stack-yaml` flag to `stack build --silent`
2021-08-17 19:09:57 +01:00
MuhammedZakir
b77ba03ed9 Add --stack-yaml flag to stack build --silent
Fixes: https://github.com/xmonad/xmonad/issues/322
2021-08-17 23:02:45 +05:30
Tomáš Janoušek
be1d2269ce Merge pull request #318 from liskin/stack-recompile
Recompilation overhaul: stack.yaml detection, deprecation warnings
2021-08-17 10:55:38 +01:00
Tomas Janousek
7bdc7ab9dc Print the recompilation command into the error file
When `stack build --silent` fails, the output is not helpful at all:

    Errors detected while compiling xmonad config: /home/slot/.config/xmonad/xmonad.hs
    ExitFailure 1

    Please check the file for errors.

And even in other circumstances it's helpful to see the command that was
executed, as it makes it easy for the user to diagnose what's wrong.
2021-08-17 10:46:07 +01:00
Tomas Janousek
ae97c1f107 CHANGES: Mention recompilation improvements 2021-08-17 10:46:07 +01:00
Tomas Janousek
782ac25b8e INSTALL: Update after stack.yaml autodetection
Related: https://github.com/xmonad/xmonad/issues/310
2021-08-17 10:46:07 +01:00
Tomas Janousek
8aa0d4a3e0 Detect deprecation warnings during recompilation and warn the user
Deprecation warnings were suppressed during recompilation, which made it
difficult to improve things in the codebase and API as users did not
(know they) have a transition period to adapt their configuration.

See additional discussions about deprecations:

 - https://github.com/xmonad/xmonad-contrib/pull/404#issuecomment-731607447
 - https://github.com/xmonad/xmonad-contrib/pull/410#issuecomment-732841235

Fixes: https://github.com/xmonad/xmonad/issues/304
Related: https://github.com/xmonad/xmonad-contrib/pull/404
Related: https://github.com/xmonad/xmonad-contrib/pull/410
2021-08-17 10:46:07 +01:00
Tomas Janousek
1f8e5b43e1 Fix indent in getDirectories
Fixes: 90101613e7 ("Unclobber dirs/Dirs in import XMonad")
2021-08-17 10:46:07 +01:00
Tomas Janousek
9813e218b0 Move config binary and GHC intermediate outputs to cacheDir
That's where they belong. As XDG was more or less broken in previous
xmonad releases, we can assume few people use it so now's the best time
to move files around.

For users of `~/.xmonad`, this only causes intermediate outputs (.o,
.hi) to go elsewhere.

Fixes: https://github.com/xmonad/xmonad/issues/178
2021-08-17 10:46:07 +01:00
Tomas Janousek
403e4df624 Use "stack ghc" for recompilation when stack.yaml exists
This makes it unnecessary for users of Stack to use a custom build
script, making installation easier.

Inspired by a similar feature in dyre:
a04be85f60

Fixes: https://github.com/xmonad/xmonad/issues/310
2021-08-17 10:46:07 +01:00
Tomas Janousek
aa35ea1856 Make xmessage handle UTF-8 and export it
This unfortunately breaks xmonad-contrib as several modules define their
own `xmessage` function.

Related: https://github.com/xmonad/xmonad/pull/309
2021-08-17 10:46:07 +01:00
Tomas Janousek
3b6d00ba91 Refactor 'recompile' to be less of a spaghetti-code
This is a preparation for autodetecting stack.yaml and using stack for
recompilation.

Related: https://github.com/xmonad/xmonad/issues/310
2021-08-17 10:46:07 +01:00
Tomas Janousek
befc4bc8d8 github: Drop FUNDING.yml
Replaced with https://github.com/xmonad/.github/blob/main/FUNDING.yml,
which points to xmonad's funding platforms instead of mine.

Related: https://github.com/xmonad/xmonad/pull/295
Related: https://github.com/xmonad/xmonad-contrib/pull/544
2021-08-15 22:27:39 +01:00
Tomas Janousek
6c31aad683 github: Sync ISSUE_TEMPLATE with xmonad-contrib
Related: https://github.com/xmonad/xmonad-contrib/pull/533
2021-08-15 22:25:59 +01:00
Tomas Janousek
3e76270245 ci: Disable optimization in the haskell-ci workflow
Cuts a couple dozen seconds from the build. This was already disabled in
the Stack workflow and we just forgot to put it here as well.

Related: https://github.com/xmonad/xmonad-contrib/pull/580
2021-08-08 11:54:58 +01:00
Tomas Janousek
3a414660fc MAINTAINERS: Link to maintainers' GPG keys
I'm the only one with valid GPG key uploaded to GitHub, it seems. :-/

Fixes: https://github.com/xmonad/xmonad/issues/75
2021-08-08 00:53:36 +01:00
Tomas Janousek
453010bb6d Link to xmonad-docs instead of hackage (fixes some links)
Fixes broken links to X.U.Hacks and X.U.ClickableWorkspaces.
2021-08-07 15:14:12 +01:00
Tomas Janousek
2ac8f0ea27 INSTALL: Fixes to ease inclusion into xmonad-web
Jekyll doesn't do autolinks, so wrap the one bare link in angle
brackets.

Also, link to additional resources for configuring xinitrc/xsession.
2021-08-02 21:36:16 +01:00
Tomas Janousek
ad7288030f TUTORIAL: Fixes to ease inclusion into xmonad-web
Also makes it easier to copy & paste.
2021-08-02 15:09:43 +01:00
Tomas Janousek
206fc918bb ci: Verbose nix build 2021-08-01 22:21:43 +01:00
Tomas Janousek
f5a60f82ee Merge branch 'add-nix-flake' 2021-08-01 22:07:11 +01:00
Ivan Malison
a1ee3b4530 Add comment explaining that I am maintaining flake.nix 2021-08-01 22:06:50 +01:00
Ivan Malison
89218fc57d Add nix github workflow 2021-08-01 22:06:49 +01:00
Ivan Malison
71af4239bd Add a flake.nix file 2021-08-01 22:06:49 +01:00
Tomas Janousek
f1d6316526 Merge branch 'broadcast-destroy-window-events' 2021-07-31 15:25:08 +01:00
Yecine Megdiche
92d01e37a0 Broadcast DestroyWindowEvent to layouts
Some layout and layout modifiers that keep track of some window
properties don't do garbage collection and update their state when
windows are destroyed. By broadcasting this event, it should be easier
for layouts to clean up

Related: https://github.com/xmonad/xmonad-contrib/pull/474
2021-07-31 15:24:31 +01:00
Tomas Janousek
101c7052f4 Merge branch 'install-instructions' 2021-07-29 11:33:19 +01:00
Tomas Janousek
7b7feeca42 INSTALL: Use "console" syntax highlighting for console listings 2021-07-29 11:32:20 +01:00
Tomas Janousek
cbe7ee7c03 INSTALL: Add instructions for cabal-install
Related: https://github.com/xmonad/xmonad/issues/199
Related: https://github.com/xmonad/xmonad/issues/310
2021-07-29 11:32:20 +01:00
Tomas Janousek
8adb8463ab INSTALL: Generalize the instructions a bit
Mention installation from distro; factor preparation/download out of
the Stack section (prep for inclusion of cabal-install section).
2021-07-29 11:32:20 +01:00
Tomas Janousek
256eb29ef1 INSTALL: Add section about launching XMonad as default WM 2021-07-25 10:44:41 +01:00
Tomas Janousek
4ba9c8b8c1 INSTALL: Mention dependencies for a couple common distros 2021-07-25 10:44:41 +01:00
Tomas Janousek
98173777fe INSTALL: Drop indent from code blocks
Makes it easier to copy&paste.
2021-07-25 01:17:40 +01:00
Tomáš Janoušek
05aeef0dc2 Merge pull request #308 from liskin/release-infra
Automate and document releasing to Hackage + couple cleanups
2021-07-25 02:17:21 +02:00
Tomas Janousek
85787ce059 Automate and document releasing to Hackage
Pioneered in https://github.com/xmonad/X11, this adds automation for
Hackage releases and updates MAINTAINERS.md with simplified instruction
for the release procedure.

Related: https://github.com/xmonad/xmonad/issues/75
Related: https://github.com/xmonad/xmonad/issues/264
Related: https://github.com/xmonad/xmonad-contrib/issues/393
2021-07-24 18:13:11 +01:00
Tomas Janousek
d64aeba80f Revise extra-source-files in xmonad.cabal
Ship the new docs, drop tests as these get picked up automatically.
2021-07-24 18:13:11 +01:00
Tomas Janousek
72cbe0667d Revise dependencies a bit
utf8-string isn't needed since d9e3ebf531
(late 2014).

Most of the executable xmonad deps are unnecessary since
307b82a53d (early 2015).

Switch from data-default to data-default-class to avoid bringing in deps
and instances we don't need.
2021-07-24 18:13:11 +01:00
slotThe
af354f7528 Merge pull request #312 from mzrinsky/update-cheatsheet
Add help command to help message
2021-07-03 09:10:02 +02:00
Matt Zrinsky
1a4c95fac8 Add help command to help message.
Attempt to contribute to xmonad,
and add a simple line to the help message,
containing the help command itself.

Fixes xmonad/xmonad#287
2021-07-02 13:58:00 -05:00
slotThe
42d319545b Merge pull request #309 from MuhammedZakir/xmessage-fix
Use printf instead of echo for xmessage help
2021-06-27 08:27:00 +02:00
MuhammedZakir
2e6eb9068d Use printf instead of echo for xmessage help
In some shells—like bash—the entire help message is shown in a single
line because a newline is printed as a literal "\n" character when using
echo.  Some distributions[1] have /bin/sh linked to bash, and so new
users are likely to run into this at some point.

We could fix this by either removing show and explicitly adding an
escaped quotation symbol before and after the string, or by using printf
instead of echo.  The printf solution seems more portable[2] and so I
propose we go with that one.

[1]: f5b9a25cdd/nixos/modules/config/shells-environment.nix (L129-L143)
[2]: https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo/#65819
2021-06-27 08:09:33 +02:00
slotThe
13849c6230 INSTALL.md: Mention stack upgrade
Users who are installing stack on systems like Debian stable or Ubuntu
may get really old version of stack—much older than we currently
support.  Thankfully, stack can upgrade itself rather easily with one
single command, so mention that.

Fixes: https://github.com/xmonad/xmonad/issues/303
2021-06-15 10:10:17 +02:00
Tomas Janousek
6a7eb85e84 MAINTAINERS.md: Update xmonad core team, add hall of fame
More or less synced with https://xmonad.org/community.html

Fixes: https://github.com/xmonad/xmonad/issues/248
2021-06-07 10:35:48 +01:00
slotThe
2a3c358533 MAINTAINERS.md: Add slotThe
Related:
  - https://github.com/xmonad/xmonad/issues/75
  - https://github.com/xmonad/xmonad/issues/248
2021-06-07 09:44:29 +02:00
slotThe
28637d0db7 Tutorial: Remove ewmhFullscreen for versions < 0.17
Not released as of xmonad-contrib 0.16.

Related: https://github.com/xmonad/xmonad/issues/307
2021-06-07 08:44:50 +02:00
Tomas Janousek
b14b3ffcec Bump version number (pre-release 0.16.9999)
Merging NewSelect into Choose is a breaking change, bump the version to
let xmonad-contrib depend on it.
2021-06-04 19:03:11 +01:00
Tomáš Janoušek
bbb4a0ef25 Merge pull request #281 from slotThe/NewSelect
Merge X.L.LayoutCombinators.(|||) into X.L.(|||)
2021-06-04 18:22:40 +01:00
slotThe
9db74715f2 X.Layout: Simplify JumpToLayout implementation
The `choose` combinator is very general; much more so than the
combination of `switch`, `swap`, and `passOnM`.

We can thus replace most of the implementation with calls to `choose`
and properly put everything related to `JumpToLayout` under one guard.
Plus, we don't need to define any extra functions that would have to be
tested in some way.

Co-authored-by: Tomas Janousek <tomi@nomi.cz>
2021-06-04 18:17:13 +01:00
slotThe
5b064f474d Update CHANGES.md 2021-06-04 18:17:11 +01:00
slotThe
bffa6dc2ce Merge X.L.LayoutCombinators.(|||) into X.L.(|||)
The functionality of the former are quite handy to have in core and we
can do so with minimal code changes.

The drawbacks of this approach are

  1. We can't merge JumpToLayout into ChangeLayout because people may
     have imported JumpToLayout(..), which we can't simulate with type
     aliases, patterns, and the like.

  2. Because the internal structure of X.L.LayoutCombintors.(|||) is a
     bit different, this creates a regression for people who used
     NextLayoutNoWrap or Wrap in their configs.  We could work around
     this by creating fake instances of these fields in the new
     JumpToLayout constructor and simply not doing anything with them,
     but since this seems like quite an advanced and specific use-case,
     failing fast and hard (as opposed to adding deprecation messages
     and then "silently" not handling these messages) seems preferrable
     here.

Related: https://github.com/xmonad/xmonad-contrib/pull/493
Related: https://github.com/xmonad/xmonad-contrib/issues/116
2021-06-04 18:17:04 +01:00
Tomas Janousek
341dea5907 Call xrrUpdateConfiguration to sync Xlib's view of screen config
Xrandrint.h says:

    /*
     * if a configure notify on the root is recieved, or
     * an XRRScreenChangeNotify is recieved,
     * XRRUpdateConfiguration should be called to update the X library's
     * view of the screen configuration; it will also invalidate the cache
     * provided by XRRScreenConfig and XRRConfig, and force a round trip
     * when next used.  Returns invalid status if not an event type
     * the library routine understand.
     */

If any user code needs that info/cache to be correct, we need to call
this.
2021-06-01 18:45:36 +01:00
Tomas Janousek
676530307b Clean up uses of setClientMessageEvent
In sendRestart, switch to the new setClientMessageEvent' and pass no
data, as no data are expected. Passing currentTime was confusing, as
it's only interpreted as time in some ClientMessage types.

In killWindow, it's the other way around.
2021-06-01 18:41:21 +01:00
Tomas Janousek
09425bbe43 Bump version number (pre-release 0.16.999) 2021-06-01 18:37:19 +01:00
Tomas Janousek
1805666e9d Bump X11 dependency
We have a bunch of open PRs that need features added in X11-1.10.

Related: https://github.com/xmonad/xmonad/pull/274
Related: https://github.com/xmonad/xmonad/pull/273
Related: https://github.com/xmonad/xmonad-contrib/pull/545
Related: https://github.com/xmonad/xmonad-contrib/pull/546
Related: https://github.com/xmonad/xmonad-contrib/pull/399
2021-06-01 18:37:19 +01:00
slotThe
40cd2081da Upcase tutorial file, mention it in README
Related: https://github.com/xmonad/xmonad/pull/278#issuecomment-850717606
2021-05-29 07:41:18 +02:00
slotThe
66514127f3 Merge pull request #278 from slotThe/new-tutorial
Add New Tutorial
2021-05-28 17:43:03 +02:00
slotThe
fdc3bf0484 tutorial.md: Point IRC channel to libera.chat
The reference to the freenode matrix channel is deleted for now; though
it may be added back in later-on, as the matrix team is working towards
bridging Libera.  We may also link the other matrix channel at some
point.

Related: https://github.com/xmonad/xmonad-web/pull/21
2021-05-28 16:52:09 +02:00
slotThe
f97d2527ff tutorial.md: Use logTitles
Ever since 322e06eed9c4b23a465d2857df462b8a2d716246 `ppTitleUnfocused`
does not exist anymore, as it was moved to `X.U.Loggers.logTitles`.
This makes the tutorial aware of that.
2021-05-28 16:52:09 +02:00
slotThe
7199d953a7 Add tutorial.md 2021-05-28 16:52:09 +02:00
slotThe
15653d4669 Add INSTALL.md 2021-05-28 16:50:06 +02:00
Tomas Janousek
d64a22d8db Switch IRC channel to irc.libera.chat
Since freenode has decided to kick us off because we said that we're
moving to Libera:

    Topic for #xmonad is "This channel has moved to ##xmonad. The topic is in violation of freenode policy: https://freenode.net/policies"
    Topic set by freenodecom (~com@freenode/staff) on Wed, 26 May 2021 04:59:51

it is high time we change the official channel on the website—even
though we don't even have OP on Libera yet.

Related: https://github.com/xmonad/xmonad-web/pull/26/
Co-authored-by: slotThe <soliditsallgood@mailbox.org>
2021-05-26 10:55:52 +01:00
Tomas Janousek
2e9f8dc831 ci: Refresh caches once a month
GitHub Actions writes caches on the first miss and then never updates
them again. If the CI is used frequently, the caches never expire and
as they get old, become less useful.

To avoid this, force refreshing the caches once a month.

Related: 52751f47d0
2021-05-24 23:19:30 +01:00
Tomas Janousek
e8bfc5bb69 ci: Prevent ~/.stack/pantry cache from being empty
When building with an LTS version that has exactly the dependencies we
need (X11-1.9.2), stack doesn't need to download the Hackage index. If
GitHub Actions cache locking chooses this job as the one that writes the
cache, then the "stack-pantry-Linux" cache entry stays empty, possibly
forever.

Force Hackage index update to prevent this from happening.

Related: d1a4820b55
2021-05-24 23:13:45 +01:00
Joan Milev
9e5b16ed8a Remove all instances of deriving Typeable
The derivation of Typeable has been automatic for quite a while now.

Related: https://github.com/xmonad/xmonad-contrib/issues/548
2021-05-24 22:56:36 +01:00
Tomas Janousek
d72da951c9 Use -Wno-* instead of the obsolete -fno-warn-* 2021-05-24 11:19:53 +01:00
Tomas Janousek
90101613e7 Unclobber dirs/Dirs in import XMonad
"dirs" is used several times in xmonad-contrib as a short for
"directions" and when I tried renaming those uses, I had a really hard
time coming up with something nice/meaningful. Therefore I think it's
best if we rename this instead, "dirs" is a valuable part of the
namespace. :-)

Fixes: 735fb58f6c ("Revise XDG handling")
2021-05-24 11:15:00 +01:00
Tomas Janousek
6caac22df1 ci: git-friendly apt deps in cabal.haskell-ci 2021-05-23 18:33:00 +01:00
Tomas Janousek
e9987b1adf Merge branches 'pr/grabkeys-nosymbol', 'pr/extensible-config' and 'pr/funding' 2021-05-21 11:49:52 +01:00
Tomas Janousek
383ffb772e Prevent grabKey from accidentally grabbing all unbound keys
grabKeys doesn't check that a KeySym is valid before looking up the
KeyCode(s) it's bound to. In particular, KeySym 0 (NoSymbol) gets mapped to every
unbound KeyCode, since that's what XKeycodeToKeysym returns for those.

This can most easily be reproduced using `statusBar` in xmonad-contrib,
with def as the key-mapping function; this unexpectedly invokes the
following instances:

    instance Default b => a -> b where def = const def
    instance (Default a, Default b) => (a, b) where def = (def, def)
    instance Default CInt where def = 0

thus producing a function which binds `toggleStruts` to the (KeyMask,
KeySym) pair (0, 0) (and demonstrating why Default is a dangerous
abstraction). The person who reported this used xmodmap to clear KeySyms
from their numpad keys, and then xmonad would bind strut toggling to the
entire numpad.

Note that it is not reliably possible to override `def` in this
situation (without introducing a new type); the only thing we can do is
try to avoid the aftermath, and I would expect that an inadvertent 0
KeySym is the most common error here anyway.

Fixes: https://github.com/xmonad/xmonad/issues/293
Fixes: 40cb12ce17 ("Grab all keycodes linked to each keysym, not just one")
Co-authored-by: Brandon S Allbery KF8NH <allbery.b@gmail.com>
2021-05-21 11:41:52 +01:00
Tomas Janousek
6379307baa Add Sponsor button to GitHub
These past months I've spent a lot of time working on xmonad¹ and I feel
like I've done a lot. This is, however, not sustainable long term. :-(

¹) primarily xmonad-contrib and the community in general

I'd like to try making my GitHub Sponsors profile a bit more visible,
hoping that would allow me to continue dedicating time to xmonad.

I know that the correct approach probably is for the xmonad project to
find a fiscal sponsor like the Software Freedom Conservancy or Open
Collective or something and accept donations as a project, and then
redistribute that to people, but I don't think the project has enough
momentum to do something as complicated as that. :-/

Related: https://github.com/xmonad/xmonad-contrib/pull/544
2021-05-18 16:53:07 +01:00
Tomas Janousek
d620639f7d Add support for extensible config in contrib modules
It's often difficult to make contrib modules work together. When one
depends on a functionality of another, it is often necessary to expose
lots of low-level functions and hooks and have the user combine these
into a complex configuration that works. This is error-prone, and
arguably a bad UX in general.

This commit presents a simple solution to that problem inspired by
"extensible state": extensible config. It 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
other use cases I haven't thought of yet.

For more, see the related pull request to xmonad-contrib.

Related: https://github.com/xmonad/xmonad-contrib/pull/547
2021-05-17 17:46:48 +01:00
Tomas Janousek
a5cee9bac2 ci: Fix failing installation of GHC
Since actions/virtual-environments#3268, the GHC PPA is no longer
enabled by default. :-(

(It would be better to use the haskell/actions/setup action, but it
insists on doing `cabal update` even if all we need is to install GHC
and Stack, which adds noticeable delay:
https://github.com/haskell/actions/issues/29)
2021-05-14 19:20:16 +01:00
github-actions
131fd3669f man: Update 2021-04-27 11:05:32 +01:00
Tomas Janousek
56f810d182 GenerateManpage: Drop all dependencies except base and invoke in CI
pandoc's API changes often enough that distros like Debian were patching
our GenerateManpage.hs to work with their version of pandoc, and it
doesn't build against any Stackage LTS except the recently released
LTS-17. Also, building pandoc from source takes quite some time and
resources.

But for what benefit? We're not using any special pandoc functionality
whatsoever. It's just that it was all in Haskell and the entire build
was orchestrated by cabal, but is all that complexity and resource
consumption worth it? I think not.

(Frankly, this whole thing was just a massive waste of time as the help
text in Config.hs isn't generated at all, so we still need to do this
manually. And then, the default key-bindings in core are unlikely to
change ever again.)

Let's simplify this:

* drop all dependencies except base and just run it through runhaskell

* add a Makefile and GH Actions workflow that invokes this after push

* only ship the results in release tarball, not the scripts which are
  considered our dev infrastructure

Closes: https://github.com/xmonad/xmonad/issues/283
Related: https://github.com/xmonad/xmonad/pull/260
Related: https://github.com/xmonad/xmonad/pull/261
Related: https://github.com/xmonad/xmonad/pull/265
Related: https://github.com/xmonad/xmonad/pull/266
Related: https://github.com/xmonad/xmonad/pull/267
Related: https://github.com/xmonad/xmonad/pull/268
2021-04-27 11:05:32 +01:00
Tomáš Janoušek
46f637e0be Merge pull request #289 from slotThe/maintainers
Update MAINTAINERS.md: add twitter, website references
2021-04-08 21:14:57 +01:00
slotThe
095d0e37d6 Update MAINTAINERS.md: add twitter, website references
Relevant mailing list discussion for the website part is available at
[1].

[1]: https://mail.haskell.org/pipermail/xmonad/2020-February/015358.html
2021-04-08 19:29:38 +02:00
Tomáš Janoušek
7e798afd11 Merge pull request #288 from slotThe/minimal-pragma
Add `MINIMAL` for `ExtensionClass` and `LayoutClass`
2021-04-07 18:33:07 +01:00
slotThe
669a9aed9e LayoutClass: update documentation
Make it clearer that we in fact do not require implementations for a
minimal complete definition, but that the default implementations have a
certain kind of dependency structure.
2021-04-07 10:12:15 +02:00
slotThe
c869129c71 ExtensionClass: Use MINIMAL pragma 2021-04-07 10:12:14 +02:00
Tomáš Janoušek
b8523a3c9b Merge pull request #202 from wygulmage/stack_instances
Add Functor, Foldable, and Traversable instances for StackSet.Stack
2021-04-04 09:56:45 +01:00
Tomas Janousek
400730fe3b Disable quickcheck-classes when building with stack
Stack doesn't support automatic flags: it doesn't backtrack when
dependency resolution fails using the default value of an automatic
flag, it just fails the build plan construction. We can't use automatic
flags to check if quickcheck-classes is available, and since the code is
tested by the haskell-ci.yml cabal workflow anyway, let's just disable
it here. It's not worth the hassle trying to enable it for select LTS
versions only. It's too much noise already, actually. :-(

Further reading:
https://cabal.readthedocs.io/en/latest/cabal-package.html#resolution-of-conditions-and-flags
https://github.com/commercialhaskell/stack/issues/1313#issuecomment-157259270
2021-04-03 17:13:25 +01:00
Tomas Janousek
6c5204b91c Simplify quickcheck-classes tests for Stack
We don't need the compat hacks for GHC 8.4.
2021-04-03 17:13:25 +01:00
Tomas Janousek
910d99cb74 Update CHANGES 2021-04-03 17:13:25 +01:00
Tomas Janousek
031bbd6230 Make quickcheck-classes dependency optional 2021-04-03 17:13:25 +01:00
Keith
05e8c204e9 Add quickcheck-classes tests for Stack
The tests are implemented by using a newtype wrapper `TestStack`. This is to
avoid creating `Eq1` and `Show1` instances for `Stack` itself, which are needed
by quickcheck-classes to run with GHC less than 8.5. Tests are automatically
generated by `traversalLaws` and `foldableLaws` using the `Arbitrary` instance
for `TestStack`.
2021-04-03 17:13:25 +01:00
Keith
2c91ea1621 Add Foldable, Functor, and Traversable instances for Stack
`Functor` is provided by DeriveFunctor.
`Foldable` uses `integrate` (`Stack`'s `toList`).
`Traversable` uses the `Reverse` Applicative to traverse the `up` list in
reverse order.
2021-04-03 17:13:25 +01:00
Tomas Janousek
5cdf428f55 ci: Enable -Werror
We don't want to ignore warnings do we?
2021-03-31 12:00:30 +01:00
Tomas Janousek
22b579bd14 Drop deprecated migrateState
migrateState is only necessary when _live_ upgrading from xmonad 0.12,
so I believe we can drop it now.

This fixes a compilation warning that we'd otherwise have to suppress.
2021-03-31 12:00:30 +01:00
Tomas Janousek
14d9fa247a Drop "testing" cabal flag
Since 8863761d66 (early 2014), the test
suite is a proper cabal test-suite and needs the "xmonad" library to be
built, and thus the flag serve no purpose.

Related: https://github.com/xmonad/xmonad/issues/283
2021-03-31 12:00:30 +01:00
Tomas Janousek
cfe99998fc ci: Cache pantry (hackage metadata) separately
This further reduces our usage of cache storage by caching the metadata
only once per repo. Now the metadata cache is ~250MB and the individual
caches with built dependencies are ~5M each.

Related: https://github.com/xmonad/X11/pull/75
Related: https://github.com/xmonad/xmonad/issues/283
2021-03-31 12:00:30 +01:00
Tomas Janousek
9fce3805fc ci: Use system GHC in Stack to not waste GH Actions cache space
Stack installation of GHC takes more than a gigabyte, so caching it
wastes space (GitHub docs say it will be aggressively evicted after
reaching the 5G limit) and also time (compression, decompression).

Related: https://github.com/xmonad/X11/pull/75
Related: https://github.com/xmonad/xmonad/issues/283
2021-03-31 12:00:30 +01:00
Tomas Janousek
fd243ca1c1 ci: Refresh tested-with
Related: https://github.com/xmonad/X11/pull/75
Related: https://github.com/xmonad/xmonad/issues/283
2021-03-31 12:00:30 +01:00
Tomas Janousek
c90df53081 tests: Fix build with GHC 9
Related: https://github.com/xmonad/xmonad/issues/283
2021-03-31 12:00:30 +01:00
Tomas Janousek
e4659c2475 ci: Move apt deps to cabal.haskell-ci and regenerate haskell-ci
Related: https://github.com/xmonad/X11/pull/75
Related: https://github.com/xmonad/xmonad/issues/283
2021-03-31 11:59:13 +01:00
Tomas Janousek
caae51c399 ci: Minor cleanup
* rename workflow to Stack

* test all branches (provides feedback before opening a PR)

* split into buildenv deps and stack build

* make the deps list more git-friendly; tweak the deps

* use stack from github environment (I've seen the curl fail intermittently)

* use `*.cabal` to minimize diff between our repos

Related: https://github.com/xmonad/X11/pull/75
Related: https://github.com/xmonad/xmonad/issues/283
2021-03-31 11:58:07 +01:00
Tomas Janousek
fb390fa9cc Revert "Revert "Re-enable lts based testing""
This reverts commit 8a8d5f71b1.

There will be a separate GH Actions workflow for rebuilding the manpage,
and generatemanpage will be dropped from xmonad.cabal (see
https://github.com/xmonad/xmonad/issues/283) therefore the revert no
longer makes sense and the stack workflow can indeed be reverted back to
a working state so we can continue from there.

For more information, see the following revenge revert storm:

Related: https://github.com/xmonad/xmonad/issues/283
Related: https://github.com/xmonad/xmonad/pull/260
Related: https://github.com/xmonad/xmonad/pull/261
Related: https://github.com/xmonad/xmonad/pull/265
Related: https://github.com/xmonad/xmonad/pull/266
Related: https://github.com/xmonad/xmonad/pull/267
Related: https://github.com/xmonad/xmonad/pull/268
2021-03-31 11:52:57 +01:00
Tomáš Janoušek
4a0b166998 Merge pull request #275 from liskin/pr/notifications-obscured-by-floats
Fix obscuring notifications when moving floats
2021-03-21 21:30:36 +00:00
Tomas Janousek
b9ce5b034b Fix obscuring notifications when moving floats
Since 4565e2c90e, not only do we raise the floating window to top when
starting dragging, we also restack all other managed windows directly
below it (mouse{Move,Resize}Window now call `float` while dragging,
which invoke `windows` and that restacks all windows). This means that
as soon as we start dragging, all xmonad-managed windows are raised to
the top, obscuring any unmanaged (override-redirect) windows that were
at the top before.

The good thing about 4565e2c90e is that since we refloat and refresh
on every mouse move while dragging, we no longer need the `raiseWindow`.
It was probably only there so that it stays visible when dragging
between Xinerama screens (3cb64d7461), and that's taken care of by
refloating the window on every move. As the refresh restacks everything
anyway, the only function of the `raiseWindow` is to stack windows above
unmanaged ones, which is likely undesirable.

There is still one known issue related to obscuring notifications:
https://github.com/xmonad/xmonad/issues/89
That one is caused by X opening new windows at the top of stacking order
and xmonad then restacking all other windows directly beneath them if
the new window is in the master position of the stack. Some notification
daemons like dunst work around this by raising themselves whenever a new
window is opened
(77bfbc4f7f/src/x11/x.c (L348)).
Fixing this one is considerably more complicated as we'd need to keep
track of (unmanaged) windows that wish to be stacked above new windows,
therefore this won't be addressed here, and probably won't be addressed
in xmonad core ever.

Fixes: https://github.com/xmonad/xmonad/issues/208
Fixes: 4565e2c90e ("fix #63: window jumping to origin position when dragging")
2021-02-16 12:00:28 +00:00
Tomáš Janoušek
a90558c07e Merge pull request #223 from wygulmage/cleanup-tests
deleted unused 'mul' definition in 'prop_aspect_fits'.
2021-01-26 01:15:01 +01:00
Sibi Prabakaran
56b0f850bc Merge pull request #200 from wygulmage/strict-rectangle
Make ScreenDetail a newtype and RationalRect strict in its contents
2021-01-23 19:46:15 +05:30
Peter Simons
51a179dc68 Merge pull request #267 from xmonad/revert-266-reenable-lts
Revert "Re-enable lts based testing"
2021-01-20 12:48:44 +01:00
Peter Simons
8a8d5f71b1 Revert "Re-enable lts based testing" 2021-01-20 12:48:13 +01:00
Tomáš Janoušek
4b69a456cc Merge pull request #266 from xmonad/reenable-lts
Re-enable lts based testing
2021-01-20 12:27:53 +01:00
Sibi Prabakaran
e12d0be1b2 Re-enable lts based testing 2021-01-20 16:20:58 +05:30
Peter Simons
002326ceb1 Merge pull request #265 from xmonad/t/haskell-ci
Generate CI with the haskell-ci utility.
2021-01-19 20:58:01 +01:00
Peter Simons
758e3d85e6 Generate CI with the haskell-ci utility. 2021-01-19 20:48:16 +01:00
Keith
f5bd77a7f8 made ScreenDetail a newtype and RationalRect strict in its contents. 2021-01-19 12:06:07 -05:00
Tomáš Janoušek
519c79a57e Merge pull request #263 from slotThe/exception-extensible
Control.Exception.Extensible -> Control.Exception
2021-01-17 20:39:00 +01:00
slotThe
0aa40480f9 Control.Exception.Extensible -> Control.Exception
According to its documentation[1], this module simply re-exports
Control.Exception on recent GHC versions.  As we only support recent
versions, this import is unnecessary.

[1] http://hackage.haskell.org/package/extensible-exceptions-0.1.1.4/docs/Control-Exception-Extensible.html
2021-01-12 11:57:34 +01:00
Peter Simons
36dd6afb49 Merge pull request #261 from xmonad/t/switch-stack-builds-to-nightly
stack.yaml: switch from Stackage LTS to Nightly
2021-01-10 19:36:12 +01:00
Peter Simons
3015968ee4 Run CI builds with Stackage Nightly.
Unfortunately, our CI builds have a hard-coded assumption that all kinds of
LTS-X builds can be performed with the same stack.yaml file, which is not true.
We need to use specialized build instructions for each version of the
environment we're building in, e.g. stack-lts-16.yaml, stack-nightly.yaml, and
so on. Making that change is not super hard, but it's not completely trivial
either because the hooks use a hash of the stack file to determine whether the
cash is still valid, etc., and I'm not entirely sure about the best way to
adapt that code to support multiple YAML files.

For the time being, the most important build IMHO is Stackage Nightly,
because (a) we really want to support the nightly snapshot since (b) it can
compile the genmanpage executable relatively easily.
2021-01-09 19:46:50 +01:00
Peter Simons
5bb6c88b41 stack.yaml: switch from Stackage LTS to Nightly
Let's use a moderately recent compiler like ghc-8.10.3
instead of the old 8.8.x branch for development. Same goes
for our dependencies; development should take place with
recent versions of our dependencies.
2021-01-09 18:06:17 +01:00
Tomáš Janoušek
f875a56620 Merge pull request #258 from slotThe/xdg-things
Revise XDG handling
2021-01-07 19:52:51 +01:00
slotThe
70a75e5e3f Update documentation 2021-01-07 17:25:30 +01:00
slotThe
735fb58f6c Revise XDG handling
Improve handling of the whole XDG situation.  This now looks as follows

  1. If all three of xmonad's environment variables (XMONAD_DATA_DIR,
     XMONAD_CONFIG_DIR, and XMONAD_CACHE_DIR) are set, use them.
  2. If there is a build script `build' or configuration `xmonad.hs' in
     `~/.xmonad', set all three directories to `~/.xmonad'.
  3. Otherwise, use XDG_DATA_HOME, XDG_CONFIG_HOME, and
     XDG_CACHE_HOME (or their respective fallbacks).

If none of the above exist, we default to using the XDG_* variables,
creating the necessary directories if needed.
2021-01-07 15:32:02 +01:00
Tomas Janousek
e8f48b77f9 Sync MAINTAINERS.md with reality on GitHub 2021-01-03 14:05:32 +01:00
Sibi Prabakaran
e363c44bb0 Merge pull request #259 from vmchale/master
Accord with new pattern-match
2021-01-01 11:52:44 +05:30
Vanessa McHale
ec1c3e0159 update changelog 2020-12-31 20:19:10 -06:00
Vanessa McHale
2a1a18023a Accord with new pattern-match 2020-12-31 20:17:22 -06:00
Tomas Janousek
ff738988d3 github: Sync test workflow with that of xmonad-contrib 2020-12-16 11:24:15 +00:00
Sibi Prabakaran
fc4657d529 Merge pull request #252 from slotThe/unused-imports
Get rid of unused imports
2020-12-16 10:30:05 +05:30
Tomáš Janoušek
bd961b7866 Merge pull request #255 from felixonmars/patch-1
Correct a typo in Layout.hs
2020-12-16 01:16:05 +00:00
Felix Yan
3df77d6f20 Correct a typo in Layout.hs 2020-12-16 00:54:36 +08:00
Yecine Megdiche
b59c768cdd Updated CONTRIBUTING.md and MAINTAINERS.md (#253)
* Updated CONTRIBUTING.md and MAINTAINERS.md

* Fixed broken GitHub links
* Added instructions on how to run tests
* Linked XMonad.Doc.Developing

* updated hackage link
2020-12-15 21:19:07 +05:30
Sibi Prabakaran
a37a3cb6e8 Merge pull request #254 from slotThe/fix-LR
Rename LR = L | R to CLR = CL | CR
2020-12-14 18:07:30 +05:30
slotThe
823581816a Conditionally error on unused imports
This turns off the warnings about unused imports _unless_ one is using
the oldest supported version of GHC (right now that's 8.4.4 or older);
then it turns them into errors!  This prevents xmonad from emitting
warnings about imports that have to be there due to backwards
compatibility, but are obsolete in newer versions (think MFP), while at
the same time preventing bitrot.
2020-12-13 22:15:41 +01:00
slotThe
3ea0d74954 Drop unsupported GHC versions from tested-with
Removes all versions of GHC that are not part of the CI, as these can't
be guaranteed to be built against master every time
2020-12-13 22:15:41 +01:00
slotThe
b3c860b892 Rename LR to CLR
Some modules in xmonad-contrib define their own LR type with L and R as
data constructors, leading to build failures; this fixes that.
2020-12-13 17:23:14 +01:00
slotThe
958e701bf4 Get rid of unused imports
Starting with 28e75da77f we only support
GHC versions 8.4.3 and up (more precisely, the GHC version associated
with stackage lts-12.0 and up).  The imports in question are now in
Prelude and need not be imported explicitly.
2020-12-13 15:57:19 +01:00
Sibi Prabakaran
28e75da77f Merge pull request #251 from xmonad/github-ci
Add github CI integration
2020-12-12 12:51:48 +05:30
Sibi Prabakaran
a812869c0c Use v2 of cache. Thanks to @liskin for the help! 2020-12-12 12:15:40 +05:30
Sibi Prabakaran
96fb01b9be Add lts-12 2020-12-12 12:09:15 +05:30
Sibi Prabakaran
6fa0bb7d4f Remove travis rule 2020-12-11 18:10:35 +05:30
Sibi Prabakaran
0a040cbc96 Add github CI integration 2020-12-11 17:49:45 +05:30
Sibi Prabakaran
96d4f1fe85 Merge pull request #219 from jumper149/master
Allow pattern-matching on 'XMonad.Layout.Choose' by exporting constructors
2020-12-08 21:14:27 +05:30
Felix Springer
30f2d9f325 Merge branch 'master' into master 2020-12-08 16:41:36 +01:00
Sibi Prabakaran
5be975b4f2 Merge pull request #247 from srid/patch-1
Fix dzen URL
2020-11-25 11:23:56 +05:30
Sridhar Ratnakumar
22c370a068 Fix dzen URL
dzen has moved to GitHub
2020-11-24 12:40:10 -05:00
Brent Yorgey
c3e032e08e Merge pull request #242 from liskin/layoutclass-typeable
Make layouts Typeable
2020-11-09 14:14:13 -06:00
Peter Simons
2c3bf17dfb Merge pull request #244 from blackgnezdo/flag-fix
Add required + before flag name cabal.project
2020-11-09 07:40:21 +01:00
Greg Steuck
a926b68838 Add required + before flag name cabal.project
This fixes the build with cabal-3.4.
2020-11-08 20:53:28 -08:00
Tomas Janousek
6dc1e319d1 Make layouts Typeable
This makes it possible to query the current layout state, which might be
useful to e.g. show the current X.L.WorkspaceDir in xmobar.

Example of use (assuming myLayout is the layout that is assigned to
layoutHook):

    asMyLayout (Layout l) = (`asTypeOf` myLayout) <$> cast l

    …

    layout <- asMyLayout . W.layout . W.workspace . W.current <$> gets windowset
    case layout of
        Just (WorkspaceDir d) -> …

Unfortunately this requires adding the Typeable constraint to a bunch of
classes in xmonad-contrib, so we need to merge those changes there first
(fortunately it doesn't need to go in lockstep, adding a Typeable
constraint to those classes in xmonad-contrib is harmless).
2020-11-04 15:28:04 +00:00
Peter Simons
0db71d552a Merge pull request #221 from liskin/float-dimensions
Use current screen to set dimensions of new floating windows
2020-08-25 12:49:53 +02:00
Peter Simons
f52ed1d19e Merge pull request #224 from dreamsmasher/master
Fixed typo in Main.hs
2020-08-25 12:48:03 +02:00
Peter Simons
ffcb01ad80 travis.yml: build GenerateManpage when compiling with ghc-8.6.x or later
Older versions will probably choke on pandoc-2.10.x.
2020-08-25 11:50:52 +02:00
Peter Simons
66d2241703 GenerateManpage: port the utility to pandoc 2.10.x 2020-08-25 11:46:30 +02:00
Peter Simons
11814bfec3 cabal.project: enable the generatemanpage flag by default in developer builds 2020-08-25 11:46:06 +02:00
Peter Simons
bbc1c010ed Re-generate the Travis-CI build instructions with the latest version of haskell-ci. 2020-08-25 11:45:51 +02:00
Norman Liu
eeeae810ae Fixed typo in Main.hs 2020-05-29 17:11:09 -04:00
Keith
3c6f52a349 deleted unused 'mul' definition in 'prop_aspect_fits'. 2020-05-25 13:03:08 -04:00
Sibi Prabakaran
40466b2be2 Merge pull request #222 from SCKelemen/patch-1
Nit: fix grammar/spelling issue
2020-05-23 12:43:36 +05:30
Samuel Kelemen
6a4a742feb Nit: fix grammar/spelling issue 2020-05-22 23:56:52 -04:00
Tomas Janousek
f8b243b66e Use current screen to set dimensions of new floating windows
This fixes a bug when using multiple screens with different dimensions,
causing some floating windows to be smaller/larger than the size they
requested.

Some applications (e.g. pinentry) always map their window at (0, 0) so
floatLocation would compute the window size relative to the screen
containing (0, 0) and if the current workspace is on another screen with
a different size, this relative size results in a different absolute
size, which is undesirable for fixed size floats.

Other applications like ssh-agent place their window at the center of
the framebuffer (ignoring xinerama layout). Same problem.

Then there are apps that remember their position/size when minimizing to
tray and then attempt to restore it when reopened. Again, if they
restore it on another screen, we miscalculate the size.

The fix is to use the current screen for calculating dimensions of new
(not yet mapped) floating windows.

Co-Authored-By: Vincent Vinel <narthorn@gmail.com>
2020-05-13 20:11:32 +02:00
Felix Springer
fa9a3abe49 Allow pattern-matching on 'XMonad.Layout.Choose' by exporting constructors 2020-03-31 22:46:56 +02:00
Sibi
78b967198b Merge pull request #215 from anandijain/master
update dead dmenu link
2020-03-24 15:21:53 +05:30
anand jain
68574be2cf Update README.md
Co-Authored-By: Tomáš Janoušek <tomi@nomi.cz>
2020-03-19 15:08:49 -05:00
anand jain
2ab37aa4a4 update dead dmenu link 2020-03-18 19:56:32 -05:00
Peter Simons
5ab9fede6c Merge pull request #204 from owm111/patch-2
Make "Default Keyboard Bindings" a heading
2019-12-27 11:38:48 +01:00
Owen McGrath
a81ba4ba53 Make "Default Keyboard Bindings" a heading
It looks like it should be a heading, but there wasn't a space so it wasn't one. Looked strange in the manpage.
2019-12-18 18:36:43 -06:00
Sibi
b65728032d Merge pull request #203 from owm111/patch-1
Update example config link
2019-11-17 12:08:20 +05:30
Owen McGrath
e747377775 Update example config link
The link for the example config leads to file saying "go here instead"; change initial link to "there".
2019-11-16 12:50:38 -06:00
Peter Simons
d6f88918de Merge pull request #196 from BPDanek/patch-1
punctuation change
2019-07-14 17:38:55 +02:00
Benjamin Danek
21cd920b61 punctuation change 2019-07-10 15:16:24 -07:00
Peter Simons
bb13853929 Bump version number, update changelog, and re-generate the man page. 2018-09-30 13:34:01 +02:00
Brent Yorgey
3d1720c3f3 Merge pull request #176 from LSLeary/sendmessage
Reimplement sendMessage to deal properly with windowset changes made during handling
2018-09-12 17:31:18 -05:00
L. S. Leary
0614ffb65c XMonad.Operations
* Add `windowBracket`: provide a means of handling windowset changes
   made during the course of arbitrary `X` actions. Buys composability.
 * Add `windowBracket_` variant.
 * Add `modifyWindowSet` utility for use with the above.
 * Re-implement `sendMessage` using `windowBracket_` so that its refresh
   handles changes made to the windowset by the message handler.
2018-09-13 08:19:37 +12:00
Sibi
85b47fc3ac Merge pull request #182 from nikolas/patch-1
Fix typo in delete test comment: identiy -> identity
2018-08-31 22:57:03 +05:30
nikolas
1a99280227 typo fix in delete test comment: identiy -> identity 2018-08-31 12:37:04 -04:00
Peter Simons
e8133eb9a6 CHANGES.md: add an entry for the 0.14.2 version 2018-08-24 12:11:20 +02:00
Peter Simons
4ccaff8f25 xmonad.cabal: bump version number to 0.14.2 for release 2018-08-21 09:59:02 +02:00
Peter Simons
56dc186e68 xmonad.cabal: the author attribute is free-form, not a list
I rather not trust other code that interprets that file to know how to strip
the white space before the commas.
2018-08-21 09:57:59 +02:00
Peter Simons
10b2efe81c xmonad.cabal: add missing xmonad.hs file to the tarball again
Fixes https://github.com/xmonad/xmonad/issues/181.
2018-08-21 09:57:11 +02:00
Peter Simons
49c69fa73b xmonad.cabal: require at least base version 4.9
We need GHC 8.x and beyond for Data.Semigroup, Control.Monad.Fail, etc.
2018-08-20 14:06:32 +02:00
Peter Simons
120ebce490 xmonad.cabal: simplify and modernize the Cabal file
It's also pretty-printed with 'stylish-cabal' for consistent formatting.
2018-08-20 14:05:33 +02:00
Peter Simons
c0cf91303f Tentatively bump version number to 0.14.1. 2018-08-20 13:49:34 +02:00
Peter Simons
80f1c6f027 CHANGES.md: move PR entries into the right section 2018-08-20 13:49:34 +02:00
Peter Simons
c54e7088f0 Merge pull request #167 from mgsloan/log-recompilation-info
Log information about xmonad compile + avoid unnecessary recompile
2018-08-20 13:49:10 +02:00
Peter Simons
1f3a27f9b9 Don't build generatemanpage with ghc 8.6.x yet: we're lacking pandoc. 2018-08-20 12:44:16 +02:00
Peter Simons
ec97d83f3f GenerateManpage: fix compiler warnings 2018-08-20 12:01:33 +02:00
Peter Simons
f0975b734c git: ignore "cabal new-build"-style artifacts 2018-08-20 12:01:33 +02:00
Peter Simons
2324266fae travis.yml: build with -fgeneratemanpage 2018-08-20 12:01:33 +02:00
Peter Simons
3b0559c6cc Merge pull request #129 from madnight/patch-2
Change comment for grabButtons in Main.hs
2018-08-20 11:47:45 +02:00
Peter Simons
886a0d4041 GenerateManpage: greatly simplify the code
We can take advantage of modern Pandoc features to move information like the
release date, the man page section, etc. into the markdown source rather than
having to insert that data during the rendering process. The only thing that
remains to be figured out by this tool is the set of known key bindings.
2018-08-20 11:35:31 +02:00
Peter Simons
98f39eabc1 xmonad.cabal: don't depend on semigroups when building with GHC 8.x
Recent compiler versions have Data.Semigroup in 'base'.
2018-08-20 10:40:03 +02:00
Peter Simons
425c3c0872 Core: derive 'MonadFail X' instance for GHC 8.6.x and beyond
A side effect of that change is that our code no longer compiles with GHC
versions prior to 8.0.x. We could work around that, no doubt, but the resulting
code would require CPP and Cabal flags and whatnot. It feels more reasonable to
just require a moderately recent compiler instead of going through all that
trouble.
2018-08-20 10:40:03 +02:00
Peter Simons
29c9819daa xmonad.cabal: update constraints on 'base'
- Our code does not compile with versions prior to 4.6, because we need
   System.Environment.lookupEnv.

 - Our code does not compile with version 4.12 (GHC 8.6.x) and beyond.

Closes https://github.com/xmonad/xmonad/issues/180.
2018-08-20 08:45:11 +02:00
Sibi
3c2b09c213 Merge pull request #179 from countermeasure/patch-1
Update Debian packages in README
2018-08-07 10:35:16 +05:30
Sky
64a660894d Update Debian packages in README 2018-08-06 18:42:59 -07:00
Peter Simons
27b1ce9dd7 Merge pull request #98 from tmciver/master
Update README to add xrandr dependency and add build/install instruct…
2018-07-30 12:43:33 +02:00
Peter Simons
5caf235f6b CHANGES.md: document recent changes 2018-07-30 12:42:16 +02:00
Peter Simons
4ef9c12d13 Merge pull request #99 from gliptak/warnings1
Cleanup build warnings
2018-07-30 12:40:09 +02:00
Peter Simons
d6705fd595 Merge pull request #128 from madnight/patch-1
Remove unused CPP extension from Core.hs
2018-07-30 12:38:16 +02:00
Peter Simons
7c1065c43f Merge pull request #127 from dudebout/patch-1
remove the man pages from data-files
2018-07-30 12:31:26 +02:00
Peter Simons
af104509c3 GenerateManpage does not compile with Cabal 2.2.x. 2018-07-30 11:56:07 +02:00
Michiel Derhaeg
586ee75a9a fix manpage generation 2018-07-30 11:53:58 +02:00
Clint Adams
013da018a1 Port GenerateManpage.hs to pandoc 2
Closes: #123

There is a regression here in terms of aesthetics.
2018-07-30 11:53:58 +02:00
Peter Simons
71cb355948 travis.yml: we can now run "cabal check" successfully 2018-07-30 11:06:02 +02:00
Peter Simons
19069b3d4b xmonad.cabal: drop hard-coded profiling mode
Hackage won't accept the package with that "hack" in place. If you want to
compile with profiling enabled, please configure the build with
--enable-profiling via "cabal" or "stack" or whatever build driver you're
using.
2018-07-30 11:03:53 +02:00
Peter Simons
969fca9406 Merge pull request #157 from xmonad/travis
Travis
2018-07-30 10:44:27 +02:00
Peter Simons
61f00e65f1 Merge pull request #169 from mimi1vx/patch-1
Allow  X11-1.9
2018-07-30 09:43:07 +02:00
Peter Simons
db11089e70 travis.yml: re-generate with latest version of make-travis-yml 2018-07-30 09:36:19 +02:00
Peter Simons
e601a7d16d xmonad.cabal: updated tested-with fields to the latest major release, respectively 2018-07-30 09:33:03 +02:00
Peter Simons
0dd23bddfa Merge branch 'master' into travis. 2018-07-30 09:32:12 +02:00
Peter Simons
55b14d4850 Bump version number to 0.14 for upcoming release. 2018-07-30 09:29:12 +02:00
Ondřej Súkup
9df514b378 Allow X11-1.9 2018-05-15 09:45:36 +02:00
Michael Sloan
b6d92b4e38 Log information about xmonad compile + avoid unnecessary recompile
Particularly with the addition of build scripts, it can be tricky to figure out
what XMonad is doing when attempting recompilation.  This makes it clearer by
adding some logging.

Due to this logging, I noticed that the lag of xmonad start was because it was
always recompiling!  When I startup my computer, I do not want it to delay
rebuilding my window manager. This also fixes that issue such that it only
recompiles XMonad if it is going to reinvoke due to getProgName not being the
expected string.
2018-05-09 18:41:46 -07:00
geekosaur
ecf1a0ca0d Merge pull request #163 from aplaice/patch-1
Fix typo Utils -> Util
2018-04-18 18:25:18 -04:00
aplaice
d216e95f97 Fix typo Utils -> Util
This is extremely minor, but it results in haddock incorrectly
hyperlinking XMonad.Utils.ExtensibleState at
https://hackage.haskell.org/package/xmonad-0.13/docs/XMonad-Core.html#v:extensibleState
2018-04-11 23:46:11 +02:00
Brent Yorgey
af3d3818c8 Revert "remove unnecessary profiling flag"
This reverts commit d065038c8a.

Put profiling flag back, and comment out 'cabal check' test in .travis.yml
2018-03-21 22:22:58 -05:00
Brent Yorgey
d065038c8a remove unnecessary profiling flag
It was making cabal check unhappy.
2018-03-21 15:34:27 -05:00
Brent Yorgey
10bc213349 include libxrandr-dev 2018-03-21 15:24:54 -05:00
Brent Yorgey
d22d93b43f try updating travis config 2018-03-21 14:58:54 -05:00
Brent Yorgey
871a80fee7 add GHC 8.4.1 to tested-with 2018-03-21 14:58:38 -05:00
Brent Yorgey
2d59f5157c update GHC version number to 8.4.1 2018-03-21 14:58:25 -05:00
Brent Yorgey
0738262d9e Merge pull request #153 from vmchale/master
update to work with latest GHC
2018-03-21 14:54:30 -05:00
Brent Yorgey
63d6a66133 Merge branch 'master' into master 2018-03-21 14:52:50 -05:00
Brent Yorgey
fe6215d309 Merge pull request #156 from MichielDerhaeg/compat
restored compatability with GHC versions prior to 8.0.1
2018-03-21 14:50:17 -05:00
Michiel Derhaeg
c3cb4ad65f forgot to remote windows specific code 2018-03-18 00:08:03 +01:00
Michiel Derhaeg
126f891d11 restored compatability with GHC versions prior to 8.0.1 2018-03-17 23:23:11 +01:00
Vanessa McHale
d3383ce0f5 make it work w/ xmonad-testing 2018-02-05 18:29:14 -06:00
Vanessa McHale
c96a59fa0d update to work with latest GHC 2018-02-05 17:46:57 -06:00
Peter J. Jones
12a45b4b99 Merge pull request #88 from Javran/master
fix xmonad/xmonad#87
2017-09-13 18:07:04 -07:00
Javran Cheng
462957b2f0 fix xmonad/xmonad#87
switch focus when mouse is entering a workspace
but not moving into a window (w' == 0)

for magic number 0 in w' == 0
see discussion in https://github.com/xmonad/xmonad/pull/88#pullrequestreview-62489502
2017-09-13 19:28:02 -04:00
Fabian Beuke
3ec3536761 Change comment for grabButtons in Main.hs 2017-08-11 14:46:19 +02:00
Fabian Beuke
179b6a30f4 Remove unused CPP extension from Core.hs 2017-08-08 00:46:00 +02:00
Nicolas Dudebout
3dc65c3d2e remove the man pages from data-files
The man pages are available for packagers in `extra-source-files`.

Having them in `data-files` is confusing since, according to Cabal's user guide [1], `data-files` contains "A list of files to be installed for run-time use by the package.", but the man pages are not used at run-time by xmonad.

[1]: https://www.haskell.org/cabal/users-guide/developing-packages.html
2017-08-06 10:29:54 -04:00
Peter J. Jones
2e6312776b Merge pull request #111 from YoYoYonnY/origin/patch-1
Safer string quoting for help message
2017-05-31 10:05:03 -07:00
Jonne Ransijn
3897cab7c9 Safer string quoting for help message
Using `show` to quote help string instead of hard-coding it.
Allows for quotes and other characters to be placed inside the help string.
2017-05-09 00:25:48 +02:00
Gábor Lipták
0c97a89754 Cleanup build warnings
Signed-off-by: Gábor Lipták <gliptak@gmail.com>
2017-04-15 18:16:40 -04:00
Tim McIver
5afdc16387 Update README to add xrandr dependency and add build/install instruction. 2017-04-13 22:31:34 -04:00
Peter Jones
10b843ad21 Add a section on rebashing and squashing 2017-04-10 16:58:45 -07:00
Peter J. Jones
bc320b69da Merge pull request #91 from pjones/pjones/remove-state-file
Remove the xmonad state file after reading it
2017-04-10 10:47:03 -07:00
Peter Jones
89a8cc88c3 Remove the xmonad state file after reading it
Tries to make sure IO is not lazy so the file is processed before it
is removed from the file system.

Fixes #86 and friends.
2017-03-30 16:19:26 -07:00
Peter Jones
76f4a16258 Add a stack.yaml file for testing and easy Hackage upload 2017-02-10 16:20:01 -07:00
Peter Jones
8f2eb540d7 Update documentation after final release review 2017-02-10 15:48:44 -07:00
Peter Jones
ba2d75b930 Build the man page for 0.13 2017-02-10 15:46:45 -07:00
Peter Jones
acf0652952 Add a release date 2017-02-09 16:25:41 -07:00
Peter Jones
e4d231920c Bump version to 0.13 2017-02-09 16:09:52 -07:00
Peter J. Jones
980828feea Merge pull request #76 from SirBoonami/master
Rare crash fix due to uncaught exception from `getWindowAttributes`
2017-02-09 15:04:41 -07:00
Felix Hirn
2e5ae02059 Updated CHANGES.md 2017-02-09 22:34:26 +01:00
Felix Hirn
50eb1844eb Fixed a crash due to an unhandled getWindowAttributes exception 2017-02-09 22:28:57 +01:00
Peter Jones
f18bda7dc7 Having an executable build script implies force = True' in recompile'
This is slight change to the original implementation.  This version
forces a recompile if XMonad detects a custom build script.  The
previous version took into consideration the time stamps of the source
files.

For a custom build script, the source files may be located in another
location, or there could be dependencies unknown to XMonad.  Better to
just always call the build script and let it work out if something
needs to be built.
2017-02-08 19:48:42 -07:00
Peter Jones
2d8cad02fe Improve function docs for setWindowBorderWithFallback 2017-02-08 16:53:40 -07:00
Peter Jones
2baab28602 When looking for a directory, prefer ~/.xmonad over XDG BDS
This fixes issue #71
2017-02-07 13:28:53 -07:00
Peter J. Jones
ef65f901ce Merge pull request #65 from HebaruSan/focus-race-condition
Don't send focus events for the wrong windows
2017-02-06 17:29:32 -07:00
Peter Jones
f2da028ff9 Add CONTRIBUTING.md and GitHub templates 2017-01-12 12:23:48 -07:00
Brent Yorgey
bad3ce7a5e Merge pull request #62 from pjones/feature/recompile
Support for custom config locations and recompile strategies
2017-01-03 17:56:40 -05:00
Peter Jones
e1c555e3e6 Implement recommendations from Brent's review 2017-01-03 15:46:07 -07:00
Peter Jones
ab20f7df8d Stop using the term XDG unqualified
This change makes it clear that xmonad can use the "XDG Base Directory
Specification" a la the directory package without making any other XDG
claims.
2017-01-03 15:15:26 -07:00
Peter Jones
a70bf6a6a3 Fix another compatibility issue with GHC 7.6.x and 7.8.x 2017-01-03 15:15:26 -07:00
Peter Jones
f58b2399bd Fix compatibility issue with GHC 7.6.x and 7.8.x 2017-01-03 15:15:25 -07:00
Peter Jones
91d23656a3 Fix compile issue when using directory-1.2.3 2017-01-03 15:15:24 -07:00
Peter Jones
d6b6189cc1 Add release notes for changes in this PR 2017-01-03 15:15:23 -07:00
Peter Jones
0248e3c9fa Custom entry point for people using build tools (cabal, stack, etc.)
The xmonadNoArgs function is now exposed as launch.  This allows users
to start xmonad from a custom binary and skip the configuration
complication check. (This is related to issue #62.)

In order to make the new launch function easier to use, resume state
has been removed from the command line and is now stored in a
temporary file.  As a bonus this fixes issue #12.

This commit also includes a method of migrating from older command
line state to the newer state file.  This should allow you have an
older xmonad instance start a newer xmonad and maintain state.
2017-01-03 15:15:22 -07:00
Peter Jones
40fc10b6a5 Allow customization of xmonad directories
Users can specify directory overrides via environment variables.  If
those aren't set, xmonad now prefers XDG directories.  If ~/.xmonad
exists and none of the others do, it will be used instead.

See: xmonad/xmonad#61
2017-01-03 15:15:22 -07:00
Paul Hebble
3a140badf5 Don't send focus events for the wrong windows
Prevents focus switching loop with UpdatePointer.
From https://code.google.com/archive/p/xmonad/issues/200
2016-12-24 21:01:44 -06:00
Brent Yorgey
2b103ede55 Merge pull request #64 from bennofs/fix-63
fix #63: window jumping to origin position when dragging
2016-12-21 13:49:57 -05:00
Benno Fünfstück
4565e2c90e fix #63: window jumping to origin position when dragging
We need to make sure that the StackSet contains the right
position for the window at all times while dragging. Previously,
the window was only placed at a different position. If, for any
reason, a layout refresh happens while the window is being dragged,
then the window will jump back to the old position because the StackSet
still contains the position of the window where it was as the drag started.

This patch fixes that issue by calling float after each mouse movement, which
ensures that the position for the window is updated in the StackSet.

Fixes #63
2016-12-15 18:42:54 +01:00
Peter Jones
285ee2f836 Add build status badge from Travis 2016-12-14 14:46:52 -07:00
Peter J. Jones
7e9c9ccb1f Merge pull request #59 from xmonad/feature/fix-PR9
Fetch border color from the window's colormap
2016-12-14 14:39:08 -07:00
Peter Jones
dc078490d0 Guard most calls to getWindowAttributes since it may throw an exception
This is a continuation of the work done by Adam Sjøgren (@asjo) to
resolve an issue where RGBA windows have transparent borders.  It also
fixes bugs related to windows suddenly disappearing right before
xmonad calls getWindowAttributes.

For more information see xmonad/xmonad#9
2016-12-14 14:18:45 -07:00
Peter Jones
202e239ea4 Refactor xmonad/xmonad#9 and remove explicit exception handling 2016-12-14 14:18:44 -07:00
Peter J. Jones
e159ec36fe Merge pull request #53 from Philonous/rebuild_script
Add support for (re-) building using script
2016-12-13 12:24:41 -07:00
Philipp Balzarek
0b1ccc75ef update CHANGES.md 2016-12-10 11:21:06 +01:00
Philipp Balzarek
b0f9a3d0b9 clean up build-script handling 2016-12-10 11:10:04 +01:00
Philipp Balzarek
75d297a633 Add support for rebuild script
(#46)
2016-12-10 11:10:04 +01:00
Peter J. Jones
5f5e737d9c Merge pull request #57 from xmonad/prof-flag
only turn on -prof -auto-all with profiling flag
2016-12-08 11:00:52 -07:00
geekosaur
a39ed3ee1b Merge pull request #58 from xmonad/manual-testing
cabal: set 'testing' flag to manual
2016-11-15 13:27:18 -05:00
Brent Yorgey
e05a046bca cabal: set 'testing' flag to manual 2016-11-15 05:12:40 -06:00
Brent Yorgey
12ddc800ab only turn on -prof -auto-all with profiling flag 2016-11-09 10:20:51 -06:00
Brent Yorgey
2fab1bb9f5 test GHC 8 on Travis 2016-11-09 10:09:01 -06:00
Brent Yorgey
1b17d1c378 Merge pull request #48 from mschristiansen/tests
Fix failing tests on GHC 8 and reduce warnings.
2016-11-09 11:06:46 -05:00
Mikkel Christiansen
f490ced673 Fix failing tests on GHC 8 and reduce warnings.
Fix test failures on GHC 8 for `abort` and `new_abort` caused by `error`
appending the stack trace to the error message (since base
4.9.0.0)[1]. This fixes #36.

An alternative is to use `errorWithoutStackTrace` (new in base 4.9.0.0),
but this then requires use of CPP for backwards compatibility.

Remove type constraints prompting GHC to warn about redundant
constraints.

Tested with 7.6.3, 7.8.4, 7.10.3, 8.0.1 (all on NixOS).

[1] https://hackage.haskell.org/package/base-4.9.0.0/docs/GHC-Stack.html
2016-11-09 12:03:34 +02:00
Brent Yorgey
0919ecfbde Merge pull request #51 from mgsloan/no-unicode-with-xmessage
Replace unicode characters in ghc error messages
2016-11-08 23:09:30 -05:00
Brent Yorgey
41b7b1341e Merge pull request #33 from dlahoti/patch-1
replace "XXX comment me" with actual (albeit rudimentary) comments in `mouseMoveWindow` and `mouseResizeWindow`
2016-11-08 23:01:07 -05:00
Michael Sloan
0f0aa5e8cb Replace unicode characters in ghc error messages 2016-10-14 17:40:51 -07:00
dlahoti
ad4417c8e0 clicked -> dragged 2016-05-15 10:46:14 -04:00
dlahoti
b0f7643cc5 replace "XXX comment me" with actual comments 2016-05-15 10:32:28 -04:00
Francesco Ariis
8b055621e9 clarify GenerateManpage.hs license 2016-05-12 11:08:37 -05:00
geekosaur
dc6a972bc1 Merge pull request #29 from windo/fix_help
help: correct section for workspace switching
2016-03-18 08:49:31 -04:00
Siim Põder
e4a3eede18 help: correct section for workspace switching 2016-03-18 09:49:15 +00:00
Brent Yorgey
a0ffe7e47d pandoc, etc. should not be a dependency when not building generatemanpage 2015-12-18 14:23:38 -06:00
Brent Yorgey
b00b94fda7 Merge pull request #13 from pjones/release-0.12
Release 0.12
2015-12-18 11:25:07 -06:00
Peter Jones
45a78ba802 Finial tweaks before release 2015-12-14 13:30:55 -07:00
Peter Jones
4c0717e9cc Make it easier to update the man page 2015-12-11 11:19:40 -07:00
Peter Jones
30b4ff5e40 Update development references (darcs, code.google.com, etc.)
* All references to darcs have been updated to git

  * Most Google Code references have been changed to GitHub

  * References to specific issues at code.google.com have been left
    alone

  * Updated the GenerateManpage.hs to work with the latest version of
    pandoc
2015-12-11 11:17:26 -07:00
Peter Jones
b68ebc797a Remove references to development snapshots from darcs 2015-12-11 10:34:31 -07:00
Peter Jones
eb4ef5b23f Update CONFIG instructions: defaultConfig -> def 2015-12-11 10:00:53 -07:00
Peter Jones
73224be21b Bring in entries from https://wiki.haskell.org/Xmonad/Notable_changes_since_0.11 2015-12-09 12:54:14 -07:00
Peter Jones
7e287ec815 Revert a typo that wasn't actually a typo 2015-12-08 16:46:16 -07:00
Peter Jones
60f472faa2 Fix a couple of links (darcs -> git) 2015-12-08 16:28:56 -07:00
Peter Jones
bd72c6e1e2 Add CHANGES.md 2015-12-08 13:50:20 -07:00
Peter Jones
b5402e76d3 Fix some bad formatting 2015-12-08 13:31:54 -07:00
Peter Jones
39dc00b16f Add a file extension to the README file to make GitHub happy 2015-12-08 13:30:11 -07:00
Peter Jones
038f77de5a Reformat the README as proper markdown for GitHub and Hackage 2015-12-08 13:28:08 -07:00
Adam Vogt
59e731ea11 minor update to STYLE and TODO 2015-11-28 16:23:24 -05:00
Brent Yorgey
3ce9dcbbb5 add generated .travis.yml 2015-11-06 14:49:32 -06:00
Brent Yorgey
f25afdab9f .cabal: update tested-with field 2015-11-06 14:49:15 -06:00
brandon s allbery kf8nh
577d5ae968 Add gitignore
Shamelessly ganked from cabal, with obvious project-related files removed.
2015-08-22 13:32:54 -04:00
Josh Holland
29bcd465c2 Bump dependency on utf8-string
The release of utf8-string version 1 just removed some deprecated APIs
that are not used by XMonad.

Fixes #598.
2015-05-05 10:27:18 +00:00
Adam Vogt
307b82a53d Make ~/.xmonad/xmonad-$arch-$os handle args like /usr/bin/xmonad 2015-04-14 18:48:21 +00:00
Adam Vogt
197b0091f8 remove unused FFI pragma 2015-03-27 19:27:59 +00:00
Adam Vogt
69c5dae00d Avoid using instances removed in QuickCheck-2.7
see https://github.com/nick8325/quickcheck/issues/31 for details
2015-03-01 13:50:49 +00:00
Adam Vogt
28c3482411 use setLocale properly
passing in Nothing returns the locale, while Just "" sets the locale according
to environment variables (which is what was being done before).

Thanks to geekosaur & v_v for finding this.
2015-02-27 02:52:28 +00:00
Adam Vogt
73ee008cf6 generate man/xmonad.1 man/xmonad.1.html with a more recent pandoc (1.13) 2014-12-22 00:40:33 +00:00
Adam Vogt
82a1cae123 build with ghc-6.12 again 2014-08-15 03:38:08 +00:00
Adam Vogt
d01b913594 depend on the setlocale package 2014-10-20 14:10:06 +00:00
Adam Vogt
d9e3ebf531 don't encode paths for spawnPID (#348) 2014-10-20 14:04:10 +00:00
Adam Vogt
252c9d5eee stop floating Gimp with the default manageHook
most people have probably moved on to gimp-2.8,
which doesn't really need to be floating
2014-07-04 22:40:25 +00:00
Joey Hess
5f0b1601d5 needs update for mplayer2
xmonad floats mplayer by default. However, Debian has switched to
mplayer2, and so on upgrade, it will stop floating. This can be easily
fixed in the user's config file, but here is a patch that avoids
bothering the user with breakage on upgrade.
2014-07-03 05:43:59 +00:00
Adam Vogt
d60791e3f5 minor formatting of manpage 2014-05-11 17:17:35 +00:00
Adam Vogt
f0054fdde7 update GenerateManpage to pandoc 1.12 2014-05-11 17:14:41 +00:00
Adam Vogt
a9de363b14 make GenerateManpage look in src/ 2014-05-11 17:14:07 +00:00
Daniel Wagner
7eb6ba0126 use only POSIX-standard regexen in GenerateManpage 2014-05-11 15:46:02 +00:00
Adam Vogt
f03d2cdf74 make the check for overflow cleaner 2014-05-07 02:49:30 +00:00
Adam Vogt
16c0cb9a33 make cabal test show a FAIL when at least one test fails 2014-05-03 05:46:29 +00:00
Adam Vogt
fdf3fb2c58 remove unnecessary CPP 2014-05-03 04:48:15 +00:00
Adam Vogt
22d5e7eaa3 add missing files to sdist 2014-05-03 04:47:40 +00:00
Adam Vogt
f837b830fc note -fno-warn-unused-do-bind in STYLE 2014-05-03 04:41:05 +00:00
Adam Vogt
939c0558e6 update README 2014-05-03 04:40:52 +00:00
Adam Vogt
fbd406eb03 add some more forgotten properties 2014-05-03 03:41:44 +00:00
Adam Vogt
ecde376224 make hpc report cover the testsuite too 2014-05-03 03:41:25 +00:00
Adam Vogt
edf3394821 run more tests (and add a couple) 2014-05-03 02:11:03 +00:00
Adam Vogt
20be322b08 put hpc reports in dist/hpc/ 2014-05-02 19:29:07 +00:00
Adam Vogt
f8f53fdff8 add forgotten tests back 2014-05-02 19:28:58 +00:00
Adam Vogt
1da1e2e21e updated hpc 2014-05-02 18:49:04 +00:00
Adam Vogt
4026075bc6 clean up cabal file
We don't support ghc-6.6 anymore, so no need to include this split_base stuff.
2014-05-02 18:07:21 +00:00
Adam Vogt
8863761d66 update testsuite (mostly due Jesper Reenberg)
* use quickcheck2
* run them using cabal's test-suite field
* split up Properties into separate files
2014-05-02 18:01:46 +00:00
Adam Vogt
d67dcd8c4b hack to avoid crash when fewer workspaces than screens (#543) 2014-05-02 17:59:10 +00:00
Adam Vogt
aa84841289 OPTIONS_GHC is the preferred pragma name 2014-05-02 17:57:34 +00:00
Adam Vogt
d10abdcdd0 move library part to src/ 2014-05-02 17:51:59 +00:00
Adam Vogt
3073826dfc avoid warnings from missing Prelude.catch in ghc>=7.6 2014-05-02 05:58:23 +00:00
Adam Vogt
daed0062c6 derive Applicative instance for Query 2014-05-02 05:57:43 +00:00
Adam Vogt
abd737cfb4 Bump version to 0.12
This is to make contrib build failures resulting from by data-default
lead people to do the right thing: update xmonad-core.
2013-07-20 19:21:24 +00:00
Daniel Wagner
e719be4e69 warning police: name userCodeDef's argument defValue instead of def 2013-05-28 16:44:01 +00:00
Daniel Wagner
ec1a20c727 depend on data-default, and deprecate the monomorphic name defaultConfig 2013-05-28 00:35:31 +00:00
Daniel Wagner
8f039ec434 use "modm" instead of "modMask" in the sample config 2013-01-06 17:44:14 +00:00
Daniel Wagner
057fcc5162 define the "help" string in the sample configuration bundled with xmonad 2013-01-06 17:42:54 +00:00
mwlochbaum
8e7634f543 configurableEventMasks 2013-02-05 18:28:58 +00:00
Daniel Wagner
40cb12ce17 Grab all keycodes linked to each keysym, not just one
This patch is based heavily on the one contributed by
svein.ove@aas.no, but updated to avoid causing a conflict and to work
with the newest X11 bindings. The name of the patch (and comment
below) are copied verbatim from his patch.

XKeysymToKeycode only gives the first code bound to a given symbol. To
handle the case where multiple keys are bound to the same symbol,
XKeycodeToKeysym is used instead, searching through all possible
keycodes for each sym.
2013-01-18 22:54:46 +00:00
Adam Vogt
b803fd74a5 Issue 135 use wa_border_width for floating windows (neoraider) 2013-01-15 17:07:15 +00:00
Adam Vogt
d386a230f6 Add flags for call to ghc closing issue 240
The -main-is flag goes back to at least ghc 6.10, and maybe the warning that
this otherwise redundant flag enables (when xmonad.hs isn't a  module Main)
also dates back that far.
2013-01-01 03:50:34 +00:00
Adam Vogt
e87456ab77 bump cabal-version to satisfy hackage 2013-01-01 01:40:56 +00:00
Adam Vogt
cdc22f0849 TAG 0.11 2013-01-01 01:30:31 +00:00
Adam Vogt
70413b2e22 Bump version to 0.11 2012-12-31 18:48:10 +00:00
Adam Vogt
67ffde0dfb Add more metadata to cabal file 2012-12-31 18:46:52 +00:00
Adam Vogt
d904fb1cc4 Update generated manpage (and releaseDate in util/GenerateManpage.hs) 2012-12-31 16:34:03 +00:00
Adam Vogt
4120be8ba0 Copy help binding from XMonad.Config to man/xmonad.hs 2012-12-31 16:33:05 +00:00
Adam Vogt
e015155131 Shorter hyperlinks to hackage in README. 2012-12-31 10:53:58 +00:00
Adam Vogt
4c1536cd18 Add clickJustFocuses option to template configuration. 2012-12-31 10:44:20 +00:00
Adam Vogt
a34a5e979a Add configuration option clickToFocus (issue 225)
To summarize this allows clicks which change the focus to also be passed on to
that window.
2012-01-03 01:39:16 +00:00
conrad.irwin
38faddf9de pass mouse clicks on to focused windows (experimental)
Originally: http://www.haskell.org/pipermail/xmonad/2008-June/005807.html
2011-05-25 04:34:13 +00:00
Adam Vogt
3ab2b28711 HCAR.tex convert line endings. 2012-11-18 19:40:06 +00:00
gwern0
934ff6a562 HCAR.tex: update to Janis's master version per his instructions 2012-11-18 01:03:10 +00:00
Adam Vogt
f8b07d8956 Add generated manpage and html manpage to the repo.
The intention of adding these files to the data-files
is so that they get included in the upload to hackage:
people might like manpage but not have to install pandoc.

It's not really clear that this is the best solution.
2012-11-08 23:11:39 +00:00
Adam Vogt
67d436a4e6 Resolve conflicts Geoff Reedy's window focus hack. 2010-02-22 14:45:12 +00:00
Geoff Reedy
c6fef373dc Give focus to windows that don't set the input hint 2009-10-10 23:19:07 +00:00
Geoff Reedy
2d4f304c0a implement the ICCCM WM_TAKE_FOCUS protocol 2009-06-22 05:19:11 +00:00
Geoff Reedy
1df8ea3d0e track currently processing event 2009-06-22 03:56:49 +00:00
Adam Vogt
490719c035 resolve HCar.tex conflict 2012-11-08 22:35:14 +00:00
gwern0
3cd001e8df HCAR.tex: update with Janis's master version 2012-10-03 19:04:25 +00:00
gwern0
b0dda7b351 HCAR.tex: update per Janis 2012-05-16 21:13:52 +00:00
gwern0
d8495adf0d HCAR.tex: update per Janis 2012-05-13 21:15:22 +00:00
gwern0
06f35a650e Config.hs: implement mod-shift-/ newbie keybinding guide per http://code.google.com/p/xmonad/issues/detail?id=182 2012-01-13 01:04:10 +00:00
gwern0
56f5ecb320 Config.hs: rm commented out keybinding (dead for years) 2012-01-13 00:01:15 +00:00
Adam Vogt
ff674a27e2 Include manual pages in data-files. 2011-12-04 00:11:37 +00:00
Adam Vogt
6c51745122 Correctly identify source files in ~/.lib (David McLean) 2012-04-30 15:42:22 +00:00
Adam Vogt
108c2280ef Address versioning problems related to X11 1.6 release.
Bump version to 0.10.1 since cabal uses hackage dependencies even when the
locally installed package differs.

Allow X11-1.6 dependency.
2012-03-20 00:49:24 +00:00
Adam Vogt
e70b489936 Drop PlainConfig from HCAR.tex: it doesn't exist in contrib.
The code for that moved out to a separate project:
http://braincrater.wordpress.com/2008/08/28/announcing-xmonad-light/
2011-12-11 00:44:05 +00:00
gwern0
450c3a34fe HCAR: update module count, date, versions, maintainer 2011-12-04 02:59:31 +00:00
Adam Vogt
32f416a3c2 Minor updates to supporting files (for 0.10 release). 2011-11-18 23:13:24 +00:00
Adam Vogt
4be3b39cd2 Assume locale is utf8 for spawn.
This adds the utf8-string dependency so that users no longer need to encode
strings they (possibly indirectly) pass to spawn. This is the expected
behavior, since each Char in String should be an actual character.

For systems that do not use utf8 this does not help. Fixing this by using iconv
or similar libraries may be done later.
2011-11-18 18:29:20 +00:00
Adam Vogt
75889ab62e Correct recompile keybinding (issue 126)
Thanks reenberg for pointing out the previous patch incorrectly warns about a
missing xmonad when the config has an error.

Also changed is "type" which as a shell builtin is more likely to exist than
"which".
2011-11-17 04:25:22 +00:00
Adam Vogt
792add376e Warn with xmessage when xmonad cannot be found for recompile (issue 126) 2010-03-30 00:33:10 +00:00
Daniel Wagner
87c50a911f change the default mod+p binding to keep up with changes to dmenu 2011-10-13 16:25:09 +00:00
Daniel Wagner
d16aa9975e recognize the --replace option 2011-08-09 19:52:50 +00:00
Adam Vogt
f34642cbac Break a long line 2011-06-09 04:24:24 +00:00
Jens Petersen
008c3638a5 output error code when xmonad.hs compile fails without any error output
Currently if there is no ghc on the path say for some reason,
xmonad.error is empty.  This patch makes
it output the exitcode code when the compile process fails
without any error output.  (It might be easier just to spawn
a shell to get "ghc: command not found" output for free.)
2011-04-26 06:23:41 +00:00
Adam Vogt
f5c40e9e12 Remove -fglasgow-exts for deriving Data/Typeable needed with ghc-6.6
This gets rid of a warning with ghc-7.0.2, and -XDeriveDataTypeable seems to
have been added with 6.8, which should be far back enough:
http://www.haskell.org/ghc/docs/6.8-latest/html/users_guide/deriving.html#deriving-typeable
2011-06-08 23:04:15 +00:00
Adam Vogt
bd82cc9150 Expose instances to haddock
While haddock may have choked on -XGeneralizedNewtypeDeriving before, this is
no longer the case. Also this doesn't change the results with a recent haddock
(2.9.2)
2011-06-08 22:56:13 +00:00
Adam Vogt
a025912ab7 Haddock formatting for a type (-->) 2011-05-24 01:51:35 +00:00
Brandon S Allbery KF8NH
19c1759b35 Generalize types of ManageHook functions, so they can be reused 2011-02-24 00:30:21 +00:00
gwern0
92acd1eb74 HCAR.tex: update per Janis's final version 2011-05-22 18:37:55 +00:00
gwern0
db9f39d6af HCAR.text: mention 2 features added since last HCAR
Prompted by Janis's usual request for updated HCARs
2011-04-11 16:45:19 +00:00
Adam Vogt
ebcd67efac Correct misleading documentation on Stack (thanks sdrodge)
`Possibly empty' applies only to `Maybe (Stack a)', not `Stack a' described
there, so this is easier to understand.
2011-03-28 00:19:30 +00:00
gwern0
387a253f62 HCAR.tex: apply Janis Voigtlaender's HCAR changes 2010-11-09 20:50:22 +00:00
Adam Vogt
4c83e8e097 Bump version to 0.10
This doesn't mean it's ready for this number release, but at least
contrib/core incompatibilities introduced since 0.9 will be avoided.
2011-01-15 18:07:15 +00:00
Adam Vogt
ae59a5184f Update util/GenerateManpage for pandoc 1.6 2010-12-31 16:31:18 +00:00
Adam Vogt
fa8fe9aca4 Update util/GenerateManpage to be pandoc-1.4 compatible 2010-04-03 18:13:54 +00:00
gwern0
673c3e9ed9 HCAR.tex: update from May
- there were no significant changes to xmonad-core
- description of 2 new modules not mentioned in HCAR
2010-10-12 01:09:15 +00:00
Adam Vogt
6ba45cdb38 Update comments describing `recompile' 2010-04-03 18:11:15 +00:00
Adam Vogt
b995b430bc Note things to update each release. 2010-03-22 15:06:22 +00:00
gwern0
ba482a4611 XMonad.Core: escape slashes, ln module 2010-06-20 17:57:41 +00:00
Tomas Janousek
684907bc77 fix haddock comment being assigned to constructor instead of field 2010-04-15 17:39:36 +00:00
gwern0
ad4136df26 HCAr.tex: update with additions and versions 2010-05-02 20:13:21 +00:00
gwern0
defe0c282e +original HCAR entry 2010-05-02 20:02:52 +00:00
Spencer Janssen
c7bdac1a7e Less refreshing in mouse-2 binding (thanks aavogt) 2010-05-03 15:50:17 +00:00
Daniel Schoepe
17799f131a Replaced custom forever_ by library function 2009-01-14 21:55:56 +00:00
Tomas Janousek
8cd66aa380 reveal: don't insert non-clients into the set of mapped windows
In xmonad-core, this fixes a small bug that caused doIgnored windows to get
into `mapped' and never being removed from there.

In the context of xmonad-contrib, this fixes a tremendous memory leak that
could be triggered by using MouseResizableTile and UrgencyHook at the same
time. MRT would create dummy windows that would get added to `mapped' by the
reveal call in `windows'. As these were not removed (removal from `mapped' is
filtered by `isClient'), they'd stay there forever and due to an inefficiency
in UrgencyHook would eat up all memory sooner or later.
2010-03-27 21:42:43 +00:00
gwern0
32ba0d4a0d loc.hs: hlintify 2010-02-13 23:15:37 +00:00
Spencer Janssen
77b3f62610 Various clean-ups suggested by HLint 2010-02-14 02:57:50 +00:00
Spencer Janssen
f3b07eb5dc Make the --replace docs consistent 2010-02-13 00:26:47 +00:00
Adam Vogt
4372c256ed Add --replace flag with documentation (issue 99). 2009-12-20 18:35:29 +00:00
Adam Vogt
34239a79de Fix compile error when using base-3 (thanks bogner). 2010-02-11 06:39:38 +00:00
Daniel Schoepe
5866db4f0f Broadcast PropertyChange events (needed for layouts with decoration) 2010-01-13 20:40:17 +00:00
Adam Vogt
46d039cde5 Rename numlockMask to numberlockMask to help users of the template config.
Without the change, the errors are like:

>     [ unrelated error messages ]
>     No constructor has all these fields: `numlockMask',
>       `terminal', [every other field set]

With the change:

>     `numlockMask' is not a record selector
>     [ context where numlockMask is named ]
2010-01-18 16:22:56 +00:00
Adam Vogt
dd22717961 Correct warnings with ghc-6.12
Changes include:
  - compatibility with base-4 or 3 (base-2 untested) by using
    extensible-exceptions. This adds an additional dependency for users of
    ghc<6.10)
  - list all dependencies again when -ftesting (change in Cabal-1.8.0.2)
  - remove unnecessary imports
  - suppress -fwarn-unused-do-bind, with appropriate Cabal-1.8 workaround,
    described here:
    http://www.haskell.org/pipermail/xmonad/2010-January/009554.html
2010-01-18 18:15:32 +00:00
Spencer Janssen
0beeb4164b Add xfork: a forkProcess that works around process global state 2009-12-23 06:16:23 +00:00
Spencer Janssen
0b435028ff TAG 0.9.1 2009-12-16 23:36:43 +00:00
Spencer Janssen
84a988da82 extra-source-files for the new manpage 2009-12-16 23:20:05 +00:00
Spencer Janssen
dbd739e41e Bump to 0.9.1 2009-12-16 23:11:10 +00:00
Spencer Janssen
d5d8d551e6 Determine numlockMask automatically, fixes #120 2009-12-16 01:21:40 +00:00
Spencer Janssen
a2ba4d8a6c Update for X11 1.5.0.0 2009-12-16 01:17:00 +00:00
Spencer Janssen
557d3edb7d Safer X11 version dependency 2009-12-16 01:03:30 +00:00
Brent Yorgey
262db2367f man/xmonad.hs: remove reference to deprecated 'dynamicLogDzen' function 2009-11-26 05:39:08 +00:00
Spencer Janssen
eddb445307 A few tweaks to --verbose-version 2009-12-08 04:07:29 +00:00
Adam Vogt
d5aadf2538 Generalize the type of (<+>). It can be used for keybindings too. 2009-12-05 23:36:11 +00:00
gwern0
a16bb44934 Main.hs +--verbose-version flag
This resolves http://code.google.com/p/xmonad/issues/detail?id=320 by adding a
--verbose-version option yielding output like "xmonad 0.9 compiled by ghc 6.10 for linux/i386"
2009-11-28 14:48:40 +00:00
Spencer Janssen
2b854ee47c Swap the order that windows are mapped/unmapped. Addresses #322 2009-11-19 02:54:40 +00:00
Spencer Janssen
02ed1cabdc Add GPL warning to GenerateManpage 2009-11-11 00:01:06 +00:00
Adam Vogt
02693d307c Add a basic header to the html manpage output 2009-10-28 03:30:42 +00:00
Adam Vogt
37dc284460 Use pandoc to convert a markdown manpage tranlation to html and man. 2009-10-28 03:06:39 +00:00
Daniel Schoepe
73e406f4a6 Support for extensible state in contrib modules. 2009-11-06 11:50:50 +00:00
Spencer Janssen
44bc9558d9 Set SIGPIPE to default in forked processes 2009-11-06 22:37:43 +00:00
Spencer Janssen
0eb84e4866 Bump version to 0.9 2009-10-26 00:45:43 +00:00
Adam Vogt
b4bf8de874 Grab the xmonad.cabal version for putting into the manpage 2009-10-24 20:09:20 +00:00
Adam Vogt
17c89e327e Correct formatting in manpage 2009-10-24 20:07:48 +00:00
Don Stewart
da71b6c8ac depend on X11 >= 1.4.6.1, for conformity with XMC 2009-10-24 21:24:09 +00:00
Khudyakov Alexey
2621f3f6a8 Fix for Tall documentation 2009-05-16 10:47:53 +00:00
Daniel Wagner
8ec0bf3290 correct a comment 2009-07-27 03:20:24 +00:00
Spencer Janssen
7e20d0d308 man/xmonad.hs is in data-files, remove it from extra-source-files 2009-10-23 03:14:57 +00:00
Adam Vogt
24d8de93d7 Add the template config as distributed file. 2009-10-22 04:14:02 +00:00
Adam Vogt
2dd6eeba7d Note in manpage that 'exec xmonad' should be used 2009-09-01 04:05:38 +00:00
Adam Vogt
72997cf982 Manual page spelling: maximise -> maximize, utilising -> utilizing 2009-09-01 04:02:17 +00:00
Adam Vogt
7365d7bc11 Describe modular configuration in the manual page 2009-09-01 04:00:46 +00:00
Spencer Janssen
36e20f689c Remove redundant parens 2009-09-18 03:55:47 +00:00
wirtwolff
cde261ed56 man_xmonad.hs: import Data.Monoid for mempty, keybinding edits
Bring mempty into scope. Add commented ToggleStruts binding.
Replace shadowed modMask in keybindings with modm instead.
2009-03-20 02:46:24 +00:00
Adam Vogt
8d8cc8bcd8 Only watch mtime for .hs, .lhs, .hsc for ~/.xmonad/lib
Previously xmonad would force a recompile due to the object files being too
new, so only look at files which may contain haskell code.
2009-05-03 23:54:15 +00:00
Adam Vogt
ccb6ff92f2 Add lib to ghc searchpath with recompilation check 2009-03-21 23:29:07 +00:00
Adam Vogt
e944a6c8d3 Remove tabs from ManageHook.hs 2009-07-10 01:14:24 +00:00
Adam Vogt
eb1e29c8bb Set infix 0 --> to reduce parentheses in ManageHooks
What was previously:
> (appName ?= x <&&> classname ?= y) --> (doFloat <+> doIgnore)

Can now be:
> appName ?= x <&&> classname ?= y --> doFloat <+> doIgnore
2009-07-10 01:13:08 +00:00
Adam Vogt
66e7715ea6 Pester the user with one (not two) xmessages on config errors 2009-03-21 23:37:36 +00:00
Wouter Swierstra
d9d3e40112 Minor bugfix in the creation of new StackSets. 2009-05-03 15:43:21 +00:00
Spencer Janssen
7385793c65 Avoid deadly cycle in man/xmonad.hs 2009-03-19 08:19:18 +00:00
wirtwolff
72885e7e24 X.Config.hs, ./man/xmonad.hs: update Event Hook doc 2009-02-09 18:38:37 +00:00
Spencer Janssen
a931776e54 Use records to document Tall's arguments 2009-02-21 23:06:28 +00:00
Joachim Breitner
61568318d6 Fix possible head []
This seems to be a rare case, but I just got hit by it.
2009-01-06 19:20:26 +00:00
Spencer Janssen
3caa989e20 ManageHook.doShift: use shiftWin instead of shift 2009-02-19 04:14:58 +00:00
Spencer Janssen
09fd11d13b Express shift in terms of shiftWin 2009-02-17 23:53:43 +00:00
Don Stewart
f33681de49 Use standard -fforce-recomp instead of undocumented -no-recomp 2009-02-08 16:55:18 +00:00
Daniel Schoepe
bf8bfc66a5 Support for custom event hooks 2009-02-03 15:55:36 +00:00
Daniel Schoepe
4075e2d9d3 Make X an instance of Typeable 2009-01-28 21:54:06 +00:00
Spencer Janssen
78856e1a6f Add uninstallSignalHandlers, use in spawn 2009-01-22 00:26:43 +00:00
Spencer Janssen
4222dd9ad3 Create a new session for forked processes 2009-01-22 00:04:23 +00:00
Spencer Janssen
34a547ce57 TAG 0.8.1 2009-01-18 08:39:10 +00:00
Spencer Janssen
353e7cd681 Close stdin in spawned processes 2009-01-17 04:00:24 +00:00
Spencer Janssen
72dece0769 Document spawnPID 2009-01-17 03:59:07 +00:00
Spencer Janssen
6e1c5e9b49 Asynchronously recompile/restart xmonad on mod-q 2009-01-17 03:53:00 +00:00
Spencer Janssen
bf8ba79090 Add --restart, a command line flag to cause a running xmonad process to restart 2009-01-17 03:49:59 +00:00
Spencer Janssen
5edfb1d262 Bump version to 0.8.1 2009-01-16 22:36:21 +00:00
Spencer Janssen
0fecae0abc Remove doubleFork, handle SIGCHLD
This is a rather big change.  Rather than make spawned processes become
children of init, we handle them in xmonad.  As a side effect of this change,
we never need to use waitForProcess in any contrib module -- in fact, doing so
will raise an exception.  The main benefit to handling SIGCHLD is that xmonad
can now be started with 'exec', and will correctly clean up after inherited
child processes.
2009-01-16 20:47:42 +00:00
gwern0
26f4f734f9 Main.hs: escape / in Haddocks
This lets haddocks for Main.hs, at least, to build with 2.3.0.
2008-12-07 02:09:15 +00:00
Daniel Schoepe
5e7df396b9 More flexible userCode function 2009-01-10 22:18:52 +00:00
Spencer Janssen
314ba78335 Call logHook as the very last action in windows 2008-12-09 23:37:00 +00:00
Spencer Janssen
7aa78ecc75 Accept inferior crossing events. This patch enables fmouse-focus-follows-screen 2008-12-05 04:51:30 +00:00
Spencer Janssen
ba8e26458e Tile all windows at once 2008-11-18 07:44:47 +00:00
Spencer Janssen
c627e8cc4d Factor rational rect scaling into a separate function 2008-11-18 07:28:49 +00:00
Spencer Janssen
04f894275d Change screen focus by clicking on the root window.
This is a modification of a patch from Joachim Breitner.
2008-11-06 22:40:31 +00:00
Spencer Janssen
edb752136f Fix #192. 2008-10-21 22:00:59 +00:00
Adam Vogt
2b463a632f select base < 4 for building on ghc 6.10 2008-10-13 21:45:09 +00:00
Joachim Breitner
ca122dd2cb add killWindow function
This is required to kill anything that is not focused, without
having to focus it first.
2008-10-05 00:18:04 +00:00
Devin Mullins
77657b65f9 add'l documentation 2008-09-27 23:46:39 +00:00
Spencer Janssen
28c57a837a Regression: ungrab buttons on *non* root windows 2008-10-07 21:43:51 +00:00
Spencer Janssen
afda20b56d Partial fix for #40
Improvements:
 - clicking on the root will change focus to that screen
 - moving the mouse from a window on a screen to an empty screen changes focus
   to that screen
The only remaining issue is that moving the mouse between two empty screens
does not change focus.  In order to solve this, we'd have to select motion events
on the root window, which is potentially expensive.
2008-10-07 21:20:53 +00:00
Spencer Janssen
0cc7b12fd0 Track mouse position via events received 2008-10-07 20:39:53 +00:00
Spencer Janssen
15a78ae715 Fix haddock 2008-10-07 09:46:41 +00:00
Spencer Janssen
18444799e0 Move screen locating code into pointScreen 2008-10-07 09:42:07 +00:00
Spencer Janssen
cc60fa73ad Make pointWithin a top-level binding 2008-10-07 09:02:29 +00:00
gwern0
8881e2ac78 sp README, CONFIG, STYLE, TODO 2008-09-13 02:44:57 +00:00
Spencer Janssen
533031e3d6 Use the same X11 dependency as xmonad-contrib 2008-09-21 06:15:08 +00:00
Spencer Janssen
76d4af15e4 Export focusUp' and focusDown' -- work entirely on stacks 2008-09-11 21:48:03 +00:00
Devin Mullins
74c6dd2721 add W.shiftMaster, fix float/tile-reordering bug 2008-09-11 05:39:09 +00:00
Spencer Janssen
b605fd9fce Spelling. Any bets on how long this has been there? 2008-09-05 19:52:11 +00:00
Spencer Janssen
85202ebd47 Bump version to 0.8 2008-09-05 19:42:25 +00:00
Spencer Janssen
328c660ce7 Remove obsolete comments about darcs X11 2008-09-05 19:49:15 +00:00
Spencer Janssen
b185a439b1 Recommend latest packages rather than specific versions 2008-09-05 19:48:37 +00:00
Spencer Janssen
0016e06984 Also remove -optl from the executable section 2008-08-20 21:00:23 +00:00
Spencer Janssen
339b2d0097 -optl-Wl,-s is not needed with recent Cabal versions 2008-08-20 20:41:02 +00:00
Malebria
5f4d63ba71 Haddock links 2008-06-01 21:25:15 +00:00
Malebria
942572c830 Haddock syntax for enumeration 2008-06-01 20:49:51 +00:00
Spencer Janssen
46ac2ca24b I prefer the spencerjanssen@gmail.com address now 2008-07-14 20:26:50 +00:00
Trevor Elliott
3830d7a571 Raise windows in the floating layer when moving or resizing 2008-05-21 21:50:57 +00:00
Devin Mullins
5b3eaf663a add currentTag convenience function 2008-05-11 22:42:58 +00:00
Spencer Janssen
c93b7c7c3b Make Mirror a newtype 2008-05-08 10:46:40 +00:00
Spencer Janssen
42dee4768e Comments 2008-05-07 01:31:22 +00:00
Spencer Janssen
e847b350ed Break long line 2008-05-07 01:26:08 +00:00
Spencer Janssen
cccbfa21e4 Style 2008-05-07 01:25:19 +00:00
Spencer Janssen
870b3ad282 Simplify 2008-05-07 01:13:09 +00:00
Spencer Janssen
ab30d76578 Overhaul Choose, fixes issue 183 2008-05-06 22:08:09 +00:00
Klaus Weidner
d8d636e573 Remember if focus changes were caused by mouse actions or by key commands
If the user used the mouse to change window focus (moving into or clicking on a
window), this should be handled differently than focus changes due to keyboard
commands. Specifically, it's inappropriate to discard window enter/leave events
while the mouse is moving. This fixes the bug where a fast mouse motion across
multiple windows resulted in the wrong window keeping focus.

It's also helpful information for contrib modules such as UpdatePointer - it's
supposed to move the mouse pointer only in response to keyboard actions, not if
the user was moving the mouse.
2008-05-02 17:56:03 +00:00
Spencer Janssen
ba3987f299 Wibble 2008-05-06 20:38:40 +00:00
Ivan N. Veselov
5a19425e79 Added doShift function for more user-friendly hooks 2008-05-06 18:57:57 +00:00
Don Stewart
28431e18c8 use named colours. fixes startup failure on the XO 2008-05-02 21:01:49 +00:00
Spencer Janssen
43c2d26cdb Set focus *after* revealing windows 2008-04-07 22:25:59 +00:00
Spencer Janssen
c24016882e Reveal windows after moving/resizing them.
This should reduce the number of repaints for newly visible windows.
2008-04-07 22:07:56 +00:00
Spencer Janssen
9dae87c537 Hide newly created but non-visible windows (fixes bug #172) 2008-04-30 01:40:12 +00:00
Don Stewart
b67026dd02 formatting, eta expansion 2008-04-18 18:43:37 +00:00
Lukas Mai
aa58eea6dc XMonad.ManageHook: add 'appName', another name for 'resource' 2008-04-06 01:20:06 +00:00
Lukas Mai
7db13a2a45 XMonad.ManageHook: make 'title' locale-aware; haddock cleanup
The code for 'title' was stolen from getname.patch (bug #44).
2008-04-06 01:13:38 +00:00
Lukas Mai
029e668dbc XMonad.Main: call setlocale on startup 2008-04-06 01:12:34 +00:00
robreim
6f61c83623 floats always use current screen (with less bugs) 2008-04-05 13:50:09 +00:00
Lukas Mai
bcbccbfafc XMonad.Operations: applySizeHint reshuffle
Make applySizeHints take window borders into account. Move old functionality
to applySizeHintsContents. Add new mkAdjust function that generates a custom
autohinter for a window.
2008-04-04 21:56:15 +00:00
Lukas Mai
04c8d62361 XMonad.Layout: documentation cleanup 2008-04-04 21:54:44 +00:00
Spencer Janssen
4890116e49 Remove gaps from the example config 2008-03-29 23:29:59 +00:00
Spencer Janssen
708084dd48 Remove gaps 2008-03-25 09:15:26 +00:00
Spencer Janssen
ef516142b9 Remove -fhpc from ghc-options (annoying hackage workaround) 2008-03-29 20:58:04 +00:00
Spencer Janssen
cb51875da6 Remove version numbers from README 2008-03-29 20:41:58 +00:00
Spencer Janssen
167a6e155b Bump version to 0.7 2008-03-29 19:13:36 +00:00
Don Stewart
2b2774f81d no need to expose --resume to the user 2008-03-28 21:42:19 +00:00
Spencer Janssen
16725dfe0d Rename property to stringProperty 2008-03-25 20:18:14 +00:00
Brent Yorgey
15db3c6f0a ManageHook: add a 'property' Query that can get an arbitrary String property from a window (such as WM_WINDOW_ROLE, for example) 2008-03-25 14:54:14 +00:00
Brent Yorgey
6db444eb1a Main.hs: startupHook should be guarded by userCode 2008-03-25 17:12:41 +00:00
Spencer Janssen
46bc3bbd17 Also print compilation errors to stderr 2008-03-24 22:58:57 +00:00
Don Stewart
d948210935 clean up for style 2008-03-22 21:41:16 +00:00
Andrea Rossato
db08970071 add sendMessageWithNoRefresh and have broadcastMessage use it
This patch:
- moves broadcastMessage and restart from Core to Operations (to avoid
  circular imports);
- in Operations introduces sendMessageWithNoRefresh and move
 updateLayout outside windows.
- broadcastMessage now uses sendMessageWithNoRefresh to obey to this
  rules:
  1. if handleMessage returns Nothing no action is taken;
  2. if handleMessage returns a Just ml *only* the layout field of the
     workspace record will be updated.
2008-02-23 13:07:02 +00:00
Spencer Janssen
4c69a85b3f --recompile now forces recompilation of xmonad.hs 2008-03-24 21:24:53 +00:00
Lukas Mai
ac103b8472 add --help option 2008-01-29 23:52:58 +00:00
Don Stewart
029965e4d4 add mod-shift-tab to the default bindings, from Mathias Stearn 2008-03-23 21:14:21 +00:00
Don Stewart
9fd1d4f9d0 more tests 2008-03-23 00:34:36 +00:00
Don Stewart
dbbd934b0b some tests for the size increment handling in Operations.hs 2008-03-22 23:49:52 +00:00
Don Stewart
750544fda9 more properties for splitting horizontally and vertically 2008-03-22 20:18:35 +00:00
Don Stewart
90eae3fd63 test message handling of Full layout 2008-03-22 19:27:28 +00:00
Don Stewart
d6233d0463 formatting 2008-03-22 19:26:35 +00:00
Don Stewart
5f088f4e99 strict fields on layout messages 2008-03-22 19:22:48 +00:00
Don Stewart
f8a7d8d381 QuickCheck properties to fully specify the Tall layout, and its messages 2008-03-22 04:18:01 +00:00
Don Stewart
f7686746c6 clean up Layout.hs, not entirely happy about the impure layouts. 2008-03-22 04:17:18 +00:00
Don Stewart
04ee55c3ca comments 2008-03-22 04:16:54 +00:00
Don Stewart
50ce362626 add hpc generation script 2008-03-22 04:16:40 +00:00
Don Stewart
209b88f821 add QuickCheck property for Full: it produces one window, it is fullscreen, and it is the current window 2008-03-22 00:20:26 +00:00
Don Stewart
c5cca485df QC for pureLayout. confirm pureLayout . Tall produces no overlaps 2008-03-22 00:12:29 +00:00
Don Stewart
0593a282ca whitespace 2008-03-22 00:12:08 +00:00
Don Stewart
351de8d2b6 reenable quickcheck properties for layouts (no overlap, fullscreen) 2008-03-21 23:40:15 +00:00
Don Stewart
4bd9073937 formatting 2008-03-21 23:09:56 +00:00
Don Stewart
79754fd5d3 Revert float location patch. Not Xinerama safe 2008-03-21 21:41:29 +00:00
Lukas Mai
b14de19e8b XMonad.Core: ignore SIGPIPE, let write calls throw 2008-03-21 17:19:11 +00:00
Brent Yorgey
e97c326ff0 update documentation 2008-03-11 16:07:27 +00:00
Andrea Rossato
bc13b4ba07 Reimplement Mirror with runLayout 2008-02-25 08:32:36 +00:00
Andrea Rossato
5bea59a823 Reimplement Choose with runLayout 2008-02-22 19:31:19 +00:00
Andrea Rossato
669a162cfc runLayout is now a LayoutClass method and takes the Workspace and the screen Rectangle 2008-02-22 17:58:15 +00:00
Don Stewart
310c22694e add property for ensureTags behaviour on hidden workspaces 2008-03-10 18:25:57 +00:00
robreim
1c930ba955 Small linecount fix :) 2008-03-08 02:19:39 +00:00
robreim
797204fe6c Change floats to always use the current screen 2008-03-08 01:58:29 +00:00
Don Stewart
a3ecf5d304 use -fhpc by default when testing. All developers should have 6.8.x 2008-03-07 18:42:23 +00:00
Don Stewart
1a4a4a5000 more general properties for view, greedyView 2008-03-07 18:16:57 +00:00
Don Stewart
a8d3564653 rework failure cases in StackSet.view 2008-03-07 18:16:34 +00:00
Don Stewart
d5955b023c bit more code coverage 2008-03-07 18:09:05 +00:00
Don Stewart
4d9a6c2681 more tests. slightly better test coverage 2008-02-27 18:01:13 +00:00
Don Stewart
87193ff61e test geometry setting 2008-02-27 17:55:54 +00:00
Don Stewart
3303c6e05d incorrect invariant test for greedyView 2008-02-25 18:03:50 +00:00
Brent Yorgey
9d9acba45f Add a startupHook.
The only thing I am not sure about here is at what exact point the 
startupHook should get run.  I picked a place that seems to make sense: 
as late as possible, right before entering the main loop.  That way all
the layouts/workspaces/other state are set up and the startupHook can
manipulate them.
2008-02-04 19:24:45 +00:00
Brent Yorgey
cc2754d82a Core.hs: add an Applicative instance for X 2008-02-04 19:23:48 +00:00
gwern0
cea3492d28 update LOC claim in man page 2008-02-15 21:14:20 +00:00
Don Stewart
14d9a194ff add quickstart instructions 2008-02-12 20:35:02 +00:00
Spencer Janssen
e8d1d028ba Remove non-existent windows on restart 2008-02-07 09:11:40 +00:00
Don Stewart
695860f1fd Lift initColor exceptions into Maybe
We should audit all X11 Haskell lib calls we make for whether
they throw undocumented exceptions, and then banish that.
2008-02-06 19:48:58 +00:00
Don Stewart
261f742404 some things to do 2008-02-06 19:25:33 +00:00
Don Stewart
1de1bcded2 module uses CPP 2008-02-06 19:05:21 +00:00
Spencer Janssen
0c697ebbb4 Rename runManageHook to runQuery 2008-02-04 05:33:36 +00:00
daniel
a626083721 let enter dismiss compile errors 2008-02-03 20:28:52 +00:00
Brent Yorgey
481e42ab72 Core.hs, StackSet.hs: some documentation updates 2008-02-01 19:06:53 +00:00
Andrea Rossato
e751c4b62f Make Mirror implement emptyLayout 2008-01-28 00:18:34 +00:00
"Valery V. Vorotyntsev"
730984fd60 xmonad.cabal: add `build-type' to make Cabal happy 2008-01-31 16:32:13 +00:00
Daniel Neri
ad85e11a4a Get version from the Paths_xmonad module generated by Cabal
No need to bump version in more than one place.
2008-01-29 14:40:37 +00:00
Spencer Janssen
2da09787da Kill stale xmonad 0.1 comments 2008-01-28 21:14:18 +00:00
Spencer Janssen
162a54d992 Point to 0.6 release of contrib 2008-01-28 10:11:15 +00:00
Don Stewart
d00d4ca046 notes on releases 2008-01-28 17:10:12 +00:00
Don Stewart
0dd54885eb bump output of --version 2008-01-28 17:08:40 +00:00
Spencer Janssen
f80d593d57 Generalize the type of catchIO, use it in Main.hs 2008-01-28 05:46:51 +00:00
Andrea Rossato
10be8aaae0 Add emptyLayout to LayoutClass, a method to be called when a workspace is empty 2008-01-24 01:32:07 +00:00
Don Stewart
66f623b656 clarify copyright 2008-01-08 18:56:40 +00:00
Spencer Janssen
b86351f3c3 TAG 0.6 2008-01-27 22:06:33 +00:00
Spencer Janssen
8399e80327 More other-modules 2008-01-27 22:01:52 +00:00
Spencer Janssen
3e3d516092 Update example config 2008-01-27 21:23:31 +00:00
Spencer Janssen
d2ae7310d6 Bump version to 0.6 2008-01-27 20:50:00 +00:00
Austin Seipp
ca3e277d2b Updated ./man/xmonad.1.in to contain new command line parameters 2008-01-22 07:01:53 +00:00
Spencer Janssen
bb2b6c7bf8 Depend on QuickCheck < 2 when building tests 2008-01-22 07:02:25 +00:00
Spencer Janssen
d74814af35 Roll testing into the main executable, use Cabal to build the tests 2008-01-19 09:12:15 +00:00
Spencer Janssen
f9799422f9 Simplify duplicate/cloned screen logic 2008-01-18 03:22:28 +00:00
Joachim Breitner
be5e27038f Put the screen removing stuff in getCleanedScreenInfo 2007-12-31 18:15:56 +00:00
Joachim Breitner
1f4b8cb5f6 Ignore cloned screens
This patch ignores screens that are just clones of existing ones,
or are completely contained in another. Currently only for rescreen, not yet for
xmonad start.
2007-12-31 18:06:28 +00:00
Spencer Janssen
da7ca1c29d -Werror when flag(testing) only 2008-01-18 01:48:27 +00:00
nicolas.pouillard
e095621ab9 Export doubleFork 2008-01-14 20:26:12 +00:00
Lukas Mai
93c55c948e reword comment (previous version didn't make sense to me) 2007-11-22 16:59:25 +00:00
nicolas.pouillard
9ff105340e The recompile function now returns a boolean status instead of (). 2008-01-05 22:55:00 +00:00
Spencer Janssen
5e61b137fb Make focus-follows-mouse configurable 2007-12-29 02:33:01 +00:00
Spencer Janssen
aeef36f74c Strictify all XConfig fields, gives nice error messages when a field is forgotten on construction 2007-12-29 02:19:23 +00:00
Spencer Janssen
673f303646 Spelling 2007-12-29 02:16:28 +00:00
Spencer Janssen
7f3c6823d4 Wibble 2007-12-29 02:15:19 +00:00
Spencer Janssen
79f23d6cec Broadcast button events to all layouts, fix for issue #111 2007-12-27 08:03:56 +00:00
Brent Yorgey
46f5e68cfa Config.hs: too many users seem to be ignoring/missing the polite warning not to modify this file; change it to something a bit less polite/more obvious. 2007-12-20 20:15:49 +00:00
Spencer Janssen
76d2bddaf0 Remove desktop manageHook rules in favor of ManageDocks 2007-12-22 11:37:35 +00:00
Spencer Janssen
f5e55f3a27 Wibble 2007-12-22 04:11:51 +00:00
Spencer Janssen
6c72a03fb1 Add support for several flags:
--version: print xmonad's version
 --recompile: recompile xmonad.hs if it is out of date
 --force-recompile: recompile xmonad.hs unconditionally
2007-12-22 02:05:20 +00:00
Spencer Janssen
31c7734f7b Remove getProgName capability from restart, we don't use it anymore 2007-12-19 21:50:11 +00:00
Spencer Janssen
d1af7d986d Flush pending X calls before restarting 2007-12-19 16:20:29 +00:00
tim.thelion
da167bfc11 Allow for sharing of home directory across architectures. 2007-12-18 06:51:46 +00:00
Spencer Janssen
c46f3ad549 Call 'broadcastMessage ReleaseResources' in restart 2007-12-19 06:57:10 +00:00
Adam Vogt
5b42a58d06 Manpage now describes config in ~/.xmonad/xmonad.hs 2007-12-19 02:39:18 +00:00
Adam Vogt
e8292e0e9d Update manpage to describe greedyView 2007-12-19 02:37:26 +00:00
Spencer Janssen
6cd46e12bb Depend on X11-1.4.1, it has crucial bugfixes 2007-12-15 02:21:00 +00:00
Don Stewart
2441275122 1.4.1 X11 dep 2007-12-14 16:05:58 +00:00
Spencer Janssen
f70ab7964e Set withdrawnState after calling hide 2007-12-12 06:02:50 +00:00
Spencer Janssen
237fdbf037 Remove stale comment 2007-12-11 08:42:36 +00:00
Spencer Janssen
5166ede96b Make windows responsible for setting withdrawn state 2007-12-11 08:01:17 +00:00
Spencer Janssen
56463b2391 Remove stale comment 2007-12-11 07:56:41 +00:00
Spencer Janssen
f427c2b0e9 Clean up stale mapped/waitingUnmap state in handle rather than unmanage.
This is an attempt to fix issue #96.  Thanks to jcreigh for the insights
necessary to fix the bug.
2007-12-11 07:48:10 +00:00
Spencer Janssen
287d364e0d Delete windows from waitingUnmap that aren't waitng for any unmaps 2007-12-11 07:45:06 +00:00
Brent Yorgey
8c31768b79 man/xmonad.hs: add some documentation explaining that 'title' can be used in the manageHook just like 'resource' and 'className'. 2007-12-10 17:33:57 +00:00
Lukas Mai
9ceef229c3 normalize Module headers 2007-12-10 08:53:27 +00:00
Spencer Janssen
40581c9bf8 Add 'testing' mode, this should reduce 'darcs check' time significantly 2007-12-10 00:47:04 +00:00
Spencer Janssen
161ade3593 Use XMonad meta-module in Main.hs 2007-12-10 00:44:56 +00:00
Spencer Janssen
f2461c9e3a Remove references to 0.4 2007-12-09 23:23:36 +00:00
Spencer Janssen
11b37429b1 Bump version to 0.5! 2007-12-09 23:15:39 +00:00
Spencer Janssen
bbf5d0010c Rename xmonad.hs to xmonad-template.hs 2007-12-09 23:14:26 +00:00
Andrea Rossato
2f60ee5680 StackSet: some haddock tuning 2007-12-09 16:15:25 +00:00
Don Stewart
3e2d48d5da add a template xmonad.hs 2007-12-09 22:50:18 +00:00
Spencer Janssen
462422d07a Remove kicker and gnome-panel from the default manageHook, these are better
handled by XMonad.Hooks.ManageDocks.  Also, remove the over-complicated list
comprehensions.
2007-12-09 13:54:08 +00:00
Spencer Janssen
33f28ed2ac XMonad.Layouts -> XMonad.Layout 2007-12-08 08:05:53 +00:00
Andrea Rossato
a29590034a Typos and formatting 2007-11-24 14:32:21 +00:00
Andrea Rossato
f394956e56 Move XMonad.Layouts to XMonad.Layout for uniformity with xmc 2007-11-24 14:30:00 +00:00
Spencer Janssen
039d9e2b96 Hide generalized newtype deriving from Haddock 2007-12-08 01:50:15 +00:00
Spencer Janssen
a73f8ec709 Export XMonad.Layouts from XMonad 2007-12-08 01:49:27 +00:00
Spencer Janssen
1bb18654d6 Export XMonad.Operations from XMonad 2007-12-08 00:06:36 +00:00
Spencer Janssen
fa45d59e95 Export Graphics.X11, Graphics.X11.Xlib.Extras, and various Monad stuff from XMonad 2007-12-07 23:35:35 +00:00
Spencer Janssen
f73f8f38a5 Depend on X11>=1.4.0 2007-12-05 04:59:45 +00:00
Spencer Janssen
28cc666a75 Update extra-source-files 2007-12-05 04:44:21 +00:00
Spencer Janssen
c8f16a85cf Update man location 2007-12-05 04:39:13 +00:00
Lukas Mai
6908189698 make Query a MonadIO 2007-11-28 19:51:26 +00:00
Spencer Janssen
39eccc350c Add ManageHook to the XMonad metamodule 2007-11-27 00:28:40 +00:00
Don Stewart
c8ab301c95 update todos before release 2007-11-25 05:27:20 +00:00
Don Stewart
5e310c0c94 Depend on X11 1.4.0 2007-11-25 03:40:12 +00:00
Lukas Mai
4fa10442ab add getXMonadDir (2nd try) 2007-11-21 18:30:18 +00:00
Spencer Janssen
1ab1d729a0 Add 'and' and 'or' functions to ManageHook. 2007-11-21 10:46:13 +00:00
Don Stewart
c95b8d9160 generalise type of `io' 2007-11-21 05:44:07 +00:00
Spencer Janssen
92b4510d7b Add recompilation forcing, clean up recompile's documentation 2007-11-20 22:36:14 +00:00
Spencer Janssen
6114bb371e recompile does not raise any exceptions 2007-11-20 21:58:35 +00:00
Spencer Janssen
7e2ec3840c -no-recomp because we're doing our own recompilation checking 2007-11-20 21:57:44 +00:00
Don Stewart
6ce125a566 pointfree 2007-11-20 18:40:16 +00:00
Don Stewart
3456086f85 clean up fmap overuse with applicatives. more opportunities remain 2007-11-20 18:17:43 +00:00
Spencer Janssen
3b83895d28 ManageHook is a Monoid 2007-11-19 06:08:20 +00:00
Spencer Janssen
dc6ba6b5ee No more liftM 2007-11-19 03:31:20 +00:00
Spencer Janssen
df5003eb16 Refactor recompile 2007-11-19 03:22:55 +00:00
Spencer Janssen
99dd1a30ba Trailing space 2007-11-19 03:06:58 +00:00
Spencer Janssen
d6c5eb3e80 Generalize recompile to MonadIO 2007-11-19 03:04:36 +00:00
Spencer Janssen
9d9b733994 Factor out doubleFork logic 2007-11-19 03:03:53 +00:00
Don Stewart
ea71fd67e8 handle case of xmonad binary not existing, when checking recompilation 2007-11-19 03:00:57 +00:00
Don Stewart
e9eadd6141 Use executeFile directly, rather than the shell, avoiding sh interepeting 2007-11-19 02:50:15 +00:00
Don Stewart
ddf9e49e49 use 'spawn' rather than runProcess, to report errors asynchronously, avoiding zombies 2007-11-19 02:37:12 +00:00
Don Stewart
81803ffe81 use 'spawn' rather than runProcess, to report errors asynchronously, avoiding zombies 2007-11-19 02:37:12 +00:00
Don Stewart
31ce83d04e Use xmessage to present a failure message to users when the config file cannot be loaded 2007-11-19 02:24:29 +00:00
Don Stewart
c2ae7a8c71 only check xmonad.hs against the xmonad binary, not the .o file (meaning you can remove it if you like) 2007-11-19 01:15:28 +00:00
Don Stewart
45eea722be Do our own recompilation checking: only launch ghc if the xmonad.hs is newer than its .o file 2007-11-19 01:07:59 +00:00
Don Stewart
4bb6371155 reformat export list to fit on the page 2007-11-19 00:39:00 +00:00
Devin Mullins
dfd4d435d8 add support for Mac users and their silly case-insensitive filesystems 2007-11-17 02:48:36 +00:00
Don Stewart
ac41c8fb52 some more tweaks 2007-11-16 18:42:27 +00:00
Don Stewart
223b48ab27 more todos: docs 2007-11-16 18:24:44 +00:00
Don Stewart
107b942414 we need examples for the managehook edsl 2007-11-16 18:23:32 +00:00
Don Stewart
6aee5509de more todos 2007-11-16 18:20:33 +00:00
Don Stewart
ba6d9c8a52 polish readme 2007-11-16 18:19:31 +00:00
Don Stewart
3a995b40c9 more polish for config doc 2007-11-16 18:16:40 +00:00
Don Stewart
656f4551da tweak .cabal synopsis a little 2007-11-16 18:12:45 +00:00
Andrea Rossato
6ae94edbe4 Config: small haddock fix 2007-11-16 11:31:58 +00:00
Andrea Rossato
22ccca29e6 Core: documented XConfig and ScreenDetail 2007-11-16 11:28:26 +00:00
"Valery V. Vorotyntsev"
2302bb3304 CONFIG, TODO: fix typos
CONFIG: delete trailing whitespace
2007-11-15 14:41:51 +00:00
Lukas Mai
b4e0e77911 make default ratios in config nicer to look at 2007-11-12 01:35:51 +00:00
Lukas Mai
dcf53fbaf6 refactor main, add "recompile" to XMonad.Core 2007-11-08 23:09:33 +00:00
Don Stewart
833e37da9c comments, reexport Data.Bits 2007-11-14 18:37:59 +00:00
Don Stewart
cf0c3b9ab6 polish .cabal file. add xmonad@ as the default maintainer 2007-11-14 18:27:16 +00:00
Don Stewart
532a920bce add lots more text on configuration 2007-11-14 18:25:31 +00:00
Don Stewart
0d506daf45 refactor trace. 2007-11-14 03:41:09 +00:00
Devin Mullins
4887c5ac42 clarify comment at top of Config.hs
There appears to be some confusion -- several people have wanted to edit
Config.hs as was done in the past. This comment probably won't stop that, but
it's a start.
2007-11-11 19:13:04 +00:00
David Roundy
3d0c08365d avoid Data.Ratio and % operator in XMonad.Config
I think this'll make Config.hs more friendly as a template for folks
to modify.
2007-11-11 18:37:08 +00:00
Devin Mullins
e4c2a81ca1 remove obviated (and confusing) comments 2007-11-11 05:50:47 +00:00
Spencer Janssen
58fc2bc59e XMonad.Main uses FlexibleContexts 2007-11-11 21:45:28 +00:00
David Roundy
c8473e3ae9 hide existential Layout (mostly) from user API. 2007-11-11 00:30:55 +00:00
Don Stewart
11711e1a46 Depend on X11 1.3.0.20071111 2007-11-11 20:09:32 +00:00
Don Stewart
99fb75eb9b update README some more 2007-11-09 18:12:03 +00:00
Don Stewart
ceb1c51b3f we depend on Cabal 1.2.0 or newer 2007-11-09 15:59:34 +00:00
Spencer Janssen
14b6306ac2 Generalize several functions to MonadIO 2007-11-09 06:42:14 +00:00
Spencer Janssen
b51f6f55a8 Docs for ManageHook 2007-11-09 03:18:10 +00:00
Spencer Janssen
e2ab6e8a27 New ManageHook system 2007-11-09 02:47:22 +00:00
Spencer Janssen
a5200b3862 Generalize the type of whenJust 2007-11-07 06:21:26 +00:00
Don Stewart
f81ec95fa0 maybe False (const True) -> isJust. spotted by shachaf 2007-11-08 00:35:39 +00:00
Don Stewart
39f4fe7a90 typo 2007-11-08 00:02:59 +00:00
Don Stewart
d50d6c909d imports not needed in example now 2007-11-07 03:23:46 +00:00
Don Stewart
dbfd13207d Provide top level XMonad.hs export module 2007-11-07 03:06:17 +00:00
Don Stewart
6eb23670bb point to where defns for config stuff can be found 2007-11-07 02:08:01 +00:00
Spencer Janssen
bbe4a27f65 Fix haddock comment 2007-11-07 03:05:10 +00:00
Lukas Mai
94924123bb fall back to previous ~/.xmonad/xmonad if recompilation fails 2007-11-07 01:53:09 +00:00
Don Stewart
ece268cd1e recommend --user 2007-11-06 22:10:04 +00:00
Don Stewart
dfd8e51136 add CONFIG with details of how to configure 2007-11-05 04:07:41 +00:00
Spencer Janssen
0de10862c2 Run only 50 tests per property, decreases test time by 10 seconds on my system 2007-11-05 06:49:44 +00:00
Spencer Janssen
f7b6a4508f Remove stale comment 2007-11-05 06:37:31 +00:00
Spencer Janssen
00f83ac78a Use Cabal's optimization flags rather than -O 2007-11-05 06:17:59 +00:00
Spencer Janssen
3a902ce613 Build the whole thing in the test hook 2007-11-05 06:16:15 +00:00
Spencer Janssen
5342be0e67 -Werror 2007-11-05 06:03:26 +00:00
Spencer Janssen
88845e5d97 Remove superfluous 'extensions:' field 2007-11-05 03:45:15 +00:00
Spencer Janssen
4732557c12 Use configurations in xmonad.cabal 2007-11-05 03:34:28 +00:00
Don Stewart
a13c11ff52 ~/.xmonad/Main.hs is now ~/.xmonad/xmonad.hs ! 2007-11-05 03:26:55 +00:00
Don Stewart
fcea17f920 makeMain -> xmonad 2007-11-05 03:12:03 +00:00
Don Stewart
a5acef3ad6 -Wall police 2007-11-05 02:22:02 +00:00
Don Stewart
76e960a40c remember to compile the xmonad library also with the usual ghc-optoins 2007-11-05 02:21:27 +00:00
Don Stewart
30af3a8f84 EventLoop -> Core, DefaultConfig -> Config 2007-11-05 02:17:05 +00:00
Don Stewart
c9142952c2 clean up DefaultConfig.hs 2007-11-05 02:11:42 +00:00
Don Stewart
934fb2c368 clean up some weird formatting/overboard strictness annotations 2007-11-05 01:14:00 +00:00
Spencer Janssen
d1c29a40cf Update pragmas for GHC 6.8 compatibility 2007-11-04 21:55:07 +00:00
Spencer Janssen
cd9c592ebc Use the layout and workspaces values from the actual configuration used 2007-11-04 02:03:20 +00:00
Spencer Janssen
131e060533 Float handler out of makeMain, make keys and mouseBindings dependent on XConfig for easy modMask switching 2007-11-02 02:59:24 +00:00
Spencer Janssen
4996b1bc47 We can't rely on the executable name, because it may be 'Main' 2007-11-01 20:50:57 +00:00
Spencer Janssen
4b2366b5ce Get defaultGaps from the current config, not the default one 2007-11-01 20:50:25 +00:00
Spencer Janssen
0590f5da9e exposed-modules 2007-11-01 19:33:31 +00:00
Spencer Janssen
c3c39aae12 Hierarchify 2007-11-01 18:08:46 +00:00
Spencer Janssen
7bc4ab41c7 Main.hs -> DefaultConfig.hs, add new Main.hs with 'buildLaunch' 2007-11-01 17:57:49 +00:00
Spencer Janssen
7dc2d254d1 Layouts.Choose: handle ReleaseResources 2007-11-01 15:23:02 +00:00
Spencer Janssen
528d51e58a Layouts.Choose: send Hide to non-selected layout 2007-11-01 15:11:47 +00:00
Spencer Janssen
9ef3fdcf08 Export mirrorRect 2007-11-01 08:56:31 +00:00
Spencer Janssen
e8d3f674ef Only export main from Main 2007-11-01 08:23:26 +00:00
Spencer Janssen
8a5d2490bb Add readsLayout, remove the existential from XConfig 2007-11-01 08:21:55 +00:00
Spencer Janssen
22aacf9bf6 Delete Main.hs-boot! 2007-11-01 08:00:45 +00:00
Spencer Janssen
b0b43050f4 Remove manageHook from Main.hs-boot 2007-11-01 07:53:08 +00:00
Spencer Janssen
23035e944b Remove workspaces from Main.hs-boot 2007-11-01 07:45:56 +00:00
Spencer Janssen
bf52d34bbf -Wall police 2007-11-01 07:44:11 +00:00
Spencer Janssen
8a8c538c23 Eliminate defaultTerminal 2007-11-01 07:31:47 +00:00
Spencer Janssen
e50927ffc0 Store user configuration in XConf 2007-11-01 07:23:08 +00:00
Spencer Janssen
3789f37f25 This is a massive update, here's what has changed:
* Read is no longer a superclass of Layout
 * All of the core layouts have moved to the new Layouts.hs module
 * Select has been replaced by the new statically typed Choose combinator,
   which is heavily based on David Roundy's NewSelect proposal for
   XMonadContrib.  Consequently:
    - Rather than a list of choosable layouts, we use the ||| combinator to
      combine several layouts into a single switchable layout
    - We've lost the capability to JumpToLayout and PrevLayout.  Both can be
      added with some effort
2007-11-01 06:43:18 +00:00
David Roundy
48ccbc7fb2 cleaner version of main/config inversion. 2007-10-29 18:48:23 +00:00
David Roundy
d679ceb234 make setLayout a bit more inclusive. 2007-10-24 23:12:50 +00:00
David Roundy
7b3c1243b7 make xmonad work with inverted main/config. 2007-10-18 17:00:58 +00:00
David Roundy
97fe14dfd2 sketch of config/main inversion. 2007-10-18 16:42:30 +00:00
Don Stewart
c1e039ba88 more precise X11 version required 2007-10-31 20:32:41 +00:00
Don Stewart
9bd11aeea5 tweaks to todo 2007-10-31 16:46:18 +00:00
Don Stewart
c350caf9b8 HEADS UP: remove X11-extras dependency, depend on X11 >= 1.3.0
The X11-extras library has been merged into the larger X11 library,
so we now drop the dependency on X11-extras, and instead build 
against the new X11 library.

If you apply this patch you must build and install X11-1.3.0 or greater
first,

  http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-1.3.0

You can also go ahead and wipe X11-extras from GHC's memory, (for ghci to work
out of the box with the testsuite)

  $ ghc-pkg unregister X11-extras
  $ ghc-pkg unregister --user X11-extras
2007-10-30 22:08:24 +00:00
Spencer Janssen
066da1cd99 New windows start in the iconic state 2007-10-28 06:39:49 +00:00
Don Stewart
cadf81976f add text on using xprop to find client names 2007-10-27 16:30:31 +00:00
Don Stewart
ddd1fa9cae add text reminding people to run mod-shift-space 2007-10-26 22:52:28 +00:00
Brent Yorgey
3bd63adb60 StackSet.hs: (insertUp): remove comments about new window being made master window, since that clearly isn't true. 2007-10-22 21:08:56 +00:00
Brent Yorgey
e384a358b5 Replace 'findIndex' with 'findTag', which more accurately describes what the function does.
I realize this is a big change, but the name 'findIndex' was confusing for me, since I expected it to return some sort of integer.  What it actually does, of course, is return a workspace tag, which might be more general than an index.
Of course, this change breaks several contrib modules; I'll submit a patch to make the change there as well.
2007-10-22 20:41:05 +00:00
Brent Yorgey
8971ab7fae StackSet.hs: (ensureTags): elaborate into a more descriptive comment. 2007-10-22 20:22:12 +00:00
Brent Yorgey
2e8794d0f3 StackSet.hs: remove dead code. 2007-10-22 19:26:36 +00:00
Brent Yorgey
92d58ae0a8 StackSet.hs: (differentiate): 'Texture' doesn't mean anything to me; replace with a more descriptive comment. 2007-10-22 19:13:33 +00:00
Brent Yorgey
33e14e7ba7 StackSet.hs: (new): better comment; 'm' is not an integer, it is a list of screen descriptions. 2007-10-22 18:34:11 +00:00
Brent Yorgey
ec45881d4c StackSet.hs: align some comments 2007-10-22 16:16:01 +00:00
Brent Yorgey
b73ac809ba StackSet.hs: small grammar fix and better flow in comment 2007-10-22 16:08:58 +00:00
Brent Yorgey
d0507c9eb3 StackSet.hs: better comments regarding hidden/visible workspace tracking for Xinerama
I'm not 100% sure that I understand what's going on here, but it seems as though the comment still described an older state of affairs.  I don't see any Map Workspace Screen keeping track of visible workspaces.
2007-10-22 16:02:39 +00:00
Spencer Janssen
fc82a7d412 Add Config.terminal 2007-10-24 10:53:54 +00:00
Don Stewart
cc019f487c explain that you need ghc as well 2007-10-24 03:05:20 +00:00
Spencer Janssen
4c7cf15cdb xmonad, not XMonad 2007-10-23 23:49:00 +00:00
gwern0
350a4d6f6b STYLE: enlarge on existing principles
Comments: the -Wall thing was just trying to say -Wall -Werror should work. The license thing was too narrow - or are my public domain contributions unwelcome because they are not BSD-3? I think comments are most important for exported functions users will use; it isn't so important for helper functions (used only in the module) to be very well-documented, right?
2007-10-23 22:52:25 +00:00
Don Stewart
1ddaffbfba start on style guide 2007-10-23 22:14:22 +00:00
Eric Mertens
0903c76d40 Operations.hs: flip maybe id is fromMaybe 2007-10-18 23:14:18 +00:00
Eric Mertens
156a89b761 Deobfuscate Tall layout 2007-10-18 23:13:29 +00:00
Spencer Janssen
1ea1c05617 setInitialProperties after placing windows 2007-10-19 20:13:10 +00:00
Spencer Janssen
f5ad470815 setInitialProperties after placing windows 2007-10-19 20:13:10 +00:00
Spencer Janssen
1be4bc5d91 Ignore borders in the stored RationalRects of floating windows.
Also, add 'floatWindow' which computes the actual Rectangle for that window,
including border.
2007-10-19 06:39:22 +00:00
Spencer Janssen
0514380d76 Only assign workspace keys up to xK_9. Related to bug #63 2007-10-19 08:37:46 +00:00
Spencer Janssen
18cf8fbb10 Ignore borders in the stored RationalRects of floating windows.
Also, add 'floatWindow' which computes the actual Rectangle for that window,
including border.
2007-10-19 06:39:22 +00:00
Spencer Janssen
eb65473591 I prefer fmap over liftM 2007-10-19 06:31:04 +00:00
Devin Mullins
c734586275 change 0/1/3 to named states, per X11-extras darcs head 2007-10-18 02:16:51 +00:00
David Roundy
74131eb15f remove StackOrNot type synonymn. 2007-10-17 20:14:06 +00:00
Eric Mertens
ac94932345 Operations.hs: make use of notElem and notMember 2007-10-17 17:43:57 +00:00
70 changed files with 9655 additions and 2792 deletions

30
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,30 @@
### Problem Description
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 _full_ configuration file that reproduces
the problem you are experiencing:
```haskell
module Main (main) where
import XMonad
main :: IO ()
main = xmonad def
```
### Checklist
- [ ] I've read [CONTRIBUTING.md](https://github.com/xmonad/xmonad/blob/master/CONTRIBUTING.md)
- I tested my configuration
- [ ] With `xmonad` version XXX (commit XXX if using git)
- [ ] With `xmonad-contrib` version XXX (commit XXX if using git)

15
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,15 @@
### Description
Include a description for your changes, including the motivation
behind them.
### Checklist
- [ ] I've read [CONTRIBUTING.md](https://github.com/xmonad/xmonad/blob/master/CONTRIBUTING.md)
- [ ] I've confirmed these changes don't belong in xmonad-contrib instead
- [ ] I've considered how to best test these changes (property, unit,
manually, ...) and concluded: XXX
- [ ] I updated the `CHANGES.md` file

6
.github/dependabot.yml vendored Normal file
View File

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

33
.github/workflows/generatemanpage.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Generate manpage
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Clone project
uses: actions/checkout@v4
- name: Install dependencies
run: |
set -ex
sudo apt install -y pandoc
- name: Generate manpage
run: |
set -ex
for d in /opt/ghc/*/bin; do PATH="$d:$PATH"; break; done
make -B -C man
- name: Commit/push if changed
run: |
set -ex
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git diff --quiet --exit-code && exit
git commit -a -m 'man: Update'
git push

View File

@@ -0,0 +1,139 @@
Piggy-back on the haskell-ci workflow for automatic releases to Hackage.
This extends the workflow with two additional triggers:
* When the Haskell-CI workflow is triggered manually with a non-empty version
input (matching the version in the cabal file), 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).
Note that promoting the candidate on Hackage discards the uploaded docs
(https://github.com/haskell/hackage-server/issues/70). Don't do that.
* When a release is created on GitHub, a final release is uploaded to Hackage
and docs are submitted for it.
The automation uses a special Hackage user: https://hackage.haskell.org/user/xmonad
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,15 @@
#
name: Haskell-CI
on:
- - push
- - pull_request
+ push:
+ pull_request:
+ release:
+ types:
+ - published
+ workflow_dispatch:
+ inputs:
+ version:
+ description: candidate version (must match version in cabal file)
jobs:
linux:
name: Haskell-CI - Linux - ${{ matrix.compiler }}
@@ -33,6 +40,7 @@
compilerVersion: 9.8.4
setup-method: ghcup
allow-failure: false
+ upload: true
- compiler: ghc-9.6.7
compilerKind: ghc
compilerVersion: 9.6.7
@@ -257,6 +265,10 @@
- name: haddock
run: |
$CABAL v2-haddock --disable-documentation $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
+ - name: haddock for hackage
+ if: matrix.upload
+ run: |
+ $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
@@ -267,3 +279,80 @@
with:
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
path: ~/.cabal/store
+ # must be separate artifacts because GitHub Actions are still broken:
+ # https://github.com/actions/upload-artifact/issues/441
+ # https://github.com/actions/upload-artifact/issues/457
+ - name: upload artifact (sdist)
+ if: matrix.upload
+ uses: actions/upload-artifact@v4
+ with:
+ name: sdist
+ path: ${{ github.workspace }}/sdist/*.tar.gz
+ - name: upload artifact (haddock)
+ if: matrix.upload
+ uses: actions/upload-artifact@v4
+ with:
+ name: haddock
+ path: ${{ github.workspace }}/haddock/*-docs.tar.gz
+ - name: hackage upload (candidate)
+ if: matrix.upload && github.event_name == 'workflow_dispatch' && github.event.inputs.version != ''
+ shell: bash
+ run: |
+ set -ex
+ PACKAGE_VERSION="${PACKAGE_VERSION#v}"
+ res=$(
+ curl \
+ --silent --show-error --output /dev/stderr --write-out '%{http_code}' \
+ --header "Accept: text/plain" \
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
+ --form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
+ https://hackage.haskell.org/packages/candidates/
+ )
+ [[ $res == 2?? ]] # TODO: --fail-with-body once curl 7.76.0 is available
+ res=$(
+ curl \
+ --silent --show-error --output /dev/stderr --write-out '%{http_code}' \
+ -X PUT \
+ --header "Accept: text/plain" \
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
+ --header "Content-Type: application/x-tar" \
+ --header "Content-Encoding: gzip" \
+ --data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
+ https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/candidate/docs
+ )
+ [[ $res == 2?? ]]
+ env:
+ HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
+ PACKAGE_NAME: ${{ github.event.repository.name }}
+ PACKAGE_VERSION: ${{ github.event.inputs.version }}
+ - name: hackage upload (release)
+ if: matrix.upload && github.event_name == 'release'
+ shell: bash
+ run: |
+ set -ex
+ PACKAGE_VERSION="${PACKAGE_VERSION#v}"
+ res=$(
+ curl \
+ --silent --show-error --output /dev/stderr --write-out '%{http_code}' \
+ --header "Accept: text/plain" \
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
+ --form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
+ https://hackage.haskell.org/packages/
+ )
+ [[ $res == 2?? ]] # TODO: --fail-with-body once curl 7.76.0 is available
+ res=$(
+ curl \
+ --silent --show-error --output /dev/stderr --write-out '%{http_code}' \
+ -X PUT \
+ --header "Accept: text/plain" \
+ --header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
+ --header "Content-Type: application/x-tar" \
+ --header "Content-Encoding: gzip" \
+ --data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
+ https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/docs
+ )
+ [[ $res == 2?? ]]
+ env:
+ HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
+ PACKAGE_NAME: ${{ github.event.repository.name }}
+ PACKAGE_VERSION: ${{ github.event.release.tag_name }}

335
.github/workflows/haskell-ci.yml vendored Normal file
View File

@@ -0,0 +1,335 @@
# 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.19.20250506
#
# REGENDATA ("0.19.20250506",["github","cabal.project"])
#
name: Haskell-CI
on:
push:
pull_request:
release:
types:
- published
workflow_dispatch:
inputs:
version:
description: candidate version (must match version in cabal file)
jobs:
linux:
name: Haskell-CI - Linux - ${{ matrix.compiler }}
runs-on: ubuntu-24.04
timeout-minutes:
60
container:
image: buildpack-deps:jammy
continue-on-error: ${{ matrix.allow-failure }}
strategy:
matrix:
include:
- compiler: ghc-9.12.2
compilerKind: ghc
compilerVersion: 9.12.2
setup-method: ghcup
allow-failure: false
- compiler: ghc-9.10.2
compilerKind: ghc
compilerVersion: 9.10.2
setup-method: ghcup
allow-failure: false
- compiler: ghc-9.8.4
compilerKind: ghc
compilerVersion: 9.8.4
setup-method: ghcup
allow-failure: false
upload: true
- compiler: ghc-9.6.7
compilerKind: ghc
compilerVersion: 9.6.7
setup-method: ghcup
allow-failure: false
- compiler: ghc-9.4.8
compilerKind: ghc
compilerVersion: 9.4.8
setup-method: ghcup
allow-failure: false
- compiler: ghc-9.2.8
compilerKind: ghc
compilerVersion: 9.2.8
setup-method: ghcup
allow-failure: false
- compiler: ghc-9.0.2
compilerKind: ghc
compilerVersion: 9.0.2
setup-method: ghcup
allow-failure: false
- compiler: ghc-8.10.7
compilerKind: ghc
compilerVersion: 8.10.7
setup-method: ghcup
allow-failure: false
- compiler: ghc-8.8.4
compilerKind: ghc
compilerVersion: 8.8.4
setup-method: ghcup
allow-failure: false
- compiler: ghc-8.6.5
compilerKind: ghc
compilerVersion: 8.6.5
setup-method: ghcup
allow-failure: false
fail-fast: false
steps:
- name: apt-get install
run: |
apt-get update
apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5
apt-get install -y libx11-dev libxext-dev libxinerama-dev libxrandr-dev libxss-dev
- name: Install GHCup
run: |
mkdir -p "$HOME/.ghcup/bin"
curl -sL https://downloads.haskell.org/ghcup/0.1.50.1/x86_64-linux-ghcup-0.1.50.1 > "$HOME/.ghcup/bin/ghcup"
chmod a+x "$HOME/.ghcup/bin/ghcup"
- name: Install cabal-install
run: |
"$HOME/.ghcup/bin/ghcup" install cabal 3.14.2.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
echo "CABAL=$HOME/.ghcup/bin/cabal-3.14.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
- name: Install GHC (GHCup)
if: matrix.setup-method == 'ghcup'
run: |
"$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false)
HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER")
HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#')
HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#')
echo "HC=$HC" >> "$GITHUB_ENV"
echo "HCPKG=$HCPKG" >> "$GITHUB_ENV"
echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV"
env:
HCKIND: ${{ matrix.compilerKind }}
HCNAME: ${{ matrix.compiler }}
HCVER: ${{ matrix.compilerVersion }}
- 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"
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=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV"
env:
HCKIND: ${{ matrix.compilerKind }}
HCNAME: ${{ matrix.compiler }}
HCVER: ${{ matrix.compilerVersion }}
- 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 <<EOF
program-default-options
ghc-options: $GHCJOBS +RTS -M3G -RTS
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: install cabal-plan
run: |
mkdir -p $HOME/.cabal/bin
curl -sL https://github.com/haskell-hvr/cabal-plan/releases/download/v0.7.3.0/cabal-plan-0.7.3.0-x86_64-linux.xz > cabal-plan.xz
echo 'f62ccb2971567a5f638f2005ad3173dba14693a45154c1508645c52289714cb2 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: checkout
uses: actions/checkout@v4
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="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/xmonad-[0-9.]*')"
echo "PKGDIR_xmonad=${PKGDIR_xmonad}" >> "$GITHUB_ENV"
rm -f cabal.project cabal.project.local
touch cabal.project
touch cabal.project.local
echo "packages: ${PKGDIR_xmonad}" >> cabal.project
echo "package xmonad" >> cabal.project
echo " ghc-options: -Werror=missing-methods" >> cabal.project
cat >> cabal.project <<EOF
optimization: False
package xmonad
flags: +pedantic
EOF
$HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: any.$_ installed\n" unless /^(xmonad)$/; }' >> 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: restore cache
uses: actions/cache/restore@v4
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: cabal check
run: |
cd ${PKGDIR_xmonad} || false
${CABAL} -vnormal check
- name: haddock
run: |
$CABAL v2-haddock --disable-documentation $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
- name: haddock for hackage
if: matrix.upload
run: |
$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: save cache
if: always()
uses: actions/cache/save@v4
with:
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
path: ~/.cabal/store
# must be separate artifacts because GitHub Actions are still broken:
# https://github.com/actions/upload-artifact/issues/441
# https://github.com/actions/upload-artifact/issues/457
- name: upload artifact (sdist)
if: matrix.upload
uses: actions/upload-artifact@v4
with:
name: sdist
path: ${{ github.workspace }}/sdist/*.tar.gz
- name: upload artifact (haddock)
if: matrix.upload
uses: actions/upload-artifact@v4
with:
name: haddock
path: ${{ github.workspace }}/haddock/*-docs.tar.gz
- name: hackage upload (candidate)
if: matrix.upload && github.event_name == 'workflow_dispatch' && github.event.inputs.version != ''
shell: bash
run: |
set -ex
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
res=$(
curl \
--silent --show-error --output /dev/stderr --write-out '%{http_code}' \
--header "Accept: text/plain" \
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
--form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
https://hackage.haskell.org/packages/candidates/
)
[[ $res == 2?? ]] # TODO: --fail-with-body once curl 7.76.0 is available
res=$(
curl \
--silent --show-error --output /dev/stderr --write-out '%{http_code}' \
-X PUT \
--header "Accept: text/plain" \
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
--header "Content-Type: application/x-tar" \
--header "Content-Encoding: gzip" \
--data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/candidate/docs
)
[[ $res == 2?? ]]
env:
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
PACKAGE_NAME: ${{ github.event.repository.name }}
PACKAGE_VERSION: ${{ github.event.inputs.version }}
- name: hackage upload (release)
if: matrix.upload && github.event_name == 'release'
shell: bash
run: |
set -ex
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
res=$(
curl \
--silent --show-error --output /dev/stderr --write-out '%{http_code}' \
--header "Accept: text/plain" \
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
--form package=@"${GITHUB_WORKSPACE}/sdist/${PACKAGE_NAME}-${PACKAGE_VERSION}.tar.gz" \
https://hackage.haskell.org/packages/
)
[[ $res == 2?? ]] # TODO: --fail-with-body once curl 7.76.0 is available
res=$(
curl \
--silent --show-error --output /dev/stderr --write-out '%{http_code}' \
-X PUT \
--header "Accept: text/plain" \
--header "Authorization: X-ApiKey $HACKAGE_API_KEY" \
--header "Content-Type: application/x-tar" \
--header "Content-Encoding: gzip" \
--data-binary @"${GITHUB_WORKSPACE}/haddock/${PACKAGE_NAME}-${PACKAGE_VERSION}-docs.tar.gz" \
https://hackage.haskell.org/package/${PACKAGE_NAME}-${PACKAGE_VERSION}/docs
)
[[ $res == 2?? ]]
env:
HACKAGE_API_KEY: ${{ secrets.HACKAGE_API_KEY }}
PACKAGE_NAME: ${{ github.event.repository.name }}
PACKAGE_VERSION: ${{ github.event.release.tag_name }}

22
.github/workflows/hlint.yaml vendored Normal file
View File

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

21
.github/workflows/nix.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
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@v31
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
- name: Clone project
uses: actions/checkout@v4
- name: Build
run: nix build --print-build-logs

48
.github/workflows/packdeps.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
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@v4
- name: Setup Haskell
uses: haskell-actions/setup@v2
with:
# packdeps doesn't build with newer as of 2021-10
ghc-version: '8.8'
- name: Install packdeps
run: |
set -ex
cd # go somewhere without a cabal.project
cabal install packdeps
- name: Check package bounds (all)
continue-on-error: true
run: |
set -ex
packdeps \
--exclude X11 \
*.cabal
- name: Check package bounds (preferred)
run: |
set -ex
packdeps \
--preferred \
--exclude X11 \
*.cabal
workflow-keepalive:
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- uses: liskin/gh-workflow-keepalive@v1

79
.github/workflows/stack.yml vendored Normal file
View File

@@ -0,0 +1,79 @@
name: Stack
on:
push:
pull_request:
jobs:
build:
name: Stack CI - Linux - ${{ matrix.resolver }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- resolver: lts-14 # GHC 8.6
- resolver: lts-16 # GHC 8.8
- resolver: lts-18 # GHC 8.10
- resolver: lts-19 # GHC 9.0
- resolver: lts-20 # GHC 9.2
- resolver: lts-21 # GHC 9.4
- resolver: lts-22 # GHC 9.6
- resolver: lts-23 # GHC 9.8
steps:
- name: Clone project
uses: actions/checkout@v4
- name: Install C dependencies
run: |
set -ex
sudo apt update -y
sudo apt install -y \
libx11-dev \
libxext-dev \
libxinerama-dev \
libxrandr-dev \
libxss-dev \
#
- 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. Additionally, the
# date is prefixed with an epoch number to let us manually refresh the
# cache when needed. This is a workaround for https://github.com/actions/cache/issues/2
run: |
date +date=1-%Y-%m >> $GITHUB_OUTPUT
- name: Cache Haskell package metadata
uses: actions/cache@v4
with:
path: ~/.stack/pantry
key: stack-pantry-${{ runner.os }}-${{ steps.cache-date.outputs.date }}
- name: Cache Haskell dependencies
uses: actions/cache@v4
with:
path: |
~/.stack/*
!~/.stack/pantry
!~/.stack/programs
key: stack-${{ runner.os }}-${{ matrix.resolver }}-${{ steps.cache-date.outputs.date }}-${{ hashFiles('stack.yaml') }}-${{ hashFiles('*.cabal') }}
restore-keys: |
stack-${{ runner.os }}-${{ matrix.resolver }}-${{ steps.cache-date.outputs.date }}-${{ hashFiles('stack.yaml') }}-
stack-${{ runner.os }}-${{ matrix.resolver }}-${{ steps.cache-date.outputs.date }}-
- 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
stack test \
--fast --no-terminal \
--resolver=${{ matrix.resolver }} --system-ghc \
--flag=xmonad:pedantic

32
.gitignore vendored Normal file
View File

@@ -0,0 +1,32 @@
.hpc/
*.hi
*.o
*.p_hi
*.prof
*.tix
# editor temp files
*#
.#*
*~
.*.swp
# TAGS files
TAGS
tags
# stack artifacts
/.stack-work/
# cabal-install artifacts
/.*.environment.*-*
/.cabal-sandbox/
/cabal.config
/cabal.project.local
/cabal.sandbox.config
/dist-newstyle/
/dist/
# nix artifacts
result

2
.hlint.yaml Normal file
View File

@@ -0,0 +1,2 @@
# Ignore these warnings.
- ignore: {name: "Use camelCase"}

39
.mailmap Normal file
View File

@@ -0,0 +1,39 @@
Adam Plaice <plaice.adam+github@gmail.com>
Brandon S Allbery KF8NH <allbery.b@gmail.com>
Brent Yorgey <byorgey@gmail.com> <byorgey@cis.upenn.edu>
Conrad Irwin <conrad.irwin@gmail.com>
Daniel Neri <daniel.neri@sigicom.com> <daniel.neri@sigicom.se>
Daniel Schoepe <daniel.schoepe@gmail.com> <asgaroth_@gmx.de>
Daniel Wagner <me@dmwit.com> <daniel@wagner-home.com>
David Glasser <glasser@mit.edu>
Deven Lahoti <deven.lahoti@gmail.com>
Devin Mullins <devin.mullins@gmail.com> <me@twifkak.com>
Don Stewart <dons00@gmail.com> <dons@cse.unsw.edu.au>
Don Stewart <dons00@gmail.com> <dons@galois.com>
Felix Springer <felixspringer149@gmail.com> <39434424+jumper149@users.noreply.github.com>
Gwern Branwen <gwern@gwern.net> <gwern0@gmail.com>
Lukas Mai <l.mai@web.de>
Marshall Lochbaum <mwlochbaum@gmail.com>
Michael G. Sloan <mgsloan@gmail.com>
Neil Mitchell <ndmitchell@gmail.com> <http://www.cs.york.ac.uk/~ndm/>
Neil Mitchell <ndmitchell@gmail.com> Neil Mitchell <unknown>
Nick Burlett <nickburlett@mac.com>
Nicolas Pouillard <nicolas.pouillard@gmail.com>
Nik Nyby <nnyby@columbia.edu>
Peter J. Jones <pjones@devalot.com>
Peter J. Jones <pjones@devalot.com> <pjones@pmade.com>
Robert Marlow <bobstopper@bobturf.org>
Robert Marlow <bobstopper@bobturf.org> <robreim@bobturf.org>
Sam Hughes <hughes@rpi.edu>
Shae Erisson <shae@ScannedInAvian.com>
Sibi Prabakaran <sibi@psibi.in>
Sibi Prabakaran <sibi@psibi.in> <psibi2000@gmail.com>
Spencer Janssen <spencerjanssen@gmail.com> <sjanssen@cse.unl.edu>
Timothy Hobbs <tim.thelion@gmail.com>
Tomas Janousek <tomi@nomi.cz>
Valery V. Vorotyntsev <valery.vv@gmail.com>
Vanessa McHale <vamchale@gmail.com> <vanessa.mchale@reconfigure.io>
Wirt Wolff <wirtwolff@gmail.com>
Tony Zorman <soliditsallgood@mailbox.org> <50166980+slotThe@users.noreply.github.com>
Tony Zorman <soliditsallgood@mailbox.org>

323
CHANGES.md Normal file
View File

@@ -0,0 +1,323 @@
# Change Log / Release Notes
## _unreleased_
### Breaking Changes
* Use `cabal` for `--recompile` if there is a `.cabal` file in the config
directory and none of `build`, `stack.yaml`, `flake.nix`, nor `default.nix`
exist.
### Enhancements
### Bug Fixes
### Other
PR #404 (see last change in 0.17.1) has been reverted, because the affected
compilers are (hopefully) no longer being used.
All 9.0 releases of GHC, plus 9.2.1 and 9.2.2 have the join point bug.
Note that 9.0.x is known to also have GC issues and is officially deprecated,
and the only 9.2 release that should be used is 9.2.8. Additionally, GHC HQ
doesn't support releases before 9.6.6.
## 0.18.0 (February 3, 2024)
### Breaking Changes
* Dropped support for GHC 8.4.
### Enhancements
* Exported `sendRestart` and `sendReplace` from `XMonad.Operations`.
* Exported `buildLaunch` from `XMonad.Main`.
* `Tall` does not draw windows with zero area.
* `XMonad.Operations.floatLocation` now applies size hints. This means windows
will snap to these hints as soon as they're floated (mouse move, keybinding).
Previously that only happened on mouse resize.
* Recompilation now detects `flake.nix` and `default.nix` (can be a
symlink) and switches to using `nix build` as appropriate.
* Added `unGrab` to `XMonad.Operations`; this releases XMonad's passive
keyboard grab, so other applications (like `scrot`) can do their
thing.
### Bug Fixes
* Duplicated floats (e.g. from X.A.CopyToAll) no longer escape to inactive
screens.
## 0.17.2 (April 2, 2023)
### Bug Fixes
* Fixed the build with GHC 9.6.
## 0.17.1 (September 3, 2022)
### Enhancements
* Added custom cursor shapes for resizing and moving windows.
* Exported `cacheNumlockMask` and `mkGrabs` from `XMonad.Operations`.
* Added `willFloat` function to `XMonad.ManageHooks` to detect whether the
(about to be) managed window will be a floating window or not.
### Bug Fixes
* Fixed border color of windows with alpha channel. Now all windows have the
same opaque border color.
* Change the main loop to try to avoid [GHC bug 21708] on systems
running GHC 9.2 up to version 9.2.3. The issue has been fixed in
[GHC 9.2.4] and all later releases.
[GHC bug 21708]: https://gitlab.haskell.org/ghc/ghc/-/issues/21708
[GHC 9.2.4]: https://discourse.haskell.org/t/ghc-9-2-4-released/4851
## 0.17.0 (October 27, 2021)
### Enhancements
* Migrated `X.L.LayoutCombinators.(|||)` into `XMonad.Layout`, providing the
ability to directly jump to a layout with the `JumpToLayout` message.
* Recompilation now detects `stack.yaml` (can be a symlink) alongside
`xmonad.hs` and switches to using `stack ghc`. We also updated INSTALL.md
with instructions for cabal-install that lead to correct recompilation.
Deprecation warnings during recompilation are no longer suppressed to make
it easier for us to clean up the codebase. These can still be suppressed
manually using an `OPTIONS_GHC` pragma with `-Wno-deprecations`.
* Improve handling of XDG directories.
1. If all three of xmonad's environment variables (`XMONAD_DATA_DIR,`
`XMONAD_CONFIG_DIR`, and `XMONAD_CACHE_DIR`) are set, use them.
2. If there is a build script called `build` (see [these build scripts]
for usage examples) or configuration `xmonad.hs` in `~/.xmonad`, set
all three directories to `~/.xmonad`.
3. Otherwise, use the `xmonad` directory in `XDG_DATA_HOME`,
`XDG_CONFIG_HOME`, and `XDG_CACHE_HOME` (or their respective
fallbacks). These directories are created if necessary.
In the cases of 1. and 3., the build script or executable is expected to be
in the config dir.
Additionally, the xmonad config binary and intermediate object files were
moved to the cache directory (only relevant if using XDG or
`XMONAD_CACHE_DIR`).
* Added `Foldable`, `Functor`, and `Traversable` instances for `Stack`.
* Added `Typeable layout` constraint to `LayoutClass`, making it possible to
cast `Layout` back into a concrete type and extract current layout state
from it.
* Export constructor for `Choose` and `CLR` from `Module.Layout` to allow
pattern-matching on the left and right sub-layouts of `Choose l r a`.
* Added `withUnfocused` function to `XMonad.Operations`, allowing for `X`
operations to be applied to unfocused windows.
[these build scripts]: https://github.com/xmonad/xmonad-testing/tree/master/build-scripts
### Bug Fixes
* Fixed a bug when using multiple screens with different dimensions, causing
some floating windows to be smaller/larger than the size they requested.
* Compatibility with GHC 9.0
* Fixed dunst notifications being obscured when moving floats.
https://github.com/xmonad/xmonad/issues/208
### Breaking Changes
* Made `(<&&>)` and `(<||>)` non-strict in their right operand; i.e., these
operators now implement short-circuit evaluation so the right operand is
evaluated only if the left operand does not suffice to determine the
result.
* Change `ScreenDetail` to a newtype and make `RationalRect` strict in its
contents.
* Added the `extensibleConf` field to `XConfig` which makes it easier for
contrib modules to have composable configuration (custom hooks, …).
* `util/GenerateManpage.hs` is no longer distributed in the tarball.
Instead, the manpage source is regenerated and manpage rebuilt
automatically in CI.
* `DestroyWindowEvent` is now broadcasted to layouts to let them know
window-specific resources can be discarded.
## 0.15 (September 30, 2018)
* Reimplement `sendMessage` to deal properly with windowset changes made
during handling.
* Add new library functions `windowBracket` and `modifyWindowSet` to
`XMonad.Operations`.
## 0.14.2 (August 21, 2018)
### Bug Fixes
* Add the sample configuration file xmonad.hs again to the release tarball.
[https://github.com/xmonad/xmonad/issues/181]
## 0.14.1 (August 20, 2018)
### Breaking Changes
* The cabal build no longer installs xmonad.hs, xmonad.1, and xmonad.1.html
as data files. The location cabal picks for chose files isn't useful as
standard tools like man(1) won't find them there. Instead, we rely on
distributors to pick up the files from the source tarball during the build
and to install them into proper locations where their users expect them.
[https://github.com/xmonad/xmonad/pull/127]
### Bug Fixes
* Add support for GHC 8.6.x by providing an instance for 'MonadFail X'. A
side effect of that change is that our code no longer compiles with GHC
versions prior to 8.0.x. We could work around that, no doubt, but the
resulting code would require CPP and Cabal flags and whatnot. It feels more
reasonable to just require a moderately recent compiler instead of going
through all that trouble.
* xmonad no longer always recompile on startup. Now it only does so if the
executable does not have the name that would be used for the compilation
output. The purpose of recompiling and executing the results in this case is
so that the `xmonad` executable in the package can be used with custom
configurations.
### Enhancements
* Whenever xmonad recompiles, it now explains how it is attempting to
recompile, by outputting logs to stderr. If you are using xmonad as a custom
X session, then this will end up in a `.xsession-errors` file.
## 0.14 (July 30, 2018)
### Bug Fixes
* The state file that xmonad uses while restarting itself is now
removed after it is processed. This fixes a bug that manifested
in several different ways:
- Names of old workspaces would be resurrected after a restart
- Screen sizes would be wrong after changing monitor configuration (#90)
- `spawnOnce` stopped working (xmonad/xmonad-contrib#155)
- Focus did not follow when moving between workspaces (#87)
- etc.
* Recover old behavior (in 0.12) when `focusFollowsMouse == True`:
the focus follows when the mouse enters another workspace
but not moving into any window.
* Compiles with GHC 8.4.1
* Restored compatibility with GHC version prior to 8.0.1 by removing the
dependency on directory version 1.2.3.
## 0.13 (February 10, 2017)
### Breaking Changes
* When restarting xmonad, resume state is no longer passed to the
next process via the command line. Instead, a temporary state
file is created and xmonad's state is serialized to that file.
When upgrading to 0.13 from a previous version, the `--resume`
command line option will automatically migrate to a state file.
This fixes issue #12.
### Enhancements
* You can now control which directory xmonad uses for finding your
configuration file and which one is used for storing the compiled
version of your configuration. In order of preference:
1. New environment variables. If you want to use these ensure
you set the correct environment variable and also create the
directory it references:
- `XMONAD_CONFIG_DIR`
- `XMONAD_CACHE_DIR`
- `XMONAD_DATA_DIR`
2. The `~/.xmonad` directory.
3. XDG Base Directory Specification directories, if they exist:
- `XDG_CONFIG_HOME/xmonad`
- `XDG_CACHE_HOME/xmonad`
- `XDG_DATA_HOME/xmonad`
If none of these directories exist then one will be created using
the following logic: If the relevant environment variable
mentioned in step (1) above is set, the referent directory will be
created and used. Otherwise `~/.xmonad` will be created and used.
This fixes a few issues, notably #7 and #56.
* A custom build script can be used when xmonad is given the
`--recompile` command line option. If an executable named `build`
exists in the xmonad configuration directory it will be called
instead of `ghc`. It takes one argument, the name of the
executable binary it must produce.
This fixes #8. (One of two possible custom build solutions. See
the next entry for another solution.)
* For users who build their xmonad configuration using tools such as
cabal or stack, there is another option for executing xmonad.
Instead of running the `xmonad` executable directly, arrange to
have your login manager run your configuration binary instead.
Then, in your binary, use the new `launch` command instead of
`xmonad`.
This will keep xmonad from using its configuration file
checking/compiling code and directly start the window manager
without `exec`ing any other binary.
See the documentation for the `launch` function in `XMonad.Main`
for more details.
Fixes #8. (Second way to have a custom build environment for
XMonad. See previous entry for another solution.)
## 0.12 (December 14, 2015)
* Compiles with GHC 7.10.2, 7.8.4, and 7.6.3
* Use of [data-default][] allows using `def` where previously you
had to write `defaultConfig`, `defaultXPConfig`, etc.
* The [setlocale][] package is now used instead of a binding shipped
with xmonad proper allowing the use of `Main.hs` instead of
`Main.hsc`
* No longer encodes paths for `spawnPID`
* The default `manageHook` no longer floats Gimp windows
* Doesn't crash when there are fewer workspaces than screens
* `Query` is now an instance of `Applicative`
* Various improvements to the example configuration file
[data-default]: http://hackage.haskell.org/package/data-default
[setlocale]: https://hackage.haskell.org/package/setlocale

131
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,131 @@
# Contributing to xmonad and xmonad-contrib
## Before Creating a GitHub Issue
New issue submissions should adhere to the following guidelines:
* Does your issue have to do with [xmonad][], [xmonad-contrib][], or
maybe even with the [X11][] library?
Please submit your issue to the **correct** GitHub repository.
* To help you figure out which repository to submit your issue to,
and to help us resolve the problem you are having, create the
smallest configuration file you can that reproduces the problem.
You may find that the [xmonad-testing][] repository is helpful in
reproducing the problem with a smaller configuration file.
Once you've done that please include the configuration file with
your GitHub issue.
* If possible, use the [xmonad-testing][] repository to test your
configuration with the bleeding-edge development version of xmonad
and xmonad-contrib. We might have already fixed your problem.
## Contributing Changes/Patches
Have a change to xmonad that you want included in the next release?
Awesome! Here are a few things to keep in mind:
* Review the above section about creating GitHub issues.
* It's always best to talk with the community before making any
nontrivial changes to xmonad. There are a couple of ways you can
chat with us:
- Join the [`#xmonad` IRC channel] on `irc.libera.chat` or the
official [matrix channel], which is linked to IRC. This is the
preferred (and fastest!) way to get into contact with us.
- Post a message to the [mailing list][ml].
* [XMonad.Doc.Developing][xmonad-doc-developing] is a great
resource to get an overview of xmonad. Make sure to also check
it if you want more details on the coding style.
* Continue reading this document!
## Expediting Reviews and Merges
Here are some tips for getting your changes merged into xmonad:
* If your changes can go into [xmonad-contrib][] instead
of [xmonad][], please do so. We rarely accept new features to
xmonad. (Not that we don't accept changes to xmonad, just that we
prefer changes to xmonad-contrib instead.)
* Change the fewest files as possible. If it makes sense, submit a
completely new module to xmonad-contrib.
* Your changes should include relevant entries in the `CHANGES.md`
file. Help us communicate changes to the community.
* Make sure you test your changes against the most recent commit of
[xmonad][] (and [xmonad-contrib][], if you're contributing there).
If you're adding a new module or functionality, make sure to add an
example in the documentation and in the PR description.
* Make sure you run the automated tests. Both [xmonad-contrib][]
and [xmonad][] have test-suites that you could run with
`stack test` for example.
* When committing, try to follow existing practices. For more
information on what good commit messages look like, see [How to
Write a Git Commit Message][commit-cbeams] and the [Kernel
documentation][commit-kernel] about committing logical changes
separately.
## Style Guidelines
Below are some common style guidelines that all of the core modules
follow. Before submitting a pull request, make sure that your code does
as well!
* Comment every top level function (particularly exported functions),
and provide a type signature; use Haddock syntax in the comments.
* Follow the coding style of the module that you are making changes to
(`n` spaces for indentation, where to break long type signatures, …).
* New code should not introduce any new warnings. If you want to
check this yourself before submitting a pull request, there is the
`pedantic` flag, which is enforced in our CI. You can enable it by
building your changes with `stack build --flag xmonad:pedantic` or
`cabal build --flag pedantic`.
* Likewise, your code should be free of [hlint] warnings; this is also
enforced in our GitHub CI.
* Partial functions are to be avoided: the window manager should not
crash, so do not call `error` or `undefined`.
* Any pure function added to the core should have QuickCheck
properties precisely defining its behavior.
* New modules should identify the author, and be submitted under the
same license as xmonad (BSD3 license).
## Keep rocking!
xmonad is a passion project created and maintained by the community.
We'd love for you to maintain your own contributed modules (approve
changes from other contributors, review code, etc.). However, before
we'd be comfortable adding you to the [xmonad GitHub
organization][xmonad-gh-org] we need to trust that you have sufficient
knowledge of Haskell and git; and have a way of chatting with you ([IRC,
Matrix, etc.][community]).
[hlint]: https://github.com/ndmitchell/hlint
[xmonad]: https://github.com/xmonad/xmonad
[xmonad-contrib]: https://github.com/xmonad/xmonad-contrib
[xmonad-testing]: https://github.com/xmonad/xmonad-testing
[x11]: https://github.com/xmonad/X11
[ml]: https://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad
[xmonad-doc-developing]: https://xmonad.github.io/xmonad-docs/xmonad-contrib/XMonad-Doc-Developing.html
[`#xmonad` IRC channel]: https://web.libera.chat/#xmonad
[matrix channel]: https://matrix.to/#/#xmonad:matrix.org
[commit-cbeams]: https://cbea.ms/git-commit/
[commit-kernel]: https://www.kernel.org/doc/html/v4.10/process/submitting-patches.html#separate-your-changes
[community]: https://xmonad.org/community.html
[xmonad-gh-org]: https://github.com/xmonad

259
Config.hs
View File

@@ -1,259 +0,0 @@
-----------------------------------------------------------------------------
-- |
-- Module : Config.hs
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : dons@galois.com
-- Stability : stable
-- Portability : portable
--
-- This module specifies configurable defaults for xmonad. If you change
-- values here, be sure to recompile and restart (mod-q) xmonad,
-- for the changes to take effect.
--
------------------------------------------------------------------------
module Config where
--
-- Useful imports
--
import XMonad
import Operations
import qualified StackSet as W
import Data.Ratio
import Data.Bits ((.|.))
import qualified Data.Map as M
import System.Exit
import Graphics.X11.Xlib
-- % Extension-provided imports
-- | The default number of workspaces (virtual screens) and their names.
-- By default we use numeric strings, but any string may be used as a
-- workspace name. The number of workspaces is determined by the length
-- of this list.
--
-- A tagging example:
--
-- > workspaces = ["web", "irc", "code" ] ++ map show [4..9]
--
workspaces :: [WorkspaceId]
workspaces = map show [1 .. 9 :: Int]
-- | modMask lets you specify which modkey you want to use. The default
-- is mod1Mask ("left alt"). You may also consider using mod3Mask
-- ("right alt"), which does not conflict with emacs keybindings. The
-- "windows key" is usually mod4Mask.
--
modMask :: KeyMask
modMask = mod1Mask
-- | The mask for the numlock key. Numlock status is "masked" from the
-- current modifier status, so the keybindings will work with numlock on or
-- off. You may need to change this on some systems.
--
-- You can find the numlock modifier by running "xmodmap" and looking for a
-- modifier with Num_Lock bound to it:
--
-- > $ xmodmap | grep Num
-- > mod2 Num_Lock (0x4d)
--
-- Set numlockMask = 0 if you don't have a numlock key, or want to treat
-- numlock status separately.
--
numlockMask :: KeyMask
numlockMask = mod2Mask
-- | Width of the window border in pixels.
--
borderWidth :: Dimension
borderWidth = 1
-- | Border colors for unfocused and focused windows, respectively.
--
normalBorderColor, focusedBorderColor :: String
normalBorderColor = "#dddddd"
focusedBorderColor = "#ff0000"
-- | Default offset of drawable screen boundaries from each physical
-- screen. Anything non-zero here will leave a gap of that many pixels
-- on the given edge, on the that screen. A useful gap at top of screen
-- for a menu bar (e.g. 15)
--
-- An example, to set a top gap on monitor 1, and a gap on the bottom of
-- monitor 2, you'd use a list of geometries like so:
--
-- > defaultGaps = [(18,0,0,0),(0,18,0,0)] -- 2 gaps on 2 monitors
--
-- Fields are: top, bottom, left, right.
--
defaultGaps :: [(Int,Int,Int,Int)]
defaultGaps = [(0,0,0,0)] -- 15 for default dzen font
------------------------------------------------------------------------
-- Window rules
-- | Execute arbitrary actions and WindowSet manipulations when managing
-- a new window. You can use this to, for example, always float a
-- particular program, or have a client always appear on a particular
-- workspace.
--
manageHook :: Window -- ^ the new window to manage
-> String -- ^ window title
-> String -- ^ window resource name
-> String -- ^ window resource class
-> X (WindowSet -> WindowSet)
-- Always float various programs:
manageHook w _ _ c | c `elem` floats = fmap (W.float w . snd) (floatLocation w)
where floats = ["MPlayer", "Gimp"]
-- Desktop panels and dock apps should be ignored by xmonad:
manageHook w _ n _ | n `elem` ignore = reveal w >> return (W.delete w)
where ignore = ["gnome-panel", "desktop_window", "kicker", "kdesktop"]
-- Automatically send Firefox windows to the "web" workspace:
-- If a workspace named "web" doesn't exist, the window will appear on the
-- current workspace.
manageHook _ _ "Gecko" _ = return $ W.shift "web"
-- The default rule: return the WindowSet unmodified. You typically do not
-- want to modify this line.
manageHook _ _ _ _ = return id
------------------------------------------------------------------------
-- Extensible layouts
-- | The list of possible layouts. Add your custom layouts to this list.
layouts :: [Layout Window]
layouts = [ Layout tiled
, Layout $ Mirror tiled
, Layout Full
-- Add extra layouts you want to use here:
-- % Extension-provided layouts
]
where
-- default tiling algorithm partitions the screen into two panes
tiled = Tall nmaster delta ratio
-- The default number of windows in the master pane
nmaster = 1
-- Default proportion of screen occupied by master pane
ratio = 1%2
-- Percent of screen to increment by when resizing panes
delta = 3%100
-- | The top level layout switcher. Most users will not need to modify this binding.
--
-- By default, we simply switch between the layouts listed in `layouts'
-- above, but you may program your own selection behaviour here. Layout
-- transformers, for example, would be hooked in here.
--
layoutHook :: Layout Window
layoutHook = Layout $ Select layouts
-- | Register with xmonad a list of layouts whose state we can preserve over restarts.
-- There is typically no need to modify this list, the defaults are fine.
--
serialisedLayouts :: [Layout Window]
serialisedLayouts = layoutHook : layouts
------------------------------------------------------------------------
-- Logging
-- | Perform an arbitrary action on each internal state change or X event.
-- Examples include:
-- * do nothing
-- * log the state to stdout
--
-- See the 'DynamicLog' extension for examples.
--
logHook :: X ()
logHook = return ()
------------------------------------------------------------------------
-- Key bindings:
-- | The xmonad key bindings. Add, modify or remove key bindings here.
--
-- (The comment formatting character is used when generating the manpage)
--
keys :: M.Map (KeyMask, KeySym) (X ())
keys = M.fromList $
-- launching and killing programs
[ ((modMask .|. shiftMask, xK_Return), spawn "xterm") -- %! Launch an xterm
, ((modMask, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"") -- %! Launch dmenu
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun") -- %! Launch gmrun
, ((modMask .|. shiftMask, xK_c ), kill) -- %! Close the focused window
, ((modMask, xK_space ), sendMessage NextLayout) -- %! Rotate through the available layout algorithms
, ((modMask .|. shiftMask, xK_space ), setLayout layoutHook) -- %! Reset the layouts on the current workspace to default
, ((modMask, xK_n ), refresh) -- %! Resize viewed windows to the correct size
-- move focus up or down the window stack
, ((modMask, xK_Tab ), windows W.focusDown) -- %! Move focus to the next window
, ((modMask, xK_j ), windows W.focusDown) -- %! Move focus to the next window
, ((modMask, xK_k ), windows W.focusUp ) -- %! Move focus to the previous window
, ((modMask, xK_m ), windows W.focusMaster ) -- %! Move focus to the master window
-- modifying the window order
, ((modMask, xK_Return), windows W.swapMaster) -- %! Swap the focused window and the master window
, ((modMask .|. shiftMask, xK_j ), windows W.swapDown ) -- %! Swap the focused window with the next window
, ((modMask .|. shiftMask, xK_k ), windows W.swapUp ) -- %! Swap the focused window with the previous window
-- resizing the master/slave ratio
, ((modMask, xK_h ), sendMessage Shrink) -- %! Shrink the master area
, ((modMask, xK_l ), sendMessage Expand) -- %! Expand the master area
-- floating layer support
, ((modMask, xK_t ), withFocused $ windows . W.sink) -- %! Push window back into tiling
-- 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
-- toggle the status bar gap
, ((modMask , xK_b ), modifyGap (\i n -> let x = (defaultGaps ++ repeat (0,0,0,0)) !! i in if n == x then (0,0,0,0) else x)) -- %! Toggle the status bar gap
-- quit, or restart
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit xmonad
, ((modMask , xK_q ), broadcastMessage ReleaseResources >> restart Nothing True) -- %! Restart xmonad
-- % Extension-provided key bindings
]
++
-- mod-[1..9] %! Switch to workspace N
-- mod-shift-[1..9] %! Move client to workspace N
[((m .|. modMask, k), windows $ f i)
| (i, k) <- zip workspaces [xK_1 ..]
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
++
-- mod-{w,e,r} %! Switch to physical/Xinerama screens 1, 2, or 3
-- mod-shift-{w,e,r} %! Move client to screen 1, 2, or 3
[((m .|. modMask, key), screenWorkspace sc >>= flip whenJust (windows . f))
| (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
-- % Extension-provided key bindings lists
-- | Mouse bindings: default actions bound to mouse events
--
mouseBindings :: M.Map (KeyMask, Button) (Window -> X ())
mouseBindings = M.fromList $
-- mod-button1 %! Set the window to floating mode and move by dragging
[ ((modMask, button1), (\w -> focus w >> mouseMoveWindow w))
-- mod-button2 %! Raise the window to the top of the stack
, ((modMask, button2), (\w -> focus w >> windows W.swapMaster))
-- mod-button3 %! Set the window to floating mode and resize by dragging
, ((modMask, button3), (\w -> focus w >> mouseResizeWindow w))
-- you may also bind events to the mouse scroll wheel (button4 and button5)
-- % Extension-provided mouse bindings
]
-- % Extension-provided definitions

View File

@@ -1,10 +0,0 @@
module Config where
import Graphics.X11.Xlib.Types (Dimension)
import Graphics.X11.Xlib (KeyMask,Window)
import XMonad
borderWidth :: Dimension
numlockMask :: KeyMask
workspaces :: [WorkspaceId]
logHook :: X ()
manageHook :: Window -> String -> String -> String -> X (WindowSet -> WindowSet)
serialisedLayouts :: [Layout Window]

402
INSTALL.md Normal file
View File

@@ -0,0 +1,402 @@
# Install XMonad
On many systems xmonad is available as a binary package in your
distribution (Debian, Ubuntu, Fedora, Arch, Gentoo, …).
It's by far the easiest way to get xmonad, although you'll miss out on the
latest features and fixes that may not have been released yet.
If you do want the latest and greatest, continue reading.
Those who install from distro can skip this and go straight to
[the XMonad Configuration Tutorial](TUTORIAL.md).
<!-- https://github.com/frnmst/md-toc -->
<!-- regenerate via: md_toc -s1 -p github INSTALL.md -->
<!--TOC-->
- [Dependencies](#dependencies)
- [Preparation](#preparation)
- [Download XMonad sources](#download-xmonad-sources)
- [Build XMonad](#build-xmonad)
- [Build using Stack](#build-using-stack)
- [Build using cabal-install](#build-using-cabal-install)
- [Make XMonad your window manager](#make-xmonad-your-window-manager)
- [Custom Build Script](#custom-build-script)
<!--TOC-->
## Dependencies
#### Debian, Ubuntu
``` console
$ sudo apt install \
> git \
> libx11-dev libxft-dev libxinerama-dev libxrandr-dev libxss-dev
```
#### Fedora
``` console
$ sudo dnf install \
> git \
> libX11-devel libXft-devel libXinerama-devel libXrandr-devel libXScrnSaver-devel
```
#### Arch
``` console
$ sudo pacman -S \
> git \
> xorg-server xorg-apps xorg-xinit xorg-xmessage \
> libx11 libxft libxinerama libxrandr libxss \
> pkgconf
```
#### Void
``` console
$ sudo xbps-install \
> git \
> ncurses-libtinfo-libs ncurses-libtinfo-devel \
> libX11-devel libXft-devel libXinerama-devel libXrandr-devel libXScrnSaver-devel \
> pkg-config
```
## Preparation
We'll use the [XDG] directory specifications here, meaning our
configuration will reside within `$XDG_CONFIG_HOME`, which is
`~/.config` on most systems. Let's create this directory and move to
it:
``` console
$ mkdir -p ~/.config/xmonad && cd ~/.config/xmonad
```
If you already have an `xmonad.hs` configuration, you can copy it over
now. If not, you can use the defaults: create a file called `xmonad.hs`
with the following content:
``` haskell
import XMonad
main :: IO ()
main = xmonad def
```
Older versions of xmonad used `~/.xmonad` instead.
This is still supported, but XDG is preferred.
## Download XMonad sources
Still in `~/.config/xmonad`, clone `xmonad` and `xmonad-contrib` repositories
using [git][]:
``` console
$ git clone https://github.com/xmonad/xmonad
$ git clone https://github.com/xmonad/xmonad-contrib
```
This will give you the latest `HEAD`; if you want you can also check
out a tagged release, e.g.:
``` console
$ git clone --branch v0.17.2 https://github.com/xmonad/xmonad
$ git clone --branch v0.17.1 https://github.com/xmonad/xmonad-contrib
```
(Sources and binaries don't usually go into `~/.config`. In our case,
however, it avoids complexities related to Haskell build tools and lets us
focus on the important bits of XMonad installation.)
## Build XMonad
There are two widely used Haskell build tools:
* [Stack][stack]
* [cabal-install][cabal-install]
We include instructions for both.
Unless you already know which one you prefer, use Stack, which is easier.
### Build using Stack
#### Install Stack
Probably one of the best ways to get [stack] is to use [GHCup], which is the main Haskell installer according to language's official [website][GHCup] and community [survey]. GHCup is [widely available] and is considered less error prone than other installation options.
You can also use your system's package
manager:
``` console
$ sudo apt install haskell-stack # Debian, Ubuntu
$ sudo dnf install stack # Fedora
$ sudo pacman -S stack # Arch
```
If you install stack via this method, it is advisable that you run
`stack upgrade` after installation. This will make sure that you are on
the most recent version of the program, regardless of which version your
distribution actually packages.
If your distribution does not package stack, you can also easily install
it via the following command (this is the recommended way to install
stack via its [documentation][stack]):
``` console
$ curl -sSL https://get.haskellstack.org/ | sh
```
#### Create a New Project
Let's create a stack project. Since we're already in the correct
directory (`~/.config/xmonad`) with `xmonad` and `xmonad-contrib`
subdirectories, starting a new stack project is as simple as running `stack
init`.
Stack should now inform you that it will use the relevant `stack` and
`cabal` files from `xmonad` and `xmonad-contrib` to generate its
`stack.yaml` file. At the time of writing, this looks a little bit like
this:
``` console
$ stack init
Looking for .cabal or package.yaml files to use to init the project.
Using cabal packages:
- xmonad-contrib/
- xmonad/
Selecting the best among 19 snapshots...
* Matches https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/9.yaml
Selected resolver: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/9.yaml
Initialising configuration using resolver: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/9.yaml
Total number of user packages considered: 2
Writing configuration to file: stack.yaml
All done.
```
If you look into your current directory now, you should see a freshly
generated `stack.yaml` file:
``` console
$ ls
xmonad xmonad-contrib stack.yaml xmonad.hs
```
The meat of that file (comments start with `#`, we've omitted them here)
will look a little bit like
``` yaml
resolver:
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/9.yaml
packages:
- xmonad
- xmonad-contrib
```
With `stack.yaml` alongside `xmonad.hs`, xmonad now knows that it needs to use
`stack ghc` instead of just `ghc` when (re)compiling its configuration.
If you want to keep xmonad sources and the stack project elsewhere, but still
use `xmonad --recompile`, symlink your real `stack.yaml` into the xmonad
configuration directory, or [use a custom build script](#custom-build-script).
#### Install Everything
Installing things is as easy as typing `stack install`. This will
install the correct version of GHC, as well as build all of the required
packages (`stack build`) and then copy the relevant executables
(`xmonad`, in our case) to `~/.local/bin`. Make sure to add that
directory to your `$PATH`! The command `which xmonad` should now return
that executable. In case it does not, check if you still have xmonad
installed via your package manager and uninstall it.
If you're getting build failures while building the `X11` package it may
be that you don't have the required C libraries installed. See
[above](#dependencies).
### Build using cabal-install
#### Install cabal-install
Probably one of the best ways to get [cabal-install] is to use [GHCup], which is the main Haskell installer according to language's official [website][GHCup] and community [survey]. GHCup is [widely available] and is considered less error prone than other installation options.
You can also use your system's package
manager:
``` console
$ sudo apt install cabal-install # Debian, Ubuntu
$ sudo dnf install cabal-install # Fedora
$ sudo pacman -S cabal-install # Arch
```
See also <https://www.haskell.org/cabal/#install-upgrade>.
#### Create a New Project
If you want to use `xmonad` or `xmonad-contrib` from git, you will need a
`cabal.project` file. If you want to use both from [Hackage][], you should
skip this step.
Create a file named `cabal.project` containing:
```
packages: */*.cabal
```
(If you do this step without using [git] checkouts, you will get an error from
cabal in the next step. Simply remove `cabal.project` and try again.)
#### Install Everything
You'll need to update the cabal package index, build xmonad and xmonad-contrib
libraries and then build the xmonad binary:
``` console
$ cabal update
$ cabal install --package-env=$HOME/.config/xmonad --lib base xmonad xmonad-contrib
$ cabal install --package-env=$HOME/.config/xmonad xmonad
```
This will create a GHC environment in `~/.config/xmonad` so that the libraries
are available for recompilation of the config file, and also install the
xmonad binary to `~/.cabal/bin/xmonad`. Make sure you have that directory in
your `$PATH`!
If you're getting build failures while building the `X11` package it may
be that you don't have the required C libraries installed. See
[above](#dependencies).
## Make XMonad your window manager
This step varies depending on your distribution and X display manager (if
any).
#### Debian, Ubuntu
`/etc/X11/xinit/xinitrc` runs `/etc/X11/Xsession` which runs `~/.xsession`, so
you probably want to put `exec xmonad` there (don't forget the shebang and chmod).
(Tested with `startx`, `xdm`, `lightdm`.)
By using `~/.xsession`, the distro takes care of stuff like dbus, ssh-agent, X
resources, etc. If you want a completely manual X session, use `~/.xinitrc`
instead. Or invoke `startx`/`xinit` with an explicit path.
Some newer display managers require an entry in `/usr/share/xsessions`.
To use your custom `~/.xsession`, put these lines to
`/usr/share/xsessions/default.desktop`:
```
[Desktop Entry]
Name=Default X session
Type=Application
Exec=default
```
(Tested with `sddm`.)
#### Fedora
`/etc/X11/xinit/xinitrc` runs `~/.Xclients`, so you probably want to put `exec
xmonad` there (don't forget the shebang and chmod). Like in Debian, this can
be overridden by having a completely custom `~/.xinitrc` or passing arguments
to `startx`/`xinit`.
X display managers (e.g. `lightdm`) usually invoke `/etc/X11/xinit/Xsession`
instead, which additionally redirects output to `~/.xsession-errors` and also
tries `~/.xsession` before `~/.Xclients`.
Newer display managers require an entry in `/usr/share/xsessions`, which is
available in the `xorg-x11-xinit-session` package.
#### Arch
`/etc/X11/xinit/xinitrc` runs `twm`, `xclock` and 3 `xterm`s; users are
meant to just copy that to `~/.xinitrc` and
[customize](https://wiki.archlinux.org/title/Xinit#xinitrc) it: replace the
last few lines with `exec xmonad`.
Display managers like `lightdm` have their own `Xsession` script which invokes
`~/.xsession`. Other display managers need an entry in
`/usr/share/xsessions`, <https://aur.archlinux.org/packages/xinit-xsession/>
provides one.
#### See also
* <https://xmonad.org/documentation.html#in-your-environment>
* [FAQ: How can I use xmonad with a display manager? (xdm, kdm, gdm)](https://wiki.haskell.org/Xmonad/Frequently_asked_questions#How_can_I_use_xmonad_with_a_display_manager.3F_.28xdm.2C_kdm.2C_gdm.29)
## Custom Build Script
If you need to customize what happens during `xmonad --recompile` (bound to
`M-q` by default), perhaps because your xmonad configuration is a whole
separate Haskell package, you need to create a so-called `build` file. This
is quite literally just a shell script called `build` in your xmonad directory
(which is `~/.config/xmonad` for us) that tells xmonad how it should build its
executable.
A good starting point (this is essentially [what xmonad would do][]
without a build file, with the exception that we are invoking `stack
ghc` instead of plain `ghc`) would be
``` shell
#!/bin/sh
exec stack ghc -- \
--make xmonad.hs \
-i \
-ilib \
-fforce-recomp \
-main-is main \
-v0 \
-o "$1"
```
Don't forget to mark the file as `+x`: `chmod +x build`!
Some example build scripts for `stack` and `cabal` are provided in the
`xmonad-contrib` distribution. You can see those online in the
[scripts/build][] directory. You might wish to use these if you have
special dependencies for your `xmonad.hs`, especially with cabal as
you must use a cabal file and often a `cabal.project` to specify them;
`cabal install --lib` above generally isn't enough, and when it is
it can be difficult to keep track of when you want to replicate your
configuration on another system.
#### Don't Recompile on Every Startup
By default, xmonad always recompiles itself when a build script is used
(because the build script could contain arbitrary code, so a simple
check whether the `xmonad.hs` file changed is not enough). If you find
that too annoying, then you can use the `xmonad-ARCH` executable that
`xmonad --recompile` generates instead of `xmonad` in your startup. For
example, instead of writing
``` shell
exec xmonad
```
in your `~/.xinitrc`, you would write
``` shell
exec $HOME/.cache/xmonad/xmonad-x86_64-linux
```
The `~/.cache` prefix is the `$XDG_CACHE_HOME` directory. Note that
if your xmonad configuration resides within `~/.xmonad`, then the
executable will also be within that directory and not in
`$XDG_CACHE_HOME`.
[XDG]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
[git]: https://git-scm.com/
[stack]: https://docs.haskellstack.org/en/stable/README/
[cabal-install]: https://www.haskell.org/cabal/
[GHCup]: https://www.haskell.org/ghcup/
[survey]: https://taylor.fausak.me/2022/11/18/haskell-survey-results/
[widely available]: https://www.haskell.org/ghcup/install/#supported-platforms
[what xmonad would do]: https://github.com/xmonad/xmonad/blob/master/src/XMonad/Core.hs#L659-L667
[Hackage]: https://hackage.haskell.org/
[scripts/build]: https://github.com/xmonad/xmonad-contrib/blob/master/scripts/build

47
LICENSE
View File

@@ -1,27 +1,28 @@
Copyright (c) Spencer Janssen
Copyright (c) 2007,2008 Spencer Janssen
Copyright (c) 2007,2008 Don Stewart
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 AUTHORS ``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.

146
MAINTAINERS.md Normal file
View File

@@ -0,0 +1,146 @@
# XMonad Maintainers
## The XMonad Core Team
* Brandon S Allbery [GitHub][geekosaur], IRC: `geekosaur`, [GPG][gpg:geekosaur]
* Brent Yorgey [GitHub][byorgey], IRC: `byorgey`
* Daniel Wagner [GitHub][dmwit], [Twitter][twitter:dmwit], IRC: `dmwit`
* Sibi Prabakaran [GitHub][psibi], [Twitter][twitter:psibi], IRC: `sibi`
* Tomáš Janoušek [GitHub][liskin], [Twitter][twitter:liskin], IRC: `liskin`, [GPG][gpg:liskin]
* Tony Zorman [GitHub][slotThe], IRC: `Solid`, [GPG][gpg:slotThe]
[geekosaur]: https://github.com/geekosaur
[byorgey]: https://github.com/byorgey
[dmwit]: https://github.com/dmwit
[psibi]: https://github.com/psibi
[liskin]: https://github.com/liskin
[slotThe]: https://github.com/slotThe
[gpg:geekosaur]: https://github.com/geekosaur.gpg
[gpg:liskin]: https://github.com/liskin.gpg
[gpg:slotThe]: https://github.com/slotThe.gpg
[twitter:dmwit]: https://twitter.com/dmwit13
[twitter:psibi]: https://twitter.com/psibi
[twitter:liskin]: https://twitter.com/Liskni_si
## Hall of Fame (past maintainers/developers)
* Adam Vogt [GitHub](https://github.com/aavogt)
* Peter Simons [GitHub](https://github.com/peti), [Twitter](https://twitter.com/OriginalPeti)
* Spencer Janssen [GitHub](https://github.com/spencerjanssen)
* Don Stewart [GitHub](https://github.com/donsbot), [Twitter](https://twitter.com/donsbot)
* Jason Creighton [GitHub](https://github.com/JasonCreighton)
* David Roundy [GitHub](https://github.com/droundy)
* Daniel Schoepe [GitHub](https://github.com/dschoepe)
* Eric Mertens [GitHub](https://github.com/glguy)
* Nicolas Pouillard [GitHub](https://github.com/np)
* Roman Cheplyaka [GitHub](https://github.com/UnkindPartition)
* Gwern Branwen [GitHub](https://github.com/gwern)
* Lukas Mai [GitHub](https://github.com/mauke)
* Braden Shepherdson [GitHub](https://github.com/shepheb)
* Devin Mullins [GitHub](https://github.com/twifkak)
* David Lazar [GitHub](https://github.com/davidlazar)
* Peter J. Jones [GitHub](https://github.com/pjones)
## Release Procedures
When the time comes to release another version of xmonad and xmonad-contrib:
1. Update the version number in all the `*.cabal` files and let the CI
verify that it all builds together.
2. Review documentation files and make sure they are accurate:
- [`README.md`](README.md)
- [`CHANGES.md`](CHANGES.md) (bump version, set date)
- [`INSTALL.md`](INSTALL.md)
- [`man/xmonad.1.markdown.in`](man/xmonad.1.markdown.in)
- [haddocks](https://xmonad.github.io/xmonad-docs/)
If the manpage changes, wait for the CI to rebuild the rendered outputs.
3. Update the website:
- Draft a [new release announcement][web-announce].
- Check install instructions, guided tour, keybindings cheat sheet, …
4. Make sure that `tested-with:` covers several recent releases of GHC, that
`.github/workflows/haskell-ci.yml` had been updated to test all these GHC
versions and that `.github/workflows/stack.yml` tests with several recent
revisions of [Stackage][] LTS.
5. Trigger the Haskell-CI workflow and fill in the candidate version number.
This will upload a release candidate to Hackage.
- https://github.com/xmonad/xmonad/actions/workflows/haskell-ci.yml
- https://github.com/xmonad/xmonad-contrib/actions/workflows/haskell-ci.yml
Check that everything looks good. If not, push fixes and do another
candidate. When everything's ready, create a release on GitHub:
- https://github.com/xmonad/xmonad/releases/new
- https://github.com/xmonad/xmonad-contrib/releases/new
CI will automatically upload the final release to Hackage.
See [haskell-ci-hackage.patch][] for details about the Hackage automation.
6. Post announcement to:
- [xmonad.org website](https://github.com/xmonad/xmonad-web/tree/gh-pages/news/_posts)
- [XMonad mailing list](https://mail.haskell.org/mailman/listinfo/xmonad)
- [Haskell Cafe](https://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe)
- [Haskell Discourse](https://discourse.haskell.org/)
- [Twitter](https://twitter.com/xmonad)
- [Reddit](https://www.reddit.com/r/xmonad/)
See [old announcements][old-announce] ([even older][older-announce]) for inspiration.
7. Trigger xmonad-docs build to generate and persist docs for the just
released version:
- https://github.com/xmonad/xmonad-docs/actions/workflows/stack.yml
8. Bump version for development (add `.9`) and prepare fresh sections in
[`CHANGES.md`](CHANGES.md).
[packdeps]: https://hackage.haskell.org/package/packdeps
[Stackage]: https://www.stackage.org/
[haskell-ci-hackage.patch]: .github/workflows/haskell-ci-hackage.patch
[web-announce]: https://github.com/xmonad/xmonad-web/tree/gh-pages/news/_posts
[old-announce]: https://github.com/xmonad/xmonad-web/blob/gh-pages/news/_posts/2021-10-27-xmonad-0-17-0.md
[older-announce]: https://github.com/xmonad/xmonad-web/tree/55614349421ebafaef4a47424fcb16efa80ff768
## Website and Other Accounts
* The [xmonad twitter] is tended to by [liskin].
* The [xmonad.org] domain is owned by [eyenx] and the website itself is
deployed via GitHub Pages. It can be updated by making a pull request
against the [xmonad-web] repository.
[eyenx]: https://github.com/eyenx
[xmonad-web]: https://github.com/xmonad/xmonad-web/
[xmonad.org]: https://xmonad.org/
[xmonad twitter]: https://twitter.com/xmonad

248
Main.hs
View File

@@ -1,6 +1,6 @@
----------------------------------------------------------------------------
-- |
-- Module : Main.hs
-- Module : Main
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
@@ -12,251 +12,9 @@
--
-----------------------------------------------------------------------------
module Main where
import Data.Bits
import qualified Data.Map as M
import qualified Data.Set as S
import Control.Monad.Reader
import Control.Monad.State
import Data.Maybe (fromMaybe)
import System.Environment (getArgs)
import Graphics.X11.Xlib hiding (refreshKeyboardMapping)
import Graphics.X11.Xlib.Extras
import Graphics.X11.Xinerama (getScreenInfo)
module Main (main) where
import XMonad
import Config
import StackSet (new, floating, member)
import qualified StackSet as W
import Operations
import System.IO
-- |
-- The main entry point
--
main :: IO ()
main = do
dpy <- openDisplay ""
let dflt = defaultScreen dpy
rootw <- rootWindow dpy dflt
xinesc <- getScreenInfo dpy
nbc <- initColor dpy normalBorderColor
fbc <- initColor dpy focusedBorderColor
hSetBuffering stdout NoBuffering
args <- getArgs
let initialWinset = new layoutHook workspaces $ zipWith SD xinesc gaps
maybeRead s = case reads s of
[(x, "")] -> Just x
_ -> Nothing
winset = fromMaybe initialWinset $ do
("--resume" : s : _) <- return args
ws <- maybeRead s
return . W.ensureTags layoutHook workspaces
$ W.mapLayout (fromMaybe layoutHook . maybeRead) ws
gaps = take (length xinesc) $ defaultGaps ++ repeat (0,0,0,0)
cf = XConf
{ display = dpy
, theRoot = rootw
, normalBorder = nbc
, focusedBorder = fbc }
st = XState
{ windowset = initialWinset
, mapped = S.empty
, waitingUnmap = M.empty
, dragging = Nothing }
xSetErrorHandler -- in C, I'm too lazy to write the binding: dons
-- setup initial X environment
sync dpy False
selectInput dpy rootw $ substructureRedirectMask .|. substructureNotifyMask
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
allocaXEvent $ \e ->
runX cf st $ do
grabKeys
grabButtons
io $ sync dpy False
-- bootstrap the windowset, Operations.windows will identify all
-- the windows in winset as new and set initial properties for
-- those windows
windows (const winset)
-- scan for all top-level windows, add the unmanaged ones to the
-- windowset
ws <- io $ scan dpy rootw
mapM_ manage ws
-- main loop, for all you HOF/recursion fans out there.
forever_ $ handle =<< io (nextEvent dpy e >> getEvent e)
return ()
where forever_ a = a >> forever_ a
-- ---------------------------------------------------------------------
-- IO stuff. Doesn't require any X state
-- Most of these things run only on startup (bar grabkeys)
-- | scan for any new windows to manage. If they're already managed,
-- this should be idempotent.
scan :: Display -> Window -> IO [Window]
scan dpy rootw = do
(_, _, ws) <- queryTree dpy rootw
filterM ok ws
-- TODO: scan for windows that are either 'IsViewable' or where WM_STATE ==
-- Iconic
where ok w = do wa <- getWindowAttributes dpy w
a <- internAtom dpy "WM_STATE" False
p <- getWindowProperty32 dpy a w
let ic = case p of
Just (3:_) -> True -- 3 for iconified
_ -> False
return $ not (wa_override_redirect wa)
&& (wa_map_state wa == waIsViewable || ic)
-- | Grab the keys back
grabKeys :: X ()
grabKeys = do
XConf { display = dpy, theRoot = rootw } <- ask
let grab kc m = io $ grabKey dpy kc m rootw True grabModeAsync grabModeAsync
io $ ungrabKey dpy anyKey anyModifier rootw
forM_ (M.keys keys) $ \(mask,sym) -> do
kc <- io $ keysymToKeycode dpy sym
-- "If the specified KeySym is not defined for any KeyCode,
-- XKeysymToKeycode() returns zero."
when (kc /= '\0') $ mapM_ (grab kc . (mask .|.)) extraModifiers
-- | XXX comment me
grabButtons :: X ()
grabButtons = do
XConf { display = dpy, theRoot = rootw } <- ask
let grab button mask = io $ grabButton dpy button mask rootw False buttonPressMask
grabModeAsync grabModeSync none none
io $ ungrabButton dpy anyButton anyModifier rootw
mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) extraModifiers) (M.keys mouseBindings)
-- ---------------------------------------------------------------------
-- | Event handler. Map X events onto calls into Operations.hs, which
-- modify our internal model of the window manager state.
--
-- Events dwm handles that we don't:
--
-- [ButtonPress] = buttonpress,
-- [Expose] = expose,
-- [PropertyNotify] = propertynotify,
--
handle :: Event -> X ()
-- run window manager command
handle (KeyEvent {ev_event_type = t, ev_state = m, ev_keycode = code})
| t == keyPress = withDisplay $ \dpy -> do
s <- io $ keycodeToKeysym dpy code 0
userCode $ whenJust (M.lookup (cleanMask m,s) keys) id
-- manage a new window
handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
wa <- io $ getWindowAttributes dpy w -- ignore override windows
-- need to ignore mapping requests by managed windows not on the current workspace
managed <- isClient w
when (not (wa_override_redirect wa) && not managed) $ do manage w
-- window destroyed, unmanage it
-- window gone, unmanage it
handle (DestroyWindowEvent {ev_window = w}) = whenX (isClient w) $ unmanage w
-- We track expected unmap events in waitingUnmap. We ignore this event unless
-- it is synthetic or we are not expecting an unmap notification from a window.
handle (UnmapEvent {ev_window = w, ev_send_event = synthetic}) = whenX (isClient w) $ do
e <- gets (fromMaybe 0 . M.lookup w . waitingUnmap)
if (synthetic || e == 0)
then unmanage w
else modify (\s -> s { waitingUnmap = M.adjust pred w (waitingUnmap s) })
-- set keyboard mapping
handle e@(MappingNotifyEvent {}) = do
io $ refreshKeyboardMapping e
when (ev_request e == mappingKeyboard) grabKeys
-- handle button release, which may finish dragging.
handle e@(ButtonEvent {ev_event_type = t})
| t == buttonRelease = do
drag <- gets dragging
case drag of
-- we're done dragging and have released the mouse:
Just (_,f) -> modify (\s -> s { dragging = Nothing }) >> f
Nothing -> broadcastMessage e
-- handle motionNotify event, which may mean we are dragging.
handle e@(MotionEvent {ev_event_type = _t, ev_x = x, ev_y = y}) = do
drag <- gets dragging
case drag of
Just (d,_) -> d (fromIntegral x) (fromIntegral y) -- we're dragging
Nothing -> broadcastMessage e
-- click on an unfocused window, makes it focused on this workspace
handle e@(ButtonEvent {ev_window = w,ev_event_type = t,ev_button = b })
| t == buttonPress = do
-- If it's the root window, then it's something we
-- grabbed in grabButtons. Otherwise, it's click-to-focus.
isr <- isRoot w
if isr then userCode $ whenJust (M.lookup (cleanMask (ev_state e), b) mouseBindings) ($ ev_subwindow e)
else focus w
sendMessage e -- Always send button events.
-- entered a normal window, makes this focused.
handle e@(CrossingEvent {ev_window = w, ev_event_type = t})
| t == enterNotify && ev_mode e == notifyNormal
&& ev_detail e /= notifyInferior = focus w
-- left a window, check if we need to focus root
handle e@(CrossingEvent {ev_event_type = t})
| t == leaveNotify
= do rootw <- asks theRoot
when (ev_window e == rootw && not (ev_same_screen e)) $ setFocusX rootw
-- configure a window
handle e@(ConfigureRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
ws <- gets windowset
wa <- io $ getWindowAttributes dpy w
if M.member w (floating ws)
|| not (member w ws)
then do io $ configureWindow dpy w (ev_value_mask e) $ WindowChanges
{ wc_x = ev_x e
, wc_y = ev_y e
, wc_width = ev_width e
, wc_height = ev_height e
, wc_border_width = fromIntegral borderWidth
, wc_sibling = ev_above e
, wc_stack_mode = ev_detail e }
when (member w ws) (float w)
else io $ allocaXEvent $ \ev -> do
setEventType ev configureNotify
setConfigureEvent ev w w
(wa_x wa) (wa_y wa) (wa_width wa)
(wa_height wa) (ev_border_width e) none (wa_override_redirect wa)
sendEvent dpy w False 0 ev
io $ sync dpy False
-- configuration changes in the root may mean display settings have changed
handle (ConfigureEvent {ev_window = w}) = whenX (isRoot w) rescreen
-- property notify
handle PropertyEvent { ev_event_type = t, ev_atom = a }
| t == propertyNotify && a == wM_NAME = userCode logHook
handle e = broadcastMessage e -- trace (eventName e) -- ignoring
main = xmonad def

View File

@@ -1,656 +0,0 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
-- --------------------------------------------------------------------------
-- |
-- Module : Operations.hs
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : dons@cse.unsw.edu.au
-- Stability : unstable
-- Portability : not portable, Typeable deriving, mtl, posix
--
-- Operations.
--
-----------------------------------------------------------------------------
module Operations where
import XMonad
import qualified StackSet as W
import {-# SOURCE #-} Config (borderWidth,logHook,manageHook,numlockMask,serialisedLayouts)
import Data.Maybe
import Data.List (nub, (\\), find, partition)
import Data.Bits ((.|.), (.&.), complement)
import Data.Ratio
import qualified Data.Map as M
import qualified Data.Set as S
import Control.Monad.State
import Control.Monad.Reader
import Control.Arrow ((***), second)
import System.IO
import Graphics.X11.Xlib
import Graphics.X11.Xinerama (getScreenInfo)
import Graphics.X11.Xlib.Extras
-- ---------------------------------------------------------------------
-- |
-- Window manager operations
-- manage. Add a new window to be managed in the current workspace.
-- Bring it into focus.
--
-- Whether the window is already managed, or not, it is mapped, has its
-- border set, and its event mask set.
--
manage :: Window -> X ()
manage w = whenX (fmap not $ isClient w) $ withDisplay $ \d -> do
sh <- io $ getWMNormalHints d w
let isFixedSize = sh_min_size sh /= Nothing && sh_min_size sh == sh_max_size sh
isTransient <- isJust `liftM` io (getTransientForHint d w)
(sc, rr) <- floatLocation w
-- ensure that float windows don't go over the edge of the screen
let adjust (W.RationalRect x y wid h) | x + wid > 1 || y + h > 1 || x < 0 || y < 0
= W.RationalRect (0.5 - wid/2) (0.5 - h/2) wid h
adjust r = r
f ws | isFixedSize || isTransient = W.float w (adjust rr) . W.insertUp w . W.view i $ ws
| otherwise = W.insertUp w ws
where i = fromMaybe (W.tag . W.workspace . W.current $ ws) $ W.lookupWorkspace sc ws
n <- fmap (fromMaybe "") $ io $ fetchName d w
(ClassHint rn rc) <- io $ getClassHint d w
g <- manageHook w n rn rc `catchX` return id
windows (g . f)
-- | unmanage. A window no longer exists, remove it from the window
-- list, on whatever workspace it is.
--
-- should also unmap?
--
unmanage :: Window -> X ()
unmanage w = do
windows (W.delete w)
setWMState w 0 {-withdrawn-}
modify (\s -> s {mapped = S.delete w (mapped s), waitingUnmap = M.delete w (waitingUnmap s)})
-- | Modify the size of the status gap at the top of the current screen
-- Taking a function giving the current screen, and current geometry.
modifyGap :: (Int -> (Int,Int,Int,Int) -> (Int,Int,Int,Int)) -> X ()
modifyGap f = do
windows $ \ws@(W.StackSet { W.current = c@(W.Screen { W.screenDetail = sd }) }) ->
let n = fromIntegral . W.screen $ c
g = f n . statusGap $ sd
in ws { W.current = c { W.screenDetail = sd { statusGap = g } } }
-- | Kill the currently focused client. If we do kill it, we'll get a
-- delete notify back from X.
--
-- There are two ways to delete a window. Either just kill it, or if it
-- supports the delete protocol, send a delete event (e.g. firefox)
--
kill :: X ()
kill = withDisplay $ \d -> withFocused $ \w -> do
wmdelt <- atom_WM_DELETE_WINDOW ; wmprot <- atom_WM_PROTOCOLS
protocols <- io $ getWMProtocols d w
io $ if wmdelt `elem` protocols
then allocaXEvent $ \ev -> do
setEventType ev clientMessage
setClientMessageEvent ev w wmprot 32 wmdelt 0
sendEvent d w False noEventMask ev
else killClient d w >> return ()
-- ---------------------------------------------------------------------
-- Managing windows
data LayoutMessages = Hide | ReleaseResources deriving ( Typeable, Eq )
instance Message LayoutMessages
-- | windows. Modify the current window list with a pure function, and refresh
windows :: (WindowSet -> WindowSet) -> X ()
windows f = do
XState { windowset = old } <- get
let oldvisible = concatMap (W.integrate' . W.stack . W.workspace) $ W.current old : W.visible old
ws = f old
XConf { display = d , normalBorder = nbc, focusedBorder = fbc } <- ask
mapM_ setInitialProperties (W.allWindows ws \\ W.allWindows old)
whenJust (W.peek old) $ \otherw -> io $ setWindowBorder d otherw nbc
modify (\s -> s { windowset = ws })
-- notify non visibility
let tags_oldvisible = map (W.tag . W.workspace) $ W.current old : W.visible old
gottenhidden = filter (`elem` tags_oldvisible) $ map W.tag $ W.hidden ws
sendMessageToWorkspaces Hide gottenhidden
-- for each workspace, layout the currently visible workspaces
let allscreens = W.screens ws
summed_visible = scanl (++) [] $ map (W.integrate' . W.stack . W.workspace) allscreens
visible <- fmap concat $ forM (zip allscreens summed_visible) $ \ (w, vis) -> do
let n = W.tag (W.workspace w)
this = W.view n ws
l = W.layout (W.workspace w)
flt = filter (flip M.member (W.floating ws)) (W.index this)
tiled = (W.stack . W.workspace . W.current $ this)
>>= W.filter (not . flip M.member (W.floating ws))
>>= W.filter (not . (`elem` vis))
(SD (Rectangle sx sy sw sh)
(gt,gb,gl,gr)) = W.screenDetail w
viewrect = Rectangle (sx + fromIntegral gl) (sy + fromIntegral gt)
(sw - fromIntegral (gl + gr)) (sh - fromIntegral (gt + gb))
-- just the tiled windows:
-- now tile the windows on this workspace, modified by the gap
(rs, ml') <- runLayout l viewrect tiled `catchX` runLayout (Layout Full) viewrect tiled
mapM_ (uncurry tileWindow) rs
whenJust ml' $ \l' -> runOnWorkspaces (\ww -> if W.tag ww == n
then return $ ww { W.layout = l'}
else return ww)
-- now the floating windows:
-- move/resize the floating windows, if there are any
forM_ flt $ \fw -> whenJust (M.lookup fw (W.floating ws)) $
\(W.RationalRect rx ry rw rh) -> do
tileWindow fw $ Rectangle
(sx + floor (toRational sw*rx)) (sy + floor (toRational sh*ry))
(floor (toRational sw*rw)) (floor (toRational sh*rh))
let vs = flt ++ map fst rs
io $ restackWindows d vs
-- return the visible windows for this workspace:
return vs
whenJust (W.peek ws) $ \w -> io $ setWindowBorder d w fbc
setTopFocus
userCode logHook
-- io performGC -- really helps, but seems to trigger GC bugs?
-- hide every window that was potentially visible before, but is not
-- given a position by a layout now.
mapM_ hide (nub oldvisible \\ visible)
clearEvents enterWindowMask
-- | setWMState. set the WM_STATE property
setWMState :: Window -> Int -> X ()
setWMState w v = withDisplay $ \dpy -> do
a <- atom_WM_STATE
io $ changeProperty32 dpy w a a propModeReplace [fromIntegral v, fromIntegral none]
-- | hide. Hide a window by unmapping it, and setting Iconified.
hide :: Window -> X ()
hide w = whenX (gets (S.member w . mapped)) $ withDisplay $ \d -> do
io $ do selectInput d w (clientMask .&. complement structureNotifyMask)
unmapWindow d w
selectInput d w clientMask
setWMState w 3 --iconic
-- this part is key: we increment the waitingUnmap counter to distinguish
-- between client and xmonad initiated unmaps.
modify (\s -> s { waitingUnmap = M.insertWith (+) w 1 (waitingUnmap s)
, mapped = S.delete w (mapped s) })
-- | reveal. Show a window by mapping it and setting Normal
-- this is harmless if the window was already visible
reveal :: Window -> X ()
reveal w = withDisplay $ \d -> do
setWMState w 1 --normal
io $ mapWindow d w
modify (\s -> s { mapped = S.insert w (mapped s) })
-- | The client events that xmonad is interested in
clientMask :: EventMask
clientMask = structureNotifyMask .|. enterWindowMask .|. propertyChangeMask
-- | Set some properties when we initially gain control of a window
setInitialProperties :: Window -> X ()
setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> io $ do
selectInput d w $ clientMask
setWindowBorderWidth d w borderWidth
-- we must initially set the color of new windows, to maintain invariants
-- required by the border setting in 'windows'
setWindowBorder d w nb
-- | refresh. Render the currently visible workspaces, as determined by
-- the StackSet. Also, set focus to the focused window.
--
-- This is our 'view' operation (MVC), in that it pretty prints our model
-- with X calls.
--
refresh :: X ()
refresh = windows id
-- | clearEvents. Remove all events of a given type from the event queue.
clearEvents :: EventMask -> X ()
clearEvents mask = withDisplay $ \d -> io $ do
sync d False
allocaXEvent $ \p -> fix $ \again -> do
more <- checkMaskEvent d mask p
when more again -- beautiful
-- | tileWindow. Moves and resizes w such that it fits inside the given
-- rectangle, including its border.
tileWindow :: Window -> Rectangle -> X ()
tileWindow w r = withDisplay $ \d -> do
bw <- (fromIntegral . wa_border_width) `liftM` io (getWindowAttributes d w)
-- give all windows at least 1x1 pixels
let least x | x <= bw*2 = 1
| otherwise = x - bw*2
io $ moveResizeWindow d w (rect_x r) (rect_y r)
(least $ rect_width r) (least $ rect_height r)
reveal w
-- ---------------------------------------------------------------------
-- | rescreen. The screen configuration may have changed (due to
-- xrandr), update the state and refresh the screen, and reset the gap.
rescreen :: X ()
rescreen = do
xinesc <- withDisplay (io . getScreenInfo)
windows $ \ws@(W.StackSet { W.current = v, W.visible = vs, W.hidden = hs }) ->
let (xs, ys) = splitAt (length xinesc) $ map W.workspace (v:vs) ++ hs
(a:as) = zipWith3 W.Screen xs [0..] $ zipWith SD xinesc gs
sgs = map (statusGap . W.screenDetail) (v:vs)
gs = take (length xinesc) (sgs ++ repeat (0,0,0,0))
in ws { W.current = a
, W.visible = as
, W.hidden = ys }
-- ---------------------------------------------------------------------
-- | setButtonGrab. Tell whether or not to intercept clicks on a given window
setButtonGrab :: Bool -> Window -> X ()
setButtonGrab grab w = withDisplay $ \d -> io $
if grab
then forM_ [button1, button2, button3] $ \b ->
grabButton d b anyModifier w False buttonPressMask
grabModeAsync grabModeSync none none
else ungrabButton d anyButton anyModifier w
-- ---------------------------------------------------------------------
-- Setting keyboard focus
-- | Set the focus to the window on top of the stack, or root
setTopFocus :: X ()
setTopFocus = withWindowSet $ maybe (setFocusX =<< asks theRoot) setFocusX . W.peek
-- | Set focus explicitly to window 'w' if it is managed by us, or root.
-- This happens if X notices we've moved the mouse (and perhaps moved
-- the mouse to a new screen).
focus :: Window -> X ()
focus w = withWindowSet $ \s -> do
if W.member w s then when (W.peek s /= Just w) $ windows (W.focusWindow w)
else whenX (isRoot w) $ setFocusX w
-- | Call X to set the keyboard focus details.
setFocusX :: Window -> X ()
setFocusX w = withWindowSet $ \ws -> do
dpy <- asks display
-- clear mouse button grab and border on other windows
forM_ (W.current ws : W.visible ws) $ \wk -> do
forM_ (W.index (W.view (W.tag (W.workspace wk)) ws)) $ \otherw -> do
setButtonGrab True otherw
-- If we ungrab buttons on the root window, we lose our mouse bindings.
whenX (not `liftM` isRoot w) $ setButtonGrab False w
io $ do setInputFocus dpy w revertToPointerRoot 0
-- raiseWindow dpy w
------------------------------------------------------------------------
-- Message handling
-- | Throw a message to the current LayoutClass possibly modifying how we
-- layout the windows, then refresh.
sendMessage :: Message a => a -> X ()
sendMessage a = do
w <- (W.workspace . W.current) `fmap` gets windowset
ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
whenJust ml' $ \l' -> do
windows $ \ws -> ws { W.current = (W.current ws)
{ W.workspace = (W.workspace $ W.current ws)
{ W.layout = l' }}}
-- | Send a message to a list of workspaces' layouts, without necessarily refreshing.
sendMessageToWorkspaces :: Message a => a -> [WorkspaceId] -> X ()
sendMessageToWorkspaces a l = runOnWorkspaces $ \w ->
if W.tag w `elem` l
then do ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
return $ w { W.layout = maybe (W.layout w) id ml' }
else return w
-- | Send a message to all visible layouts, without necessarily refreshing.
-- This is how we implement the hooks, such as UnDoLayout.
broadcastMessage :: Message a => a -> X ()
broadcastMessage a = runOnWorkspaces $ \w -> do
ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
return $ w { W.layout = maybe (W.layout w) id ml' }
-- | This is basically a map function, running a function in the X monad on
-- each workspace with the output of that function being the modified workspace.
runOnWorkspaces :: (WindowSpace -> X WindowSpace) -> X ()
runOnWorkspaces job =do
ws <- gets windowset
h <- mapM job $ W.hidden ws
c:v <- mapM (\s -> fmap (\w -> s { W.workspace = w}) $ job (W.workspace s))
$ W.current ws : W.visible ws
modify $ \s -> s { windowset = ws { W.current = c, W.visible = v, W.hidden = h } }
-- | Set the layout of the currently viewed workspace
setLayout :: Layout Window -> X ()
setLayout l = do
ss@(W.StackSet { W.current = c@(W.Screen { W.workspace = ws })}) <- gets windowset
handleMessage (W.layout ws) (SomeMessage ReleaseResources)
windows $ const $ ss {W.current = c { W.workspace = ws { W.layout = l } } }
-- | X Events are valid Messages
instance Message Event
------------------------------------------------------------------------
-- LayoutClass selection manager
-- | A layout that allows users to switch between various layout options.
-- This layout accepts three Messages:
--
-- > NextLayout
-- > PrevLayout
-- > JumpToLayout.
--
data ChangeLayout = NextLayout | PrevLayout | JumpToLayout String
deriving (Eq, Show, Typeable)
instance Message ChangeLayout
instance ReadableLayout Window where
readTypes = Layout (Select []) :
Layout Full : Layout (Tall 1 0.1 0.5) :
Layout (Mirror $ Tall 1 0.1 0.5) :
serialisedLayouts
data Select a = Select [Layout a] deriving (Show, Read)
instance ReadableLayout a => LayoutClass Select a where
doLayout (Select (l:ls)) r s =
second (fmap (Select . (:ls))) `fmap` doLayout l r s
doLayout (Select []) r s =
second (const Nothing) `fmap` doLayout Full r s
-- respond to messages only when there's an actual choice:
handleMessage (Select (l:ls@(_:_))) m
| Just NextLayout <- fromMessage m = switchl rls
| Just PrevLayout <- fromMessage m = switchl rls'
| Just (JumpToLayout x) <- fromMessage m = switchl (j x)
| Just ReleaseResources <- fromMessage m = do -- each branch has a different type
mlls' <- mapM (flip handleMessage m) (l:ls)
let lls' = zipWith (flip maybe id) (l:ls) mlls'
return (Just (Select lls'))
where rls [] = []
rls (x:xs) = xs ++ [x]
rls' = reverse . rls . reverse
j s zs = case partition ((s ==) . description) zs of (xs,ys) -> xs++ys
switchl f = do ml' <- handleMessage l (SomeMessage Hide)
return $ Just (Select $ f $ fromMaybe l ml':ls)
-- otherwise, or if we don't understand the message, pass it along to the real layout:
handleMessage (Select (l:ls)) m =
fmap (Select . (:ls)) `fmap` handleMessage l m
-- Unless there is no layout...
handleMessage (Select []) _ = return Nothing
description (Select (x:_)) = description x
description _ = "default"
--
-- | Builtin layout algorithms:
--
-- > fullscreen mode
-- > tall mode
--
-- The latter algorithms support the following operations:
--
-- > Shrink
-- > Expand
--
data Resize = Shrink | Expand deriving Typeable
-- | You can also increase the number of clients in the master pane
data IncMasterN = IncMasterN Int deriving Typeable
instance Message Resize
instance Message IncMasterN
-- | Simple fullscreen mode, just render all windows fullscreen.
data Full a = Full deriving (Show, Read)
instance LayoutClass Full a
-- | The inbuilt tiling mode of xmonad, and its operations.
data Tall a = Tall Int Rational Rational deriving (Show, Read)
instance LayoutClass Tall a where
doLayout (Tall nmaster _ frac) r =
return . (flip (,) Nothing) .
ap zip (tile frac r nmaster . length) . W.integrate
pureMessage (Tall nmaster delta frac) m = msum [fmap resize (fromMessage m)
,fmap incmastern (fromMessage m)]
where resize Shrink = Tall nmaster delta (max 0 $ frac-delta)
resize Expand = Tall nmaster delta (min 1 $ frac+delta)
incmastern (IncMasterN d) = Tall (max 0 (nmaster+d)) delta frac
description _ = "Tall"
-- | Mirror a rectangle
mirrorRect :: Rectangle -> Rectangle
mirrorRect (Rectangle rx ry rw rh) = (Rectangle ry rx rh rw)
-- | Mirror a layout, compute its 90 degree rotated form.
data Mirror l a = Mirror (l a) deriving (Show, Read)
instance LayoutClass l a => LayoutClass (Mirror l) a where
doLayout (Mirror l) r s = (map (second mirrorRect) *** fmap Mirror)
`fmap` doLayout l (mirrorRect r) s
handleMessage (Mirror l) = fmap (fmap Mirror) . handleMessage l
description (Mirror l) = "Mirror "++ description l
-- | tile. Compute the positions for windows using the default 2 pane tiling algorithm.
--
-- The screen is divided (currently) into two panes. all clients are
-- then partioned between these two panes. one pane, the `master', by
-- convention has the least number of windows in it (by default, 1).
-- the variable `nmaster' controls how many windows are rendered in the
-- master pane.
--
-- `delta' specifies the ratio of the screen to resize by.
--
-- 'frac' specifies what proportion of the screen to devote to the
-- master area.
--
tile :: Rational -> Rectangle -> Int -> Int -> [Rectangle]
tile f r nmaster n = if n <= nmaster || nmaster == 0
then splitVertically n r
else splitVertically nmaster r1 ++ splitVertically (n-nmaster) r2 -- two columns
where (r1,r2) = splitHorizontallyBy f r
--
-- Divide the screen vertically into n subrectangles
--
splitVertically, splitHorizontally :: Int -> Rectangle -> [Rectangle]
splitVertically n r | n < 2 = [r]
splitVertically n (Rectangle sx sy sw sh) = Rectangle sx sy sw smallh :
splitVertically (n-1) (Rectangle sx (sy+fromIntegral smallh) sw (sh-smallh))
where smallh = sh `div` fromIntegral n --hmm, this is a fold or map.
splitHorizontally n = map mirrorRect . splitVertically n . mirrorRect
-- Divide the screen into two rectangles, using a rational to specify the ratio
splitHorizontallyBy, splitVerticallyBy :: RealFrac r => r -> Rectangle -> (Rectangle, Rectangle)
splitHorizontallyBy f (Rectangle sx sy sw sh) =
( Rectangle sx sy leftw sh
, Rectangle (sx + fromIntegral leftw) sy (sw-fromIntegral leftw) sh)
where leftw = floor $ fromIntegral sw * f
-- | XXX comment me
splitVerticallyBy f = (mirrorRect *** mirrorRect) . splitHorizontallyBy f . mirrorRect
------------------------------------------------------------------------
-- Utilities
-- | Return workspace visible on screen 'sc', or Nothing.
screenWorkspace :: ScreenId -> X (Maybe WorkspaceId)
screenWorkspace sc = withWindowSet $ return . W.lookupWorkspace sc
-- | Apply an X operation to the currently focused window, if there is one.
withFocused :: (Window -> X ()) -> X ()
withFocused f = withWindowSet $ \w -> whenJust (W.peek w) f
-- | True if window is under management by us
isClient :: Window -> X Bool
isClient w = withWindowSet $ return . W.member w
-- | Combinations of extra modifier masks we need to grab keys\/buttons for.
-- (numlock and capslock)
extraModifiers :: [KeyMask]
extraModifiers = [0, numlockMask, lockMask, numlockMask .|. lockMask ]
-- | Strip numlock\/capslock from a mask
cleanMask :: KeyMask -> KeyMask
cleanMask = (complement (numlockMask .|. lockMask) .&.)
-- | Get the Pixel value for a named color
initColor :: Display -> String -> IO Pixel
initColor dpy c = (color_pixel . fst) `liftM` allocNamedColor dpy colormap c
where colormap = defaultColormap dpy (defaultScreen dpy)
------------------------------------------------------------------------
-- | Floating layer support
-- | Given a window, find the screen it is located on, and compute
-- the geometry of that window wrt. that screen.
floatLocation :: Window -> X (ScreenId, W.RationalRect)
floatLocation w = withDisplay $ \d -> do
ws <- gets windowset
wa <- io $ getWindowAttributes d w
-- XXX horrible
let sc = fromMaybe (W.current ws) $ find (pointWithin (fi $ wa_x wa) (fi $ wa_y wa) . screenRect . W.screenDetail) $ W.screens ws
sr = screenRect . W.screenDetail $ sc
bw = fi . wa_border_width $ wa
rr = W.RationalRect ((fi (wa_x wa) - fi (rect_x sr)) % fi (rect_width sr))
((fi (wa_y wa) - fi (rect_y sr)) % fi (rect_height sr))
(fi (wa_width wa + bw*2) % fi (rect_width sr))
(fi (wa_height wa + bw*2) % fi (rect_height sr))
return (W.screen $ sc, rr)
where fi x = fromIntegral x
pointWithin :: Integer -> Integer -> Rectangle -> Bool
pointWithin x y r = x >= fi (rect_x r) &&
x < fi (rect_x r) + fi (rect_width r) &&
y >= fi (rect_y r) &&
y < fi (rect_y r) + fi (rect_height r)
-- | Make a tiled window floating, using its suggested rectangle
float :: Window -> X ()
float w = do
(sc, rr) <- floatLocation w
windows $ \ws -> W.float w rr . fromMaybe ws $ do
i <- W.findIndex w ws
guard $ i `elem` map (W.tag . W.workspace) (W.screens ws)
f <- W.peek ws
sw <- W.lookupWorkspace sc ws
return (W.focusWindow f . W.shiftWin sw w $ ws)
-- ---------------------------------------------------------------------
-- Mouse handling
-- | Accumulate mouse motion events
mouseDrag :: (Position -> Position -> X ()) -> X () -> X ()
mouseDrag f done = do
drag <- gets dragging
case drag of
Just _ -> return () -- error case? we're already dragging
Nothing -> do
XConf { theRoot = root, display = d } <- ask
io $ grabPointer d root False (buttonReleaseMask .|. pointerMotionMask)
grabModeAsync grabModeAsync none none currentTime
modify $ \s -> s { dragging = Just (motion, cleanup) }
where
cleanup = do
withDisplay $ io . flip ungrabPointer currentTime
modify $ \s -> s { dragging = Nothing }
done
motion x y = do z <- f x y
clearEvents pointerMotionMask
return z
-- | XXX comment me
mouseMoveWindow :: Window -> X ()
mouseMoveWindow w = whenX (isClient w) $ withDisplay $ \d -> do
io $ raiseWindow d w
wa <- io $ getWindowAttributes d w
(_, _, _, ox', oy', _, _, _) <- io $ queryPointer d w
let ox = fromIntegral ox'
oy = fromIntegral oy'
mouseDrag (\ex ey -> io $ moveWindow d w (fromIntegral (fromIntegral (wa_x wa) + (ex - ox)))
(fromIntegral (fromIntegral (wa_y wa) + (ey - oy))))
(float w)
-- | XXX comment me
mouseResizeWindow :: Window -> X ()
mouseResizeWindow w = 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))
mouseDrag (\ex ey -> do
io $ resizeWindow d w `uncurry`
applySizeHints sh (ex - fromIntegral (wa_x wa),
ey - fromIntegral (wa_y wa)))
(float w)
-- ---------------------------------------------------------------------
-- | Support for window size hints
type D = (Dimension, Dimension)
-- | Reduce the dimensions if needed to comply to the given SizeHints.
applySizeHints :: Integral a => SizeHints -> (a,a) -> D
applySizeHints sh (w,h) = applySizeHints' sh (fromIntegral $ max 1 w,
fromIntegral $ max 1 h)
-- | XXX comment me
applySizeHints' :: SizeHints -> D -> D
applySizeHints' sh =
maybe id applyMaxSizeHint (sh_max_size sh)
. maybe id (\(bw, bh) (w, h) -> (w+bw, h+bh)) (sh_base_size sh)
. maybe id applyResizeIncHint (sh_resize_inc sh)
. maybe id applyAspectHint (sh_aspect sh)
. maybe id (\(bw,bh) (w,h) -> (w-bw, h-bh)) (sh_base_size sh)
-- | Reduce the dimensions so their aspect ratio falls between the two given aspect ratios.
applyAspectHint :: (D, D) -> D -> D
applyAspectHint ((minx, miny), (maxx, maxy)) x@(w,h)
| or [minx < 1, miny < 1, maxx < 1, maxy < 1] = x
| w * maxy > h * maxx = (h * maxx `div` maxy, h)
| w * miny < h * minx = (w, w * miny `div` minx)
| otherwise = x
-- | Reduce the dimensions so they are a multiple of the size increments.
applyResizeIncHint :: D -> D -> D
applyResizeIncHint (iw,ih) x@(w,h) =
if iw > 0 && ih > 0 then (w - w `mod` iw, h - h `mod` ih) else x
-- | Reduce the dimensions if they exceed the given maximum dimensions.
applyMaxSizeHint :: D -> D -> D
applyMaxSizeHint (mw,mh) x@(w,h) =
if mw > 0 && mh > 0 then (min w mw,min h mh) else x

120
README
View File

@@ -1,120 +0,0 @@
xmonad : a lightweight X11 window manager.
http://xmonad.org
------------------------------------------------------------------------
About:
Xmonad is a tiling window manager for X. Windows are managed using
automatic tiling algorithms, which can be dynamically configured.
Windows are arranged so as to tile the screen without gaps, maximising
screen use. All features of the window manager are accessible
from the keyboard: a mouse is strictly optional. Xmonad is written
and extensible in Haskell, and custom layout algorithms may be
implemented by the user in config files. A guiding principle of the
user interface is <i>predictability</i>: users should know in
advance precisely the window arrangement that will result from any
action, leading to an intuitive user interface.
Xmonad provides three tiling algorithms by default: tall, wide and
fullscreen. In tall or wide mode, all windows are visible and tiled
to fill the plane without gaps. In fullscreen mode only the focused
window is visible, filling the screen. Alternative tiling
algorithms are provided as extensions. Sets of windows are grouped
together on virtual workspaces and each workspace retains its own
layout. Multiple physical monitors are supported via Xinerama,
allowing simultaneous display of several workspaces.
Adhering to a minimalist philosophy of doing one job, and doing it
well, the entire code base remains tiny, and is written to be simple
to understand and modify. By using Haskell as a configuration
language arbitrarily complex extensions may be implemented by the
user using a powerful `scripting' language, without needing to
modify the window manager directly. For example, users may write
their own tiling algorithms.
------------------------------------------------------------------------
Building:
Get the dependencies
Firstly, you'll need the C X11 library headers. On many platforms,
these come pre-installed. For others, such as Debian, you can get
them from your package manager:
apt-get install libx11-dev
It is likely that you already have some of these dependencies. To check
whether you've got a package run 'ghc-pkg list some_package_name'
mtl http://hackage.haskell.org/cgi-bin/hackage-scripts/package/mtl-1.0
unix http://hackage.haskell.org/cgi-bin/hackage-scripts/package/unix-2.0
X11 http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-1.2.3
X11-extras: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras-0.4
And then build with Cabal:
runhaskell Setup.lhs configure --prefix=$HOME
runhaskell Setup.lhs build
runhaskell Setup.lhs install --user
------------------------------------------------------------------------
Notes for using the darcs version
If you're building the darcs version of xmonad, be sure to also
use the darcs version of X11-extras, which is developed concurrently
with xmonad.
darcs get http://code.haskell.org/X11-extras
Not using X11-extras from darcs, is the most common reason for the
darcs version of xmonad to fail to build.
------------------------------------------------------------------------
Running xmonad:
Add:
$HOME/bin/xmonad
to the last line of your .xsession or .xinitrc file.
------------------------------------------------------------------------
XMonadContrib
There are various contributed modules that can be used with xmonad.
Examples include an ion3-like tabbed layout, a prompt/program launcher,
and various other useful modules. XMonadContrib is available at:
0.4 release: http://www.xmonad.org/XMonadContrib-0.4.tar.gz
darcs version: darcs get http://code.haskell.org/XMonadContrib
------------------------------------------------------------------------
Other useful programs:
For a program dispatch menu:
dmenu http://www.suckless.org/download/
or
gmrun (in your package system)
For custom status bars:
dzen http://gotmor.googlepages.com/dzen
A nicer xterm replacment, that supports resizing better:
urxvt http://software.schmorp.de/pkg/rxvt-unicode.html
Authors:
Spencer Janssen
Don Stewart
Jason Creighton

104
README.md Normal file
View File

@@ -0,0 +1,104 @@
<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"><img alt="Hackage" src="https://img.shields.io/hackage/v/xmonad?logo=haskell"></a>
<a href="https://github.com/xmonad/xmonad/blob/readme/LICENSE"><img alt="License" src="https://img.shields.io/github/license/xmonad/xmonad"></a>
<a href="https://haskell.org/"><img alt="Made in Haskell" src="https://img.shields.io/badge/Made%20in-Haskell-%235e5086?logo=haskell"></a>
<br>
<a href="https://github.com/xmonad/xmonad/actions/workflows/stack.yml"><img alt="Stack" src="https://img.shields.io/github/actions/workflow/status/xmonad/xmonad/stack.yml?label=Stack&logo=githubactions&logoColor=white"></a>
<a href="https://github.com/xmonad/xmonad/actions/workflows/haskell-ci.yml"><img alt="Cabal" src="https://img.shields.io/github/actions/workflow/status/xmonad/xmonad/haskell-ci.yml?label=Cabal&logo=githubactions&logoColor=white"></a>
<a href="https://github.com/xmonad/xmonad/actions/workflows/nix.yml"><img alt="Nix" src="https://img.shields.io/github/actions/workflow/status/xmonad/xmonad/nix.yml?label=Nix&logo=githubactions&logoColor=white"></a>
<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>
<br>
<a href="https://web.libera.chat/#xmonad"><img alt="Chat on #xmonad@irc.libera.chat" src="https://img.shields.io/badge/%23%20chat-on%20libera-brightgreen"></a>
<a href="https://matrix.to/#/#xmonad:matrix.org"><img alt="Chat on #xmonad:matrix.org" src="https://img.shields.io/matrix/xmonad:matrix.org?logo=matrix"></a>
</p>
# xmonad
**A tiling window manager for X11.**
[XMonad][web:xmonad] is a tiling window manager for X11. Windows are arranged
automatically to tile the screen without gaps or overlap, maximising
screen use. Window manager features are accessible from the keyboard:
a mouse is optional. xmonad is written, configured and extensible in
Haskell. Custom layout algorithms, key bindings and other extensions
may be written by the user in config files. Layouts are applied
dynamically, and different layouts may be used on each
workspace. Xinerama is fully supported, allowing windows to be tiled
on several physical screens.
This repository contains the [xmonad][hackage:xmonad] package, a minimal,
stable, yet extensible core. It is accompanied by
[xmonad-contrib][gh:xmonad-contrib], a library of hundreds of additional
community-maintained 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.
## Installation
For installation and configuration instructions, please see:
* [downloading and installing xmonad][web:download]
* [installing latest xmonad snapshot from git][web:install]
* [configuring xmonad][web:tutorial]
If you run into any trouble, consult our [documentation][web:documentation] or
ask the [community][web:community] for help.
## Contributing
We welcome all forms of contributions:
* [bug reports and feature ideas][gh:xmonad:issues]
(also to [xmonad-contrib][gh:xmonad-contrib:issues])
* [bug fixes, new features, new extensions][gh:xmonad:pulls]
(usually to [xmonad-contrib][gh:xmonad-contrib: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]
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].
## Authors
Started in 2007 by [Spencer Janssen][gh:spencerjanssen], [Don
Stewart][gh:donsbot] and [Jason Creighton][gh:JasonCreighton], the
[XMonad][web:xmonad] project lives on thanks to [new generations of
maintainers][gh:xmonad:maintainers] and [dozens of
contributors][gh:xmonad:contributors].
[gh:spencerjanssen]: https://github.com/spencerjanssen
[gh:donsbot]: https://github.com/donsbot
[gh:JasonCreighton]: https://github.com/JasonCreighton
[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:contributors]: https://github.com/xmonad/xmonad/graphs/contributors
[gh:xmonad:issues]: https://github.com/xmonad/xmonad/issues
[gh:xmonad:maintainers]: https://github.com/xmonad/xmonad/blob/master/MAINTAINERS.md
[gh:xmonad:pulls]: https://github.com/xmonad/xmonad/pulls
[gh:xmonad:sponsors]: https://github.com/sponsors/xmonad
[gh:xmonad]: https://github.com/xmonad/xmonad
[hackage:xmonad]: https://hackage.haskell.org/package/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/

15
TODO
View File

@@ -1,15 +0,0 @@
- Write down invariants for the window life cycle, especially:
- When are borders set? Prove that the current handling is sufficient.
= Release management =
* build and typecheck all XMC
* generate haddocks for core and XMC, upload to xmonad.org
* generate manpage, generate html manpage
* document, with photos, any new layouts
* double check README build instructions
* test core with 6.6 and 6.8
* upload X11/X11-extras/xmonad to hacakge
* check examples/text in use-facing Config.hs
* check tour.html and intro.html are up to date, and mention all core bindings
* bump xmonad.cabal version

1330
TUTORIAL.md Normal file

File diff suppressed because it is too large Load Diff

276
XMonad.hs
View File

@@ -1,276 +0,0 @@
{-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.hs
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : sjanssen@cse.unl.edu
-- Stability : unstable
-- Portability : not portable, uses cunning newtype deriving
--
-- The X monad, a state monad transformer over IO, for the window
-- manager state, and support routines.
--
-----------------------------------------------------------------------------
module XMonad (
X, WindowSet, WindowSpace, WorkspaceId, ScreenId(..), ScreenDetail(..), XState(..), XConf(..), LayoutClass(..), Layout(..), ReadableLayout(..),
Typeable, Message, SomeMessage(..), fromMessage, runLayout,
runX, catchX, userCode, io, catchIO, withDisplay, withWindowSet, isRoot, getAtom, spawn, restart, trace, whenJust, whenX,
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW
) where
import StackSet
import Prelude hiding ( catch )
import Control.Exception (catch, throw, Exception(ExitException))
import Control.Monad.State
import Control.Monad.Reader
import Control.Arrow (first)
import System.IO
import System.Posix.Process (executeFile, forkProcess, getProcessStatus, createSession)
import System.Exit
import System.Environment
import Graphics.X11.Xlib
-- for Read instance
import Graphics.X11.Xlib.Extras ()
import Data.Typeable
import qualified Data.Map as M
import qualified Data.Set as S
-- | XState, the window manager state.
-- Just the display, width, height and a window list
data XState = XState
{ windowset :: !WindowSet -- ^ workspace list
, mapped :: !(S.Set Window) -- ^ the Set of mapped windows
, waitingUnmap :: !(M.Map Window Int) -- ^ the number of expected UnmapEvents
, dragging :: !(Maybe (Position -> Position -> X (), X ())) }
data XConf = XConf
{ display :: Display -- ^ the X11 display
, theRoot :: !Window -- ^ the root window
, normalBorder :: !Pixel -- ^ border color of unfocused windows
, focusedBorder :: !Pixel } -- ^ border color of the focused window
type WindowSet = StackSet WorkspaceId (Layout Window) Window ScreenId ScreenDetail
type WindowSpace = Workspace WorkspaceId (Layout Window) Window
-- | Virtual workspace indicies
type WorkspaceId = String
-- | Physical screen indicies
newtype ScreenId = S Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
-- | TODO Comment me
data ScreenDetail = SD { screenRect :: !Rectangle
, statusGap :: !(Int,Int,Int,Int) -- ^ width of status bar on the screen
} deriving (Eq,Show, Read)
------------------------------------------------------------------------
-- | The X monad, a StateT transformer over IO encapsulating the window
-- manager state
--
-- Dynamic components may be retrieved with 'get', static components
-- with 'ask'. With newtype deriving we get readers and state monads
-- instantiated on XConf and XState automatically.
--
newtype X a = X (ReaderT XConf (StateT XState IO) a)
deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf)
-- | Run the X monad, given a chunk of X monad code, and an initial state
-- Return the result, and final state
runX :: XConf -> XState -> X a -> IO (a, XState)
runX c st (X a) = runStateT (runReaderT a c) st
-- | Run in the X monad, and in case of exception, and catch it and log it
-- to stderr, and run the error case.
catchX :: X a -> X a -> X a
catchX job errcase = do
st <- get
c <- ask
(a, s') <- io $ runX c st job `catch`
\e -> case e of
ExitException {} -> throw e
_ -> do hPrint stderr e; runX c st errcase
put s'
return a
-- | Execute the argument, catching all exceptions. Either this function or
-- catchX should be used at all callsites of user customized code.
userCode :: X () -> X ()
userCode a = catchX (a >> return ()) (return ())
-- ---------------------------------------------------------------------
-- Convenient wrappers to state
-- | Run a monad action with the current display settings
withDisplay :: (Display -> X a) -> X a
withDisplay f = asks display >>= f
-- | Run a monadic action with the current stack set
withWindowSet :: (WindowSet -> X a) -> X a
withWindowSet f = gets windowset >>= f
-- | True if the given window is the root window
isRoot :: Window -> X Bool
isRoot w = liftM (w==) (asks theRoot)
-- | Wrapper for the common case of atom internment
getAtom :: String -> X Atom
getAtom str = withDisplay $ \dpy -> io $ internAtom dpy str False
-- | Common non-predefined atoms
atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, atom_WM_STATE :: X Atom
atom_WM_PROTOCOLS = getAtom "WM_PROTOCOLS"
atom_WM_DELETE_WINDOW = getAtom "WM_DELETE_WINDOW"
atom_WM_STATE = getAtom "WM_STATE"
------------------------------------------------------------------------
-- | LayoutClass handling. See particular instances in Operations.hs
-- | An existential type that can hold any object that is in the LayoutClass.
data Layout a = forall l. LayoutClass l a => Layout (l a)
-- | This class defines a set of layout types (held in Layout
-- objects) that are used when trying to read an existentially wrapped Layout.
class ReadableLayout a where
readTypes :: [Layout a]
-- | The different layout modes
--
-- 'doLayout': given a Rectangle and a Stack, layout the stack elements
-- inside the given Rectangle. If an element is not given a Rectangle
-- by 'doLayout', then it is not shown on screen. Windows are restacked
-- according to the order they are returned by 'doLayout'.
--
class (Show (layout a), Read (layout a)) => LayoutClass layout a where
-- | Given a Rectangle in which to place the windows, and a Stack of
-- windows, return a list of windows and their corresponding Rectangles.
-- The order of windows in this list should be the desired stacking order.
-- Also return a modified layout, if this layout needs to be modified
-- (e.g. if we keep track of the windows we have displayed).
doLayout :: layout a -> Rectangle -> Stack a -> X ([(a, Rectangle)], Maybe (layout a))
doLayout l r s = return (pureLayout l r s, Nothing)
-- | This is a pure version of doLayout, for cases where we don't need
-- access to the X monad to determine how to layou out the windows, and
-- we don't need to modify our layout itself.
pureLayout :: layout a -> Rectangle -> Stack a -> [(a, Rectangle)]
pureLayout _ r s = [(focus s, r)]
-- | 'handleMessage' performs message handling for that layout. If
-- 'handleMessage' returns Nothing, then the layout did not respond to
-- that message and the screen is not refreshed. Otherwise, 'handleMessage'
-- returns an updated 'LayoutClass' and the screen is refreshed.
--
handleMessage :: layout a -> SomeMessage -> X (Maybe (layout a))
handleMessage l = return . pureMessage l
-- | Respond to a message by (possibly) changing our layout, but taking
-- no other action. If the layout changes, the screen will be refreshed.
pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
pureMessage _ _ = Nothing
-- | This should be a human-readable string that is used when selecting
-- layouts by name.
description :: layout a -> String
description = show
-- Here's the magic for parsing serialised state of existentially
-- wrapped layouts: attempt to parse using the Read instance from each
-- type in our list of types, if any suceed, take the first one.
instance ReadableLayout a => Read (Layout a) where
-- We take the first parse only, because multiple matches indicate a bad parse.
readsPrec _ s = take 1 $ concatMap readLayout readTypes
where
readLayout (Layout x) = map (first Layout) $ readAsType x
-- the type indicates which Read instance to dispatch to.
-- That is, read asTypeOf the argument from the readTypes.
readAsType :: LayoutClass l a => l a -> [(l a, String)]
readAsType _ = reads s
instance ReadableLayout a => LayoutClass Layout a where
doLayout (Layout l) r s = fmap (fmap Layout) `liftM` doLayout l r s
handleMessage (Layout l) = fmap (fmap Layout) . handleMessage l
description (Layout l) = description l
instance Show (Layout a) where show (Layout l) = show l
-- | This calls doLayout if there are any windows to be laid out.
runLayout :: LayoutClass l a => l a -> Rectangle -> StackOrNot a -> X ([(a, Rectangle)], Maybe (l a))
runLayout l r = maybe (return ([], Nothing)) (doLayout l r)
-- | Based on ideas in /An Extensible Dynamically-Typed Hierarchy of Exceptions/,
-- Simon Marlow, 2006. Use extensible messages to the handleMessage handler.
--
-- User-extensible messages must be a member of this class.
--
class Typeable a => Message a
-- |
-- A wrapped value of some type in the Message class.
--
data SomeMessage = forall a. Message a => SomeMessage a
-- |
-- And now, unwrap a given, unknown Message type, performing a (dynamic)
-- type check on the result.
--
fromMessage :: Message m => SomeMessage -> Maybe m
fromMessage (SomeMessage m) = cast m
-- ---------------------------------------------------------------------
-- | General utilities
--
-- Lift an IO action into the X monad
io :: IO a -> X a
io = liftIO
-- | Lift an IO action into the X monad. If the action results in an IO
-- exception, log the exception to stderr and continue normal execution.
catchIO :: IO () -> X ()
catchIO f = liftIO (f `catch` \e -> hPrint stderr e >> hFlush stderr)
-- | spawn. Launch an external application
spawn :: String -> X ()
spawn x = io $ do
pid <- forkProcess $ do
forkProcess (createSession >> executeFile "/bin/sh" False ["-c", x] Nothing)
exitWith ExitSuccess
getProcessStatus True False pid
return ()
-- | Restart xmonad via exec().
--
-- If the first parameter is 'Just name', restart will attempt to execute the
-- program corresponding to 'name'. Otherwise, xmonad will attempt to execute
-- the name of the current program.
--
-- When the second parameter is 'True', xmonad will attempt to resume with the
-- current window state.
restart :: Maybe String -> Bool -> X ()
restart mprog resume = do
prog <- maybe (io getProgName) return mprog
args <- if resume then gets (("--resume":) . return . showWs . windowset) else return []
catchIO (executeFile prog True args Nothing)
where showWs = show . mapLayout show
-- | Run a side effecting action with the current workspace. Like 'when' but
whenJust :: Maybe a -> (a -> X ()) -> X ()
whenJust mg f = maybe (return ()) f mg
-- | Conditionally run an action, using a X event to decide
whenX :: X Bool -> X () -> X ()
whenX a f = a >>= \b -> when b f
-- | A 'trace' for the X monad. Logs a string to stderr. The result may
-- be found in your .xsession-errors file
trace :: String -> X ()
trace msg = io $! do hPutStrLn stderr msg; hFlush stderr

17
cabal.haskell-ci Normal file
View File

@@ -0,0 +1,17 @@
apt:
libx11-dev
libxext-dev
libxinerama-dev
libxrandr-dev
libxss-dev
github-patches:
.github/workflows/haskell-ci-hackage.patch
raw-project
optimization: False
package xmonad
flags: +pedantic
-- avoid --haddock-all which overwrites *-docs.tar.gz with tests docs
haddock-components: libs

4
cabal.project Normal file
View File

@@ -0,0 +1,4 @@
-- cabal.project
packages:
xmonad.cabal

106
flake.nix Normal file
View File

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

71
man/HCAR.tex Normal file
View File

@@ -0,0 +1,71 @@
% xmonad-Gx.tex
\begin{hcarentry}{xmonad}
\label{xmonad}
\report{Gwern Branwen}%11/11
\status{active development}
\makeheader
XMonad is a tiling window manager for X. Windows are arranged
automatically to tile the screen without gaps or overlap, maximizing
screen use. Window manager features are accessible from the keyboard; a
mouse is optional. XMonad is written, configured, and extensible in
Haskell. Custom layout algorithms, key bindings, and other extensions may
be written by the user in config files. Layouts are applied
dynamically, and different layouts may be used on each workspace.
Xinerama is fully supported, allowing windows to be tiled on several
physical screens.
Development since the last report has continued; XMonad founder Don Stewart
has stepped down and Adam Vogt is the new maintainer.
After gestating for 2 years, version 0.10 has been released, with simultaneous
releases of the XMonadContrib library of customizations (which has now grown to
no less than 216 modules encompassing a dizzying array of features) and the
xmonad-extras package of extensions,
Details of changes between releases can be found in the release notes:
\begin{compactitem}
\item \url{http://haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.8}
\item \url{http://haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.9}
% \item \url{http://haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.10}
\item the Darcs repositories have been upgraded to the hashed format
\item XMonad.Config.PlainConfig allows writing configs in a more 'normal' style, and not raw Haskell
\item Supports using local modules in xmonad.hs; for example: to use definitions from \~/.xmonad/lib/XMonad/Stack/MyAdditions.hs
\item xmonad --restart CLI option
\item xmonad --replace CLI option
\item XMonad.Prompt now has customizable keymaps
\item Actions.GridSelect - a GUI menu for selecting windows or workspaces \& substring search on window names
\item Actions.OnScreen
\item Extensions now can have state
\item Actions.SpawnOn - uses state to spawn applications on the workspace the user was originally on,
and not where the user happens to be
\item Markdown manpages and not man/troff
\item XMonad.Layout.ImageButtonDecoration \&\\ XMonad.Util.Image
\item XMonad.Layout.Groups
\item XMonad.Layout.ZoomRow
\item XMonad.Layout.Renamed
\item XMonad.Layout.Drawer
\item XMonad.Layout.FullScreen
\item XMonad.Hooks.ScreenCorners
\item XMonad.Actions.DynamicWorkspaceOrder
\item XMonad.Actions.WorkspaceNames
\item XMonad.Actions.DynamicWorkspaceGroups
\end{compactitem}
Binary packages of XMonad and XMonadContrib are available for all major Linux distributions.
\FurtherReading
\begin{compactitem}
\item Homepage:
\url{http://xmonad.org/}
\item Git source:
\texttt{git clone} \url{https://github.com/xmonad/xmonad.git}
\item IRC channel:
\verb+#xmonad @@ irc.freenode.org+
\item Mailing list:
\email{xmonad@@haskell.org}
\end{compactitem}
\end{hcarentry}

11
man/Makefile Normal file
View File

@@ -0,0 +1,11 @@
.PHONY: all
all: xmonad.1 xmonad.1.html
xmonad.1.markdown: xmonad.1.markdown.in
(cd .. && util/GenerateManpage.hs) <$< >$@
xmonad.1: xmonad.1.markdown
pandoc --from=markdown --to=man --standalone --output=$@ $<
xmonad.1.html: xmonad.1.markdown
pandoc --from=markdown --to=html --standalone --table-of-contents --output=$@ $<

241
man/xmonad.1 Normal file
View File

@@ -0,0 +1,241 @@
.\" Automatically generated by Pandoc 3.1.3
.\"
.\" Define V font for inline verbatim, using C font in formats
.\" that render this, and otherwise B font.
.ie "\f[CB]x\f[]"x" \{\
. ftr V B
. ftr VI BI
. ftr VB B
. ftr VBI BI
.\}
.el \{\
. ftr V CR
. ftr VI CI
. ftr VB CB
. ftr VBI CBI
.\}
.TH "XMONAD" "1" "27 October 2021" "Tiling Window Manager" ""
.hy
.SH Name
.PP
xmonad - Tiling Window Manager
.SH Description
.PP
\f[I]xmonad\f[R] is a minimalist tiling window manager for X, written in
Haskell.
Windows are managed using automatic layout algorithms, which can be
dynamically reconfigured.
At any time windows are arranged so as to maximize the use of screen
real estate.
All features of the window manager are accessible purely from the
keyboard: a mouse is entirely optional.
\f[I]xmonad\f[R] is configured in Haskell, and custom layout algorithms
may be implemented by the user in config files.
A principle of \f[I]xmonad\f[R] is predictability: the user should know
in advance precisely the window arrangement that will result from any
action.
.PP
By default, \f[I]xmonad\f[R] provides three layout algorithms: tall,
wide and fullscreen.
In tall or wide mode, windows are tiled and arranged to prevent overlap
and maximize screen use.
Sets of windows are grouped together on virtual screens, and each screen
retains its own layout, which may be reconfigured dynamically.
Multiple physical monitors are supported via Xinerama, allowing
simultaneous display of a number of screens.
.PP
By utilizing the expressivity of a modern functional language with a
rich static type system, \f[I]xmonad\f[R] provides a complete,
featureful window manager in less than 1200 lines of code, with an
emphasis on correctness and robustness.
Internal properties of the window manager are checked using a
combination of static guarantees provided by the type system, and
type-based automated testing.
A benefit of this is that the code is simple to understand, and easy to
modify.
.SH Usage
.PP
\f[I]xmonad\f[R] places each window into a \[lq]workspace\[rq].
Each workspace can have any number of windows, which you can cycle
though with mod-j and mod-k.
Windows are either displayed full screen, tiled horizontally, or tiled
vertically.
You can toggle the layout mode with mod-space, which will cycle through
the available modes.
.PP
You can switch to workspace N with mod-N.
For example, to switch to workspace 5, you would press mod-5.
Similarly, you can move the current window to another workspace with
mod-shift-N.
.PP
When running with multiple monitors (Xinerama), each screen has exactly
1 workspace visible.
mod-{w,e,r} switch the focus between screens, while shift-mod-{w,e,r}
move the current window to that screen.
When \f[I]xmonad\f[R] starts, workspace 1 is on screen 1, workspace 2 is
on screen 2, etc.
When switching workspaces to one that is already visible, the current
and visible workspaces are swapped.
.SS Flags
.PP
xmonad has several flags which you may pass to the executable.
These flags are:
.TP
\[en]recompile
Recompiles your \f[I]xmonad.hs\f[R] configuration
.TP
\[en]restart
Causes the currently running \f[I]xmonad\f[R] process to restart
.TP
\[en]replace
Replace the current window manager with xmonad
.TP
\[en]version
Display version of \f[I]xmonad\f[R]
.TP
\[en]verbose-version
Display detailed version of \f[I]xmonad\f[R]
.SS Default keyboard bindings
.TP
mod-shift-return
Launch terminal
.TP
mod-p
Launch dmenu
.TP
mod-shift-p
Launch gmrun
.TP
mod-shift-c
Close the focused window
.TP
mod-space
Rotate through the available layout algorithms
.TP
mod-shift-space
Reset the layouts on the current workspace to default
.TP
mod-n
Resize viewed windows to the correct size
.TP
mod-tab
Move focus to the next window
.TP
mod-shift-tab
Move focus to the previous window
.TP
mod-j
Move focus to the next window
.TP
mod-k
Move focus to the previous window
.TP
mod-m
Move focus to the master window
.TP
mod-return
Swap the focused window and the master window
.TP
mod-shift-j
Swap the focused window with the next window
.TP
mod-shift-k
Swap the focused window with the previous window
.TP
mod-h
Shrink the master area
.TP
mod-l
Expand the master area
.TP
mod-t
Push window back into tiling
.TP
mod-comma
Increment the number of windows in the master area
.TP
mod-period
Deincrement the number of windows in the master area
.TP
mod-shift-q
Quit xmonad
.TP
mod-q
Restart xmonad
.TP
mod-shift-slash
Run xmessage with a summary of the default keybindings (useful for
beginners)
.TP
mod-question
Run xmessage with a summary of the default keybindings (useful for
beginners)
.TP
mod-[1..9]
Switch to workspace N
.TP
mod-shift-[1..9]
Move client to workspace N
.TP
mod-{w,e,r}
Switch to physical/Xinerama screens 1, 2, or 3
.TP
mod-shift-{w,e,r}
Move client to screen 1, 2, or 3
.TP
mod-button1
Set the window to floating mode and move by dragging
.TP
mod-button2
Raise the window to the top of the stack
.TP
mod-button3
Set the window to floating mode and resize by dragging
.SH Examples
.PP
To use xmonad as your window manager add to your
\f[I]\[ti]/.xinitrc\f[R] file:
.RS
.PP
exec xmonad
.RE
.SH Customization
.PP
xmonad is customized in your \f[I]xmonad.hs\f[R], and then restarted
with mod-q.
You can choose where your configuration file lives by
.IP "1." 3
Setting \f[V]XMONAD_DATA_DIR,\f[R] \f[V]XMONAD_CONFIG_DIR\f[R], and
\f[V]XMONAD_CACHE_DIR\f[R]; \f[I]xmonad.hs\f[R] is then expected to be
in \f[V]XMONAD_CONFIG_DIR\f[R].
.IP "2." 3
Creating \f[I]xmonad.hs\f[R] in \f[I]\[ti]/.xmonad\f[R].
.IP "3." 3
Creating \f[I]xmonad.hs\f[R] in \f[V]XDG_CONFIG_HOME\f[R].
Note that, in this case, xmonad will use \f[V]XDG_DATA_HOME\f[R] and
\f[V]XDG_CACHE_HOME\f[R] for its data and cache directory respectively.
.PP
You can find many extensions to the core feature set in the xmonad-
contrib package, available through your package manager or from
xmonad.org (https://xmonad.org).
.SS Modular Configuration
.PP
As of \f[I]xmonad-0.9\f[R], any additional Haskell modules may be placed
in \f[I]\[ti]/.xmonad/lib/\f[R] are available in GHC\[cq]s searchpath.
Hierarchical modules are supported: for example, the file
\f[I]\[ti]/.xmonad/lib/XMonad/Stack/MyAdditions.hs\f[R] could contain:
.IP
.nf
\f[C]
module XMonad.Stack.MyAdditions (function1) where
function1 = error \[dq]function1: Not implemented yet!\[dq]
\f[R]
.fi
.PP
Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that
module was contained within xmonad or xmonad-contrib.
.SH Bugs
.PP
Probably.
If you find any, please report them to the
bugtracker (https://github.com/xmonad/xmonad/issues)

490
man/xmonad.1.html Normal file
View File

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

View File

@@ -1,49 +0,0 @@
./" man page created by David Lazar on April 24, 2007
./" uses ``tmac.an'' macro set
.TH xmonad 1 "18 April 07" xmonad\-1.0 "xmonad manual"
.SH NAME
xmonad \- a tiling window manager
.SH DESCRIPTION
.PP
\fBxmonad\fR is a minimalist tiling window manager for X, written in Haskell. Windows are managed using automatic layout algorithms, which can be dynamically reconfigured. At any time windows are arranged so as to maximise the use of screen real estate. All features of the window manager are accessible purely from the keyboard: a mouse is entirely optional. \fBxmonad\fR is configured in Haskell, and custom layout algorithms may be implemented by the user in config files. A principle of \fBxmonad\fR is predictability: the user should know in advance precisely the window arrangement that will result from any action.
.PP
By default, \fBxmonad\fR provides three layout algorithms: tall, wide and fullscreen. In tall or wide mode, windows are tiled and arranged to prevent overlap and maximise screen use. Sets of windows are grouped together on virtual screens, and each screen retains its own layout, which may be reconfigured dynamically. Multiple physical monitors are supported via Xinerama, allowing simultaneous display of a number of screens.
.PP
By utilising the expressivity of a modern functional language with a rich static type system, \fBxmonad\fR provides a complete, featureful window manager in less than 500 lines of code, with an emphasis on correctness and robustness. Internal properties of the window manager are checked using a combination of static guarantees provided by the type system, and type-based automated testing. A benefit of this is that the code is simple to understand, and easy to modify.
.SH USAGE
.PP
\fBxmonad\fR places each window into a "workspace". Each workspace can have any number of windows, which you can cycle though with mod-j and mod-k. Windows are either displayed full screen, tiled horizontally, or tiled vertically. You can toggle the layout mode with mod-space, which will cycle through the available modes.
.PP
You can switch to workspace N with mod-N. For example, to switch to workspace 5, you would press mod-5. Similarly, you can move the current window to another workspace with mod-shift-N.
.PP
When running with multiple monitors (Xinerama), each screen has exactly 1 workspace visible. When \fBxmonad\fR starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. If you switch to a workspace which is currently visible on another screen, \fBxmonad\fR simply switches focus to that screen. If you switch to a workspace which is *not* visible, \fBxmonad\fR replaces the workspace on the *current* screen with the workspace you selected.
.PP
For example, if you have the following configuration:
.RS
.PP
Screen 1: Workspace 2
.PP
Screen 2: Workspace 5 (current workspace)
.RE
.PP
and you wanted to view workspace 7 on screen 1, you would press:
.RS
.PP
mod-2 (to select workspace 2, and make screen 1 the current screen)
.PP
mod-7 (to select workspace 7)
.RE
.PP
Since switching to the workspace currently visible on a given screen is such a common operation, shortcuts are provided: mod-{w,e,r} switch to the workspace currently visible on screens 1, 2, and 3 respectively. Likewise, shift-mod-{w,e,r} moves the current window to the workspace on that screen. Using these keys, the above example would become mod-w mod-7.
.SS Default keyboard bindings
___KEYBINDINGS___
.SH EXAMPLES
To use \fBxmonad\fR as your window manager add:
.RS
xmonad
.RE
to your \fI~/.xinitrc\fR file
.SH CUSTOMIZATION
\fBxmonad\fR is customized by creating a custom Config.hs and (re)compiling the source code. After recompiling, 'restart' is used to fork the new version, with changes reflected immediately.
.SH BUGS
Probably. If you find any, please report them: http://code.google.com/p/xmonad/issues/list

210
man/xmonad.1.markdown Normal file
View File

@@ -0,0 +1,210 @@
% XMONAD(1) Tiling Window Manager
%
% 27 October 2021
# Name
xmonad - Tiling Window Manager
# Description
_xmonad_ is a minimalist tiling window manager for X, written in Haskell.
Windows are managed using automatic layout algorithms, which can be
dynamically reconfigured. At any time windows are arranged so as to
maximize the use of screen real estate. All features of the window manager
are accessible purely from the keyboard: a mouse is entirely optional.
_xmonad_ is configured in Haskell, and custom layout algorithms may be
implemented by the user in config files. A principle of _xmonad_ is
predictability: the user should know in advance precisely the window
arrangement that will result from any action.
By default, _xmonad_ provides three layout algorithms: tall, wide and
fullscreen. In tall or wide mode, windows are tiled and arranged to prevent
overlap and maximize screen use. Sets of windows are grouped together on
virtual screens, and each screen retains its own layout, which may be
reconfigured dynamically. Multiple physical monitors are supported via
Xinerama, allowing simultaneous display of a number of screens.
By utilizing the expressivity of a modern functional language with a rich
static type system, _xmonad_ provides a complete, featureful window manager
in less than 1200 lines of code, with an emphasis on correctness and
robustness. Internal properties of the window manager are checked using a
combination of static guarantees provided by the type system, and
type-based automated testing. A benefit of this is that the code is simple
to understand, and easy to modify.
# Usage
_xmonad_ places each window into a "workspace". Each workspace can have
any number of windows, which you can cycle though with mod-j and mod-k.
Windows are either displayed full screen, tiled horizontally, or tiled
vertically. You can toggle the layout mode with mod-space, which will cycle
through the available modes.
You can switch to workspace N with mod-N. For example, to switch to
workspace 5, you would press mod-5. Similarly, you can move the current
window to another workspace with mod-shift-N.
When running with multiple monitors (Xinerama), each screen has exactly 1
workspace visible. mod-{w,e,r} switch the focus between screens, while
shift-mod-{w,e,r} move the current window to that screen. When _xmonad_
starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. When
switching workspaces to one that is already visible, the current and
visible workspaces are swapped.
## Flags
xmonad has several flags which you may pass to the executable.
These flags are:
--recompile
: Recompiles your _xmonad.hs_ configuration
--restart
: Causes the currently running _xmonad_ process to restart
--replace
: Replace the current window manager with xmonad
--version
: Display version of _xmonad_
--verbose-version
: Display detailed version of _xmonad_
## Default keyboard bindings
mod-shift-return
: Launch terminal
mod-p
: Launch dmenu
mod-shift-p
: Launch gmrun
mod-shift-c
: Close the focused window
mod-space
: Rotate through the available layout algorithms
mod-shift-space
: Reset the layouts on the current workspace to default
mod-n
: Resize viewed windows to the correct size
mod-tab
: Move focus to the next window
mod-shift-tab
: Move focus to the previous window
mod-j
: Move focus to the next window
mod-k
: Move focus to the previous window
mod-m
: Move focus to the master window
mod-return
: Swap the focused window and the master window
mod-shift-j
: Swap the focused window with the next window
mod-shift-k
: Swap the focused window with the previous window
mod-h
: Shrink the master area
mod-l
: Expand the master area
mod-t
: Push window back into tiling
mod-comma
: Increment the number of windows in the master area
mod-period
: Deincrement the number of windows in the master area
mod-shift-q
: Quit xmonad
mod-q
: Restart xmonad
mod-shift-slash
: Run xmessage with a summary of the default keybindings (useful for beginners)
mod-question
: Run xmessage with a summary of the default keybindings (useful for beginners)
mod-[1..9]
: Switch to workspace N
mod-shift-[1..9]
: Move client to workspace N
mod-{w,e,r}
: Switch to physical/Xinerama screens 1, 2, or 3
mod-shift-{w,e,r}
: Move client to screen 1, 2, or 3
mod-button1
: Set the window to floating mode and move by dragging
mod-button2
: Raise the window to the top of the stack
mod-button3
: Set the window to floating mode and resize by dragging
# Examples
To use xmonad as your window manager add to your _~/.xinitrc_ file:
> exec xmonad
# Customization
xmonad is customized in your _xmonad.hs_, and then restarted with mod-q.
You can choose where your configuration file lives by
1. Setting `XMONAD_DATA_DIR,` `XMONAD_CONFIG_DIR`, and
`XMONAD_CACHE_DIR`; _xmonad.hs_ is then expected to be in
`XMONAD_CONFIG_DIR`.
2. Creating _xmonad.hs_ in _~/.xmonad_.
3. Creating _xmonad.hs_ in `XDG_CONFIG_HOME`. Note that, in this
case, xmonad will use `XDG_DATA_HOME` and `XDG_CACHE_HOME` for its
data and cache directory respectively.
You can find many extensions to the core feature set in the xmonad-
contrib package, available through your package manager or from
[xmonad.org].
## Modular Configuration
As of _xmonad-0.9_, any additional Haskell modules may be placed in
_~/.xmonad/lib/_ are available in GHC's searchpath. Hierarchical modules
are supported: for example, the file
_~/.xmonad/lib/XMonad/Stack/MyAdditions.hs_ could contain:
```haskell
module XMonad.Stack.MyAdditions (function1) where
function1 = error "function1: Not implemented yet!"
```
Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that
module was contained within xmonad or xmonad-contrib.
# Bugs
Probably. If you find any, please report them to the [bugtracker]
[xmonad.org]: https://xmonad.org
[bugtracker]: https://github.com/xmonad/xmonad/issues

119
man/xmonad.1.markdown.in Normal file
View File

@@ -0,0 +1,119 @@
% XMONAD(1) Tiling Window Manager
%
% 27 October 2021
# Name
xmonad - Tiling Window Manager
# Description
_xmonad_ is a minimalist tiling window manager for X, written in Haskell.
Windows are managed using automatic layout algorithms, which can be
dynamically reconfigured. At any time windows are arranged so as to
maximize the use of screen real estate. All features of the window manager
are accessible purely from the keyboard: a mouse is entirely optional.
_xmonad_ is configured in Haskell, and custom layout algorithms may be
implemented by the user in config files. A principle of _xmonad_ is
predictability: the user should know in advance precisely the window
arrangement that will result from any action.
By default, _xmonad_ provides three layout algorithms: tall, wide and
fullscreen. In tall or wide mode, windows are tiled and arranged to prevent
overlap and maximize screen use. Sets of windows are grouped together on
virtual screens, and each screen retains its own layout, which may be
reconfigured dynamically. Multiple physical monitors are supported via
Xinerama, allowing simultaneous display of a number of screens.
By utilizing the expressivity of a modern functional language with a rich
static type system, _xmonad_ provides a complete, featureful window manager
in less than 1200 lines of code, with an emphasis on correctness and
robustness. Internal properties of the window manager are checked using a
combination of static guarantees provided by the type system, and
type-based automated testing. A benefit of this is that the code is simple
to understand, and easy to modify.
# Usage
_xmonad_ places each window into a "workspace". Each workspace can have
any number of windows, which you can cycle though with mod-j and mod-k.
Windows are either displayed full screen, tiled horizontally, or tiled
vertically. You can toggle the layout mode with mod-space, which will cycle
through the available modes.
You can switch to workspace N with mod-N. For example, to switch to
workspace 5, you would press mod-5. Similarly, you can move the current
window to another workspace with mod-shift-N.
When running with multiple monitors (Xinerama), each screen has exactly 1
workspace visible. mod-{w,e,r} switch the focus between screens, while
shift-mod-{w,e,r} move the current window to that screen. When _xmonad_
starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. When
switching workspaces to one that is already visible, the current and
visible workspaces are swapped.
## Flags
xmonad has several flags which you may pass to the executable.
These flags are:
--recompile
: Recompiles your _xmonad.hs_ configuration
--restart
: Causes the currently running _xmonad_ process to restart
--replace
: Replace the current window manager with xmonad
--version
: Display version of _xmonad_
--verbose-version
: Display detailed version of _xmonad_
## Default keyboard bindings
___KEYBINDINGS___
# Examples
To use xmonad as your window manager add to your _~/.xinitrc_ file:
> exec xmonad
# Customization
xmonad is customized in your _xmonad.hs_, and then restarted with mod-q.
You can choose where your configuration file lives by
1. Setting `XMONAD_DATA_DIR,` `XMONAD_CONFIG_DIR`, and
`XMONAD_CACHE_DIR`; _xmonad.hs_ is then expected to be in
`XMONAD_CONFIG_DIR`.
2. Creating _xmonad.hs_ in _~/.xmonad_.
3. Creating _xmonad.hs_ in `XDG_CONFIG_HOME`. Note that, in this
case, xmonad will use `XDG_DATA_HOME` and `XDG_CACHE_HOME` for its
data and cache directory respectively.
You can find many extensions to the core feature set in the xmonad-
contrib package, available through your package manager or from
[xmonad.org].
## Modular Configuration
As of _xmonad-0.9_, any additional Haskell modules may be placed in
_~/.xmonad/lib/_ are available in GHC's searchpath. Hierarchical modules
are supported: for example, the file
_~/.xmonad/lib/XMonad/Stack/MyAdditions.hs_ could contain:
```haskell
module XMonad.Stack.MyAdditions (function1) where
function1 = error "function1: Not implemented yet!"
```
Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that
module was contained within xmonad or xmonad-contrib.
# Bugs
Probably. If you find any, please report them to the [bugtracker]
[xmonad.org]: https://xmonad.org
[bugtracker]: https://github.com/xmonad/xmonad/issues

334
man/xmonad.hs Normal file
View File

@@ -0,0 +1,334 @@
--
-- xmonad example config file.
--
-- A template showing all available configuration hooks,
-- and how to override the defaults in your own xmonad.hs conf file.
--
-- Normally, you'd only override those defaults you care about.
--
import XMonad
import Data.Monoid
import System.Exit
import qualified XMonad.StackSet as W
import qualified Data.Map as M
-- The preferred terminal program, which is used in a binding below and by
-- certain contrib modules.
--
myTerminal = "xterm"
-- Whether focus follows the mouse pointer.
myFocusFollowsMouse :: Bool
myFocusFollowsMouse = True
-- Whether clicking on a window to focus also passes the click to the window
myClickJustFocuses :: Bool
myClickJustFocuses = False
-- Width of the window border in pixels.
--
myBorderWidth = 1
-- modMask lets you specify which modkey you want to use. The default
-- is mod1Mask ("left alt"). You may also consider using mod3Mask
-- ("right alt"), which does not conflict with emacs keybindings. The
-- "windows key" is usually mod4Mask.
--
myModMask = mod1Mask
-- The default number of workspaces (virtual screens) and their names.
-- By default we use numeric strings, but any string may be used as a
-- workspace name. The number of workspaces is determined by the length
-- of this list.
--
-- A tagging example:
--
-- > workspaces = ["web", "irc", "code" ] ++ map show [4..9]
--
myWorkspaces = ["1","2","3","4","5","6","7","8","9"]
-- Border colors for unfocused and focused windows, respectively.
--
myNormalBorderColor = "#dddddd"
myFocusedBorderColor = "#ff0000"
------------------------------------------------------------------------
-- Key bindings. Add, modify or remove key bindings here.
--
myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $
-- launch a terminal
[ ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
-- launch dmenu
, ((modm, xK_p ), spawn "dmenu_run")
-- launch gmrun
, ((modm .|. shiftMask, xK_p ), spawn "gmrun")
-- close focused window
, ((modm .|. shiftMask, xK_c ), kill)
-- Rotate through the available layout algorithms
, ((modm, xK_space ), sendMessage NextLayout)
-- Reset the layouts on the current workspace to default
, ((modm .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf)
-- Resize viewed windows to the correct size
, ((modm, xK_n ), refresh)
-- Move focus to the next window
, ((modm, xK_Tab ), windows W.focusDown)
-- Move focus to the next window
, ((modm, xK_j ), windows W.focusDown)
-- Move focus to the previous window
, ((modm, xK_k ), windows W.focusUp )
-- Move focus to the master window
, ((modm, xK_m ), windows W.focusMaster )
-- Swap the focused window and the master window
, ((modm, xK_Return), windows W.swapMaster)
-- Swap the focused window with the next window
, ((modm .|. shiftMask, xK_j ), windows W.swapDown )
-- Swap the focused window with the previous window
, ((modm .|. shiftMask, xK_k ), windows W.swapUp )
-- Shrink the master area
, ((modm, xK_h ), sendMessage Shrink)
-- Expand the master area
, ((modm, xK_l ), sendMessage Expand)
-- Push window back into tiling
, ((modm, xK_t ), withFocused $ windows . W.sink)
-- Increment the number of windows in the master area
, ((modm , xK_comma ), sendMessage (IncMasterN 1))
-- Deincrement the number of windows in the master area
, ((modm , xK_period), sendMessage (IncMasterN (-1)))
-- Toggle the status bar gap
-- Use this binding with avoidStruts from Hooks.ManageDocks.
-- See also the statusBar function from Hooks.DynamicLog.
--
-- , ((modm , xK_b ), sendMessage ToggleStruts)
-- Quit xmonad
, ((modm .|. shiftMask, xK_q ), io exitSuccess)
-- Restart xmonad
, ((modm , xK_q ), spawn "xmonad --recompile; xmonad --restart")
-- Run xmessage with a summary of the default keybindings (useful for beginners)
, ((modm .|. shiftMask, xK_slash ), xmessage help)
]
++
--
-- mod-[1..9], Switch to workspace N
-- mod-shift-[1..9], Move client to workspace N
--
[((m .|. modm, k), windows $ f i)
| (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
++
--
-- mod-{w,e,r}, Switch to physical/Xinerama screens 1, 2, or 3
-- mod-shift-{w,e,r}, Move client to screen 1, 2, or 3
--
[((m .|. modm, key), screenWorkspace sc >>= flip whenJust (windows . f))
| (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
------------------------------------------------------------------------
-- Mouse bindings: default actions bound to mouse events
--
myMouseBindings (XConfig {XMonad.modMask = modm}) = M.fromList
-- mod-button1, Set the window to floating mode and move by dragging
[ ((modm, button1), \w -> focus w >> mouseMoveWindow w
>> windows W.shiftMaster)
-- mod-button2, Raise the window to the top of the stack
, ((modm, button2), \w -> focus w >> windows W.shiftMaster)
-- mod-button3, Set the window to floating mode and resize by dragging
, ((modm, button3), \w -> focus w >> mouseResizeWindow w
>> windows W.shiftMaster)
-- you may also bind events to the mouse scroll wheel (button4 and button5)
]
------------------------------------------------------------------------
-- Layouts:
-- You can specify and transform your layouts by modifying these values.
-- If you change layout bindings be sure to use 'mod-shift-space' after
-- restarting (with 'mod-q') to reset your layout state to the new
-- defaults, as xmonad preserves your old layout settings by default.
--
-- The available layouts. Note that each layout is separated by |||,
-- which denotes layout choice.
--
myLayout = tiled ||| Mirror tiled ||| Full
where
-- default tiling algorithm partitions the screen into two panes
tiled = Tall nmaster delta ratio
-- The default number of windows in the master pane
nmaster = 1
-- Default proportion of screen occupied by master pane
ratio = 1/2
-- Percent of screen to increment by when resizing panes
delta = 3/100
------------------------------------------------------------------------
-- Window rules:
-- Execute arbitrary actions and WindowSet manipulations when managing
-- a new window. You can use this to, for example, always float a
-- particular program, or have a client always appear on a particular
-- workspace.
--
-- To find the property name associated with a program, use
-- > xprop | grep WM_CLASS
-- and click on the client you're interested in.
--
-- To match on the WM_NAME, you can use 'title' in the same way that
-- 'className' and 'resource' are used below.
--
myManageHook = composeAll
[ className =? "MPlayer" --> doFloat
, className =? "Gimp" --> doFloat
, resource =? "desktop_window" --> doIgnore
, resource =? "kdesktop" --> doIgnore ]
------------------------------------------------------------------------
-- Event handling
-- * EwmhDesktops users should change this to ewmhDesktopsEventHook
--
-- Defines a custom handler function for X Events. The function should
-- return (All True) if the default handler is to be run afterwards. To
-- combine event hooks use mappend or mconcat from Data.Monoid.
--
myEventHook = mempty
------------------------------------------------------------------------
-- Status bars and logging
-- Perform an arbitrary action on each internal state change or X event.
-- See the 'XMonad.Hooks.DynamicLog' extension for examples.
--
myLogHook = return ()
------------------------------------------------------------------------
-- Startup hook
-- Perform an arbitrary action each time xmonad starts or is restarted
-- with mod-q. Used by, e.g., XMonad.Layout.PerWorkspace to initialize
-- per-workspace layout choices.
--
-- By default, do nothing.
myStartupHook = return ()
------------------------------------------------------------------------
-- Now run xmonad with all the defaults we set up.
-- Run xmonad with the settings you specify. No need to modify this.
--
main = xmonad defaults
-- A structure containing your configuration settings, overriding
-- fields in the default config. Any you don't override, will
-- use the defaults defined in xmonad/XMonad/Config.hs
--
-- No need to modify this.
--
defaults = def {
-- simple stuff
terminal = myTerminal,
focusFollowsMouse = myFocusFollowsMouse,
clickJustFocuses = myClickJustFocuses,
borderWidth = myBorderWidth,
modMask = myModMask,
workspaces = myWorkspaces,
normalBorderColor = myNormalBorderColor,
focusedBorderColor = myFocusedBorderColor,
-- key bindings
keys = myKeys,
mouseBindings = myMouseBindings,
-- hooks, layouts
layoutHook = myLayout,
manageHook = myManageHook,
handleEventHook = myEventHook,
logHook = myLogHook,
startupHook = myStartupHook
}
-- | Finally, a copy of the default bindings in simple textual tabular format.
help :: String
help = unlines ["The default modifier key is 'alt'. Default keybindings:",
"",
"-- launching and killing programs",
"mod-Shift-Enter Launch xterminal",
"mod-p Launch dmenu",
"mod-Shift-p Launch gmrun",
"mod-Shift-c Close/kill the focused window",
"mod-Space Rotate through the available layout algorithms",
"mod-Shift-Space Reset the layouts on the current workSpace to default",
"mod-n Resize/refresh viewed windows to the correct size",
"mod-Shift-/ Show this help message with the default keybindings",
"",
"-- move focus up or down the window stack",
"mod-Tab Move focus to the next window",
"mod-Shift-Tab Move focus to the previous window",
"mod-j Move focus to the next window",
"mod-k Move focus to the previous window",
"mod-m Move focus to the master window",
"",
"-- modifying the window order",
"mod-Return Swap the focused window and the master window",
"mod-Shift-j Swap the focused window with the next window",
"mod-Shift-k Swap the focused window with the previous window",
"",
"-- resizing the master/slave ratio",
"mod-h Shrink the master area",
"mod-l Expand the master area",
"",
"-- floating layer support",
"mod-t Push window back into tiling; unfloat and re-tile it",
"",
"-- increase or decrease number of windows in the master area",
"mod-comma (mod-,) Increment the number of windows in the master area",
"mod-period (mod-.) Deincrement the number of windows in the master area",
"",
"-- quit, or restart",
"mod-Shift-q Quit xmonad",
"mod-q Restart xmonad",
"mod-[1..9] Switch to workSpace N",
"",
"-- Workspaces & screens",
"mod-Shift-[1..9] Move client to workspace N",
"mod-{w,e,r} Switch to physical/Xinerama screens 1, 2, or 3",
"mod-Shift-{w,e,r} Move client to screen 1, 2, or 3",
"",
"-- Mouse bindings: default actions bound to mouse events",
"mod-button1 Set the window to floating mode and move by dragging",
"mod-button2 Raise the window to the top of the stack",
"mod-button3 Set the window to floating mode and resize by dragging"]

47
src/XMonad.hs Normal file
View File

@@ -0,0 +1,47 @@
--------------------------------------------------------------------
-- |
-- Module : XMonad
-- Copyright : (c) Don Stewart
-- License : BSD3
--
-- Maintainer: Don Stewart <dons@galois.com>
-- Stability : provisional
-- Portability:
--
--------------------------------------------------------------------
--
-- Useful exports for configuration files.
module XMonad (
module XMonad.Main,
module XMonad.Core,
module XMonad.Config,
module XMonad.Layout,
module XMonad.ManageHook,
module XMonad.Operations,
module Graphics.X11,
module Graphics.X11.Xlib.Extras,
(.|.),
MonadState(..), gets, modify,
MonadReader(..), asks,
MonadIO(..)
) where
-- core modules
import XMonad.Main
import XMonad.Core
import XMonad.Config
import XMonad.Layout
import XMonad.ManageHook
import XMonad.Operations
-- import XMonad.StackSet -- conflicts with 'workspaces' defined in XMonad.hs
-- modules needed to get basic configuration working
import Data.Bits
import Graphics.X11 hiding (refreshKeyboardMapping)
import Graphics.X11.Xlib.Extras
import Control.Monad.State
import Control.Monad.Reader

339
src/XMonad/Config.hs Normal file
View File

@@ -0,0 +1,339 @@
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-orphans #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Config
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : dons@galois.com
-- Stability : stable
-- Portability : portable
--
-- This module specifies the default configuration values for xmonad.
--
-- DO NOT MODIFY THIS FILE! It won't work. You may configure xmonad
-- by providing your own @~\/.xmonad\/xmonad.hs@ that overrides
-- specific fields in the default config, 'def'. For a starting point, you can
-- copy the @xmonad.hs@ found in the @man@ directory, or look at
-- examples on the xmonad wiki.
--
------------------------------------------------------------------------
module XMonad.Config (defaultConfig, Default(..)) where
--
-- Useful imports
--
import XMonad.Core as XMonad hiding
(workspaces,manageHook,keys,logHook,startupHook,borderWidth,mouseBindings
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse
,handleEventHook,clickJustFocuses,rootMask,clientMask)
import qualified XMonad.Core as XMonad
(workspaces,manageHook,keys,logHook,startupHook,borderWidth,mouseBindings
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse
,handleEventHook,clickJustFocuses,rootMask,clientMask)
import XMonad.Layout
import XMonad.Operations
import XMonad.ManageHook
import qualified XMonad.StackSet as W
import Data.Bits ((.|.))
import Data.Default.Class
import Data.Monoid
import qualified Data.Map as M
import System.Exit
import Graphics.X11.Xlib
import Graphics.X11.Xlib.Extras
-- | The default number of workspaces (virtual screens) and their names.
-- By default we use numeric strings, but any string may be used as a
-- workspace name. The number of workspaces is determined by the length
-- of this list.
--
-- A tagging example:
--
-- > workspaces = ["web", "irc", "code" ] ++ map show [4..9]
--
workspaces :: [WorkspaceId]
workspaces = map show [1 .. 9 :: Int]
-- | modMask lets you specify which modkey you want to use. The default
-- is mod1Mask ("left alt"). You may also consider using mod3Mask
-- ("right alt"), which does not conflict with emacs keybindings. The
-- "windows key" is usually mod4Mask.
--
defaultModMask :: KeyMask
defaultModMask = mod1Mask
-- | Width of the window border in pixels.
--
borderWidth :: Dimension
borderWidth = 1
-- | Border colors for unfocused and focused windows, respectively.
--
normalBorderColor, focusedBorderColor :: String
normalBorderColor = "gray" -- "#dddddd"
focusedBorderColor = "red" -- "#ff0000" don't use hex, not <24 bit safe
------------------------------------------------------------------------
-- Window rules
-- | Execute arbitrary actions and WindowSet manipulations when managing
-- a new window. You can use this to, for example, always float a
-- particular program, or have a client always appear on a particular
-- workspace.
--
-- To find the property name associated with a program, use
-- xprop | grep WM_CLASS
-- and click on the client you're interested in.
--
manageHook :: ManageHook
manageHook = composeAll
[ className =? "MPlayer" --> doFloat
, className =? "mplayer2" --> doFloat ]
------------------------------------------------------------------------
-- Logging
-- | Perform an arbitrary action on each internal state change or X event.
-- Examples include:
--
-- * do nothing
--
-- * log the state to stdout
--
-- See the 'DynamicLog' extension for examples.
--
logHook :: X ()
logHook = return ()
------------------------------------------------------------------------
-- Event handling
-- | Defines a custom handler function for X Events. The function should
-- return (All True) if the default handler is to be run afterwards.
-- To combine event hooks, use mappend or mconcat from Data.Monoid.
handleEventHook :: Event -> X All
handleEventHook _ = return (All True)
-- | Perform an arbitrary action at xmonad startup.
startupHook :: X ()
startupHook = return ()
------------------------------------------------------------------------
-- Extensible layouts
--
-- You can specify and transform your layouts by modifying these values.
-- If you change layout bindings be sure to use 'mod-shift-space' after
-- restarting (with 'mod-q') to reset your layout state to the new
-- defaults, as xmonad preserves your old layout settings by default.
--
-- | The available layouts. Note that each layout is separated by |||, which
-- denotes layout choice.
layout = tiled ||| Mirror tiled ||| Full
where
-- default tiling algorithm partitions the screen into two panes
tiled = Tall nmaster delta ratio
-- The default number of windows in the master pane
nmaster = 1
-- Default proportion of screen occupied by master pane
ratio = 1/2
-- Percent of screen to increment by when resizing panes
delta = 3/100
------------------------------------------------------------------------
-- Event Masks:
-- | The client events that xmonad is interested in
clientMask :: EventMask
clientMask = structureNotifyMask .|. enterWindowMask .|. propertyChangeMask
-- | The root events that xmonad is interested in
rootMask :: EventMask
rootMask = substructureRedirectMask .|. substructureNotifyMask
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
.|. buttonPressMask
------------------------------------------------------------------------
-- Key bindings:
-- | The preferred terminal program, which is used in a binding below and by
-- certain contrib modules.
terminal :: String
terminal = "xterm"
-- | Whether focus follows the mouse pointer.
focusFollowsMouse :: Bool
focusFollowsMouse = True
-- | Whether a mouse click select the focus or is just passed to the window
clickJustFocuses :: Bool
clickJustFocuses = True
-- | The xmonad key bindings. Add, modify or remove key bindings here.
--
-- (The comment formatting character is used when generating the manpage)
--
keys :: XConfig Layout -> M.Map (KeyMask, KeySym) (X ())
keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
-- launching and killing programs
[ ((modMask .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf) -- %! Launch terminal
, ((modMask, xK_p ), spawn "dmenu_run") -- %! Launch dmenu
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun") -- %! Launch gmrun
, ((modMask .|. shiftMask, xK_c ), kill) -- %! Close the focused window
, ((modMask, xK_space ), sendMessage NextLayout) -- %! Rotate through the available layout algorithms
, ((modMask .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf) -- %! Reset the layouts on the current workspace to default
, ((modMask, xK_n ), refresh) -- %! Resize viewed windows to the correct size
-- move focus up or down the window stack
, ((modMask, xK_Tab ), windows W.focusDown) -- %! Move focus to the next window
, ((modMask .|. shiftMask, xK_Tab ), windows W.focusUp ) -- %! Move focus to the previous window
, ((modMask, xK_j ), windows W.focusDown) -- %! Move focus to the next window
, ((modMask, xK_k ), windows W.focusUp ) -- %! Move focus to the previous window
, ((modMask, xK_m ), windows W.focusMaster ) -- %! Move focus to the master window
-- modifying the window order
, ((modMask, xK_Return), windows W.swapMaster) -- %! Swap the focused window and the master window
, ((modMask .|. shiftMask, xK_j ), windows W.swapDown ) -- %! Swap the focused window with the next window
, ((modMask .|. shiftMask, xK_k ), windows W.swapUp ) -- %! Swap the focused window with the previous window
-- resizing the master/slave ratio
, ((modMask, xK_h ), sendMessage Shrink) -- %! Shrink the master area
, ((modMask, xK_l ), sendMessage Expand) -- %! Expand the master area
-- floating layer support
, ((modMask, xK_t ), withFocused $ windows . W.sink) -- %! Push window back into tiling
-- 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 exitSuccess) -- %! Quit xmonad
, ((modMask , xK_q ), spawn "if type xmonad; then xmonad --recompile && xmonad --restart; else xmessage xmonad not in \\$PATH: \"$PATH\"; fi") -- %! Restart xmonad
, ((modMask .|. shiftMask, xK_slash ), helpCommand) -- %! Run xmessage with a summary of the default keybindings (useful for beginners)
-- repeat the binding for non-American layout keyboards
, ((modMask , xK_question), helpCommand) -- %! Run xmessage with a summary of the default keybindings (useful for beginners)
]
++
-- mod-[1..9] %! Switch to workspace N
-- mod-shift-[1..9] %! Move client to workspace N
[((m .|. modMask, k), windows $ f i)
| (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
++
-- mod-{w,e,r} %! Switch to physical/Xinerama screens 1, 2, or 3
-- mod-shift-{w,e,r} %! Move client to screen 1, 2, or 3
[((m .|. modMask, key), screenWorkspace sc >>= flip whenJust (windows . f))
| (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
where
helpCommand :: X ()
helpCommand = xmessage help
-- | Mouse bindings: default actions bound to mouse events
mouseBindings :: XConfig Layout -> M.Map (KeyMask, Button) (Window -> X ())
mouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList
-- mod-button1 %! Set the window to floating mode and move by dragging
[ ((modMask, button1), \w -> focus w >> mouseMoveWindow w
>> windows W.shiftMaster)
-- mod-button2 %! Raise the window to the top of the stack
, ((modMask, button2), windows . (W.shiftMaster .) . W.focusWindow)
-- mod-button3 %! Set the window to floating mode and resize by dragging
, ((modMask, button3), \w -> focus w >> mouseResizeWindow w
>> windows W.shiftMaster)
-- you may also bind events to the mouse scroll wheel (button4 and button5)
]
instance (a ~ Choose Tall (Choose (Mirror Tall) Full)) => Default (XConfig a) where
def = XConfig
{ XMonad.borderWidth = borderWidth
, XMonad.workspaces = workspaces
, XMonad.layoutHook = layout
, XMonad.terminal = terminal
, XMonad.normalBorderColor = normalBorderColor
, XMonad.focusedBorderColor = focusedBorderColor
, XMonad.modMask = defaultModMask
, XMonad.keys = keys
, XMonad.logHook = logHook
, XMonad.startupHook = startupHook
, XMonad.mouseBindings = mouseBindings
, XMonad.manageHook = manageHook
, XMonad.handleEventHook = handleEventHook
, XMonad.focusFollowsMouse = focusFollowsMouse
, XMonad.clickJustFocuses = clickJustFocuses
, XMonad.clientMask = clientMask
, XMonad.rootMask = rootMask
, XMonad.handleExtraArgs = \ xs theConf -> case xs of
[] -> return theConf
_ -> fail ("unrecognized flags:" ++ show xs)
, XMonad.extensibleConf = M.empty
}
-- | The default set of configuration values itself
{-# DEPRECATED defaultConfig "Use def (from Data.Default, and re-exported by XMonad and XMonad.Config) instead." #-}
defaultConfig :: XConfig (Choose Tall (Choose (Mirror Tall) Full))
defaultConfig = def
-- | Finally, a copy of the default bindings in simple textual tabular format.
help :: String
help = unlines ["The default modifier key is 'alt'. Default keybindings:",
"",
"-- launching and killing programs",
"mod-Shift-Enter Launch xterminal",
"mod-p Launch dmenu",
"mod-Shift-p Launch gmrun",
"mod-Shift-c Close/kill the focused window",
"mod-Space Rotate through the available layout algorithms",
"mod-Shift-Space Reset the layouts on the current workSpace to default",
"mod-n Resize/refresh viewed windows to the correct size",
"mod-Shift-/ Show this help message with the default keybindings",
"",
"-- move focus up or down the window stack",
"mod-Tab Move focus to the next window",
"mod-Shift-Tab Move focus to the previous window",
"mod-j Move focus to the next window",
"mod-k Move focus to the previous window",
"mod-m Move focus to the master window",
"",
"-- modifying the window order",
"mod-Return Swap the focused window and the master window",
"mod-Shift-j Swap the focused window with the next window",
"mod-Shift-k Swap the focused window with the previous window",
"",
"-- resizing the master/slave ratio",
"mod-h Shrink the master area",
"mod-l Expand the master area",
"",
"-- floating layer support",
"mod-t Push window back into tiling; unfloat and re-tile it",
"",
"-- increase or decrease number of windows in the master area",
"mod-comma (mod-,) Increment the number of windows in the master area",
"mod-period (mod-.) Deincrement the number of windows in the master area",
"",
"-- quit, or restart",
"mod-Shift-q Quit xmonad",
"mod-q Restart xmonad",
"",
"-- Workspaces & screens",
"mod-[1..9] Switch to workSpace N",
"mod-Shift-[1..9] Move client to workspace N",
"mod-{w,e,r} Switch to physical/Xinerama screens 1, 2, or 3",
"mod-Shift-{w,e,r} Move client to screen 1, 2, or 3",
"",
"-- Mouse bindings: default actions bound to mouse events",
"mod-button1 Set the window to floating mode and move by dragging",
"mod-button2 Raise the window to the top of the stack",
"mod-button3 Set the window to floating mode and resize by dragging"]

896
src/XMonad/Core.hs Normal file
View File

@@ -0,0 +1,896 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE ViewPatterns #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Core
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : spencerjanssen@gmail.com
-- Stability : unstable
-- Portability : not portable, uses cunning newtype deriving
--
-- The 'X' monad, a state monad transformer over 'IO', for the window
-- manager state, and support routines.
--
-----------------------------------------------------------------------------
module XMonad.Core (
X, WindowSet, WindowSpace, WorkspaceId,
ScreenId(..), ScreenDetail(..), XState(..),
XConf(..), XConfig(..), LayoutClass(..),
Layout(..), readsLayout, Typeable, Message,
SomeMessage(..), fromMessage, LayoutMessages(..),
StateExtension(..), ExtensionClass(..), ConfExtension(..),
runX, catchX, userCode, userCodeDef, io, catchIO, installSignalHandlers, uninstallSignalHandlers,
withDisplay, withWindowSet, isRoot, runOnWorkspaces,
getAtom, spawn, spawnPID, xfork, xmessage, recompile, trace, whenJust, whenX, ifM,
getXMonadDir, getXMonadCacheDir, getXMonadDataDir, stateFileName, binFileName,
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, atom_WM_TAKE_FOCUS, withWindowAttributes,
ManageHook, Query(..), runQuery, Directories'(..), Directories, getDirectories,
) where
import XMonad.StackSet hiding (modify)
import Prelude
import Control.Exception (fromException, try, bracket_, throw, finally, SomeException(..))
import qualified Control.Exception as E
import Control.Applicative ((<|>), empty)
import Control.Monad.Fail
import Control.Monad.Fix (fix)
import Control.Monad.State
import Control.Monad.Reader
import Control.Monad (filterM, guard, void, when)
import Data.Char (isSpace)
import Data.Semigroup
import Data.Traversable (for)
import Data.Time.Clock (UTCTime)
import Data.Default.Class
import System.Environment (lookupEnv)
import Data.List (isInfixOf, intercalate, (\\))
import System.FilePath
import System.IO
import System.Info
import System.Posix.Env (getEnv)
import System.Posix.Process (executeFile, forkProcess, getAnyProcessStatus, createSession)
import System.Posix.Signals
import System.Posix.IO
import System.Posix.Types (ProcessID)
import System.Process
import System.Directory
import System.Exit
import Graphics.X11.Xlib
import Graphics.X11.Xlib.Extras (getWindowAttributes, WindowAttributes, Event)
import Data.Typeable
import Data.Maybe (isJust,fromMaybe)
import Data.Monoid (Ap(..))
import qualified Data.Map as M
import qualified Data.Set as S
-- | XState, the (mutable) window manager state.
data XState = XState
{ windowset :: !WindowSet -- ^ workspace list
, mapped :: !(S.Set Window) -- ^ the Set of mapped windows
, waitingUnmap :: !(M.Map Window Int) -- ^ the number of expected UnmapEvents
, dragging :: !(Maybe (Position -> Position -> X (), X ()))
, numberlockMask :: !KeyMask -- ^ The numlock modifier
, extensibleState :: !(M.Map String (Either String StateExtension))
-- ^ stores custom state information.
--
-- The module "XMonad.Util.ExtensibleState" in xmonad-contrib
-- provides additional information and a simple interface for using this.
}
-- | XConf, the (read-only) window manager configuration.
data XConf = XConf
{ display :: Display -- ^ the X11 display
, config :: !(XConfig Layout) -- ^ initial user configuration
, theRoot :: !Window -- ^ the root window
, normalBorder :: !Pixel -- ^ border color of unfocused windows
, focusedBorder :: !Pixel -- ^ border color of the focused window
, keyActions :: !(M.Map (KeyMask, KeySym) (X ()))
-- ^ a mapping of key presses to actions
, buttonActions :: !(M.Map (KeyMask, Button) (Window -> X ()))
-- ^ a mapping of button presses to actions
, mouseFocused :: !Bool -- ^ was refocus caused by mouse action?
, mousePosition :: !(Maybe (Position, Position))
-- ^ position of the mouse according to
-- the event currently being processed
, currentEvent :: !(Maybe Event) -- ^ event currently being processed
, directories :: !Directories -- ^ directories to use
}
-- todo, better name
data XConfig l = XConfig
{ normalBorderColor :: !String -- ^ Non focused windows border color. Default: \"#dddddd\"
, focusedBorderColor :: !String -- ^ Focused windows border color. Default: \"#ff0000\"
, terminal :: !String -- ^ The preferred terminal application. Default: \"xterm\"
, layoutHook :: !(l Window) -- ^ The available layouts
, manageHook :: !ManageHook -- ^ The action to run when a new window is opened
, handleEventHook :: !(Event -> X All) -- ^ Handle an X event, returns (All True) if the default handler
-- should also be run afterwards. mappend should be used for combining
-- event hooks in most cases.
, workspaces :: ![String] -- ^ The list of workspaces' names
, modMask :: !KeyMask -- ^ the mod modifier
, keys :: !(XConfig Layout -> M.Map (ButtonMask,KeySym) (X ()))
-- ^ The key binding: a map from key presses and actions
, mouseBindings :: !(XConfig Layout -> M.Map (ButtonMask, Button) (Window -> X ()))
-- ^ The mouse bindings
, borderWidth :: !Dimension -- ^ The border width
, logHook :: !(X ()) -- ^ The action to perform when the windows set is changed
, startupHook :: !(X ()) -- ^ The action to perform on startup
, focusFollowsMouse :: !Bool -- ^ Whether window entry events can change focus
, clickJustFocuses :: !Bool -- ^ False to make a click which changes focus to be additionally passed to the window
, clientMask :: !EventMask -- ^ The client events that xmonad is interested in
, rootMask :: !EventMask -- ^ The root events that xmonad is interested in
, handleExtraArgs :: !([String] -> XConfig Layout -> IO (XConfig Layout))
-- ^ Modify the configuration, complain about extra arguments etc. with arguments that are not handled by default
, extensibleConf :: !(M.Map TypeRep ConfExtension)
-- ^ Stores custom config information.
--
-- The module "XMonad.Util.ExtensibleConf" in xmonad-contrib
-- provides additional information and a simple interface for using this.
}
type WindowSet = StackSet WorkspaceId (Layout Window) Window ScreenId ScreenDetail
type WindowSpace = Workspace WorkspaceId (Layout Window) Window
-- | Virtual workspace indices
type WorkspaceId = String
-- | Physical screen indices
newtype ScreenId = S Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
-- | The 'Rectangle' with screen dimensions
newtype ScreenDetail = SD { screenRect :: Rectangle }
deriving (Eq,Show, Read)
------------------------------------------------------------------------
-- | The X monad, 'ReaderT' and 'StateT' transformers over 'IO'
-- encapsulating the window manager configuration and state,
-- respectively.
--
-- Dynamic components may be retrieved with 'get', static components
-- with 'ask'. With newtype deriving we get readers and state monads
-- instantiated on 'XConf' and 'XState' automatically.
--
newtype X a = X (ReaderT XConf (StateT XState IO) a)
deriving (Functor, Applicative, Monad, MonadFail, MonadIO, MonadState XState, MonadReader XConf)
deriving (Semigroup, Monoid) via Ap X a
instance Default a => Default (X a) where
def = return def
type ManageHook = Query (Endo WindowSet)
newtype Query a = Query (ReaderT Window X a)
deriving (Functor, Applicative, Monad, MonadReader Window, MonadIO)
deriving (Semigroup, Monoid) via Ap Query a
runQuery :: Query a -> Window -> X a
runQuery (Query m) = runReaderT m
instance Default a => Default (Query a) where
def = return def
-- | Run the 'X' monad, given a chunk of 'X' monad code, and an initial state
-- Return the result, and final state
runX :: XConf -> XState -> X a -> IO (a, XState)
runX c st (X a) = runStateT (runReaderT a c) st
-- | Run in the 'X' monad, and in case of exception, and catch it and log it
-- to stderr, and run the error case.
catchX :: X a -> X a -> X a
catchX job errcase = do
st <- get
c <- ask
(a, s') <- io $ runX c st job `E.catch` \e -> case fromException e of
Just (_ :: ExitCode) -> throw e
_ -> do hPrint stderr e; runX c st errcase
put s'
return a
-- | Execute the argument, catching all exceptions. Either this function or
-- 'catchX' should be used at all callsites of user customized code.
userCode :: X a -> X (Maybe a)
userCode a = catchX (Just <$> a) (return Nothing)
-- | Same as userCode but with a default argument to return instead of using
-- Maybe, provided for convenience.
userCodeDef :: a -> X a -> X a
userCodeDef defValue a = fromMaybe defValue <$> userCode a
-- ---------------------------------------------------------------------
-- Convenient wrappers to state
-- | Run a monad action with the current display settings
withDisplay :: (Display -> X a) -> X a
withDisplay f = asks display >>= f
-- | Run a monadic action with the current stack set
withWindowSet :: (WindowSet -> X a) -> X a
withWindowSet f = gets windowset >>= f
-- | Safely access window attributes.
withWindowAttributes :: Display -> Window -> (WindowAttributes -> X ()) -> X ()
withWindowAttributes dpy win f = do
wa <- userCode (io $ getWindowAttributes dpy win)
catchX (whenJust wa f) (return ())
-- | True if the given window is the root window
isRoot :: Window -> X Bool
isRoot w = asks $ (w ==) . theRoot
-- | Wrapper for the common case of atom internment
getAtom :: String -> X Atom
getAtom str = withDisplay $ \dpy -> io $ internAtom dpy str False
-- | Common non-predefined atoms
atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, atom_WM_STATE, atom_WM_TAKE_FOCUS :: X Atom
atom_WM_PROTOCOLS = getAtom "WM_PROTOCOLS"
atom_WM_DELETE_WINDOW = getAtom "WM_DELETE_WINDOW"
atom_WM_STATE = getAtom "WM_STATE"
atom_WM_TAKE_FOCUS = getAtom "WM_TAKE_FOCUS"
------------------------------------------------------------------------
-- LayoutClass handling. See particular instances in Operations.hs
-- | An existential type that can hold any object that is in 'Read'
-- and 'LayoutClass'.
data Layout a = forall l. (LayoutClass l a, Read (l a)) => Layout (l a)
-- | Using the 'Layout' as a witness, parse existentially wrapped windows
-- from a 'String'.
readsLayout :: Layout a -> String -> [(Layout a, String)]
readsLayout (Layout l) s = [(Layout (asTypeOf x l), rs) | (x, rs) <- reads s]
-- | Every layout must be an instance of 'LayoutClass', which defines
-- the basic layout operations along with a sensible default for each.
--
-- All of the methods have default implementations, so there is no
-- minimal complete definition. They do, however, have a dependency
-- structure by default; this is something to be aware of should you
-- choose to implement one of these methods. Here is how a minimal
-- complete definition would look like if we did not provide any default
-- implementations:
--
-- * 'runLayout' || (('doLayout' || 'pureLayout') && 'emptyLayout')
--
-- * 'handleMessage' || 'pureMessage'
--
-- * 'description'
--
-- Note that any code which /uses/ 'LayoutClass' methods should only
-- ever call 'runLayout', 'handleMessage', and 'description'! In
-- other words, the only calls to 'doLayout', 'pureMessage', and other
-- such methods should be from the default implementations of
-- 'runLayout', 'handleMessage', and so on. This ensures that the
-- proper methods will be used, regardless of the particular methods
-- that any 'LayoutClass' instance chooses to define.
class (Show (layout a), Typeable layout) => LayoutClass layout a where
-- | By default, 'runLayout' calls 'doLayout' if there are any
-- windows to be laid out, and 'emptyLayout' otherwise. Most
-- instances of 'LayoutClass' probably do not need to implement
-- 'runLayout'; it is only useful for layouts which wish to make
-- use of more of the 'Workspace' information (for example,
-- "XMonad.Layout.PerWorkspace").
runLayout :: Workspace WorkspaceId (layout a) a
-> Rectangle
-> X ([(a, Rectangle)], Maybe (layout a))
runLayout (Workspace _ l ms) r = maybe (emptyLayout l r) (doLayout l r) ms
-- | Given a 'Rectangle' in which to place the windows, and a 'Stack'
-- of windows, return a list of windows and their corresponding
-- Rectangles. If an element is not given a Rectangle by
-- 'doLayout', then it is not shown on screen. The order of
-- windows in this list should be the desired stacking order.
--
-- Also possibly return a modified layout (by returning @Just
-- newLayout@), if this layout needs to be modified (e.g. if it
-- keeps track of some sort of state). Return @Nothing@ if the
-- layout does not need to be modified.
--
-- Layouts which do not need access to the 'X' monad ('IO', window
-- manager state, or configuration) and do not keep track of their
-- own state should implement 'pureLayout' instead of 'doLayout'.
doLayout :: layout a -> Rectangle -> Stack a
-> X ([(a, Rectangle)], Maybe (layout a))
doLayout l r s = return (pureLayout l r s, Nothing)
-- | This is a pure version of 'doLayout', for cases where we
-- don't need access to the 'X' monad to determine how to lay out
-- the windows, and we don't need to modify the layout itself.
pureLayout :: layout a -> Rectangle -> Stack a -> [(a, Rectangle)]
pureLayout _ r s = [(focus s, r)]
-- | 'emptyLayout' is called when there are no windows.
emptyLayout :: layout a -> Rectangle -> X ([(a, Rectangle)], Maybe (layout a))
emptyLayout _ _ = return ([], Nothing)
-- | 'handleMessage' performs message handling. If
-- 'handleMessage' returns @Nothing@, then the layout did not
-- respond to the message and the screen is not refreshed.
-- Otherwise, 'handleMessage' returns an updated layout and the
-- screen is refreshed.
--
-- Layouts which do not need access to the 'X' monad to decide how
-- to handle messages should implement 'pureMessage' instead of
-- 'handleMessage' (this restricts the risk of error, and makes
-- testing much easier).
handleMessage :: layout a -> SomeMessage -> X (Maybe (layout a))
handleMessage l = return . pureMessage l
-- | Respond to a message by (possibly) changing our layout, but
-- taking no other action. If the layout changes, the screen will
-- be refreshed.
pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
pureMessage _ _ = Nothing
-- | This should be a human-readable string that is used when
-- selecting layouts by name. The default implementation is
-- 'show', which is in some cases a poor default.
description :: layout a -> String
description = show
instance LayoutClass Layout Window where
runLayout (Workspace i (Layout l) ms) r = fmap (fmap Layout) `fmap` runLayout (Workspace i l ms) r
doLayout (Layout l) r s = fmap (fmap Layout) `fmap` doLayout l r s
emptyLayout (Layout l) r = fmap (fmap Layout) `fmap` emptyLayout l r
handleMessage (Layout l) = fmap (fmap Layout) . handleMessage l
description (Layout l) = description l
instance Show (Layout a) where show (Layout l) = show l
-- | Based on ideas in /An Extensible Dynamically-Typed Hierarchy of
-- Exceptions/, Simon Marlow, 2006. Use extensible messages to the
-- 'handleMessage' handler.
--
-- User-extensible messages must be a member of this class.
--
class Typeable a => Message a
-- |
-- A wrapped value of some type in the 'Message' class.
--
data SomeMessage = forall a. Message a => SomeMessage a
-- |
-- And now, unwrap a given, unknown 'Message' type, performing a (dynamic)
-- type check on the result.
--
fromMessage :: Message m => SomeMessage -> Maybe m
fromMessage (SomeMessage m) = cast m
-- X Events are valid Messages.
instance Message Event
-- | 'LayoutMessages' are core messages that all layouts (especially stateful
-- layouts) should consider handling.
data LayoutMessages = Hide -- ^ sent when a layout becomes non-visible
| ReleaseResources -- ^ sent when xmonad is exiting or restarting
deriving Eq
instance Message LayoutMessages
-- ---------------------------------------------------------------------
-- Extensible state/config
--
-- | Every module must make the data it wants to store
-- an instance of this class.
--
-- Minimal complete definition: initialValue
class Typeable a => ExtensionClass a where
{-# MINIMAL initialValue #-}
-- | Defines an initial value for the state extension
initialValue :: a
-- | Specifies whether the state extension should be
-- persistent. Setting this method to 'PersistentExtension'
-- will make the stored data survive restarts, but
-- requires a to be an instance of Read and Show.
--
-- It defaults to 'StateExtension', i.e. no persistence.
extensionType :: a -> StateExtension
extensionType = StateExtension
-- | Existential type to store a state extension.
data StateExtension =
forall a. ExtensionClass a => StateExtension a
-- ^ Non-persistent state extension
| forall a. (Read a, Show a, ExtensionClass a) => PersistentExtension a
-- ^ Persistent extension
-- | Existential type to store a config extension.
data ConfExtension = forall a. Typeable a => ConfExtension a
-- ---------------------------------------------------------------------
-- General utilities
-- | If-then-else lifted to a 'Monad'.
ifM :: Monad m => m Bool -> m a -> m a -> m a
ifM mb t f = mb >>= \b -> if b then t else f
-- | Lift an 'IO' action into the 'X' monad
io :: MonadIO m => IO a -> m a
io = liftIO
-- | Lift an 'IO' action into the 'X' monad. If the action results in an 'IO'
-- exception, log the exception to stderr and continue normal execution.
catchIO :: MonadIO m => IO () -> m ()
catchIO f = io (f `E.catch` \(SomeException e) -> hPrint stderr e >> hFlush stderr)
-- | spawn. Launch an external application. Specifically, it double-forks and
-- runs the 'String' you pass as a command to \/bin\/sh.
--
-- Note this function assumes your locale uses utf8.
spawn :: MonadIO m => String -> m ()
spawn x = void $ spawnPID x
-- | Like 'spawn', but returns the 'ProcessID' of the launched application
spawnPID :: MonadIO m => String -> m ProcessID
spawnPID x = xfork $ executeFile "/bin/sh" False ["-c", x] Nothing
-- | A replacement for 'forkProcess' which resets default signal handlers.
xfork :: MonadIO m => IO () -> m ProcessID
xfork x = io . forkProcess . finally nullStdin $ do
uninstallSignalHandlers
createSession
x
where
nullStdin = do
#if MIN_VERSION_unix(2,8,0)
fd <- openFd "/dev/null" ReadOnly defaultFileFlags
#else
fd <- openFd "/dev/null" ReadOnly Nothing defaultFileFlags
#endif
dupTo fd stdInput
closeFd fd
-- | Use @xmessage@ to show information to the user.
xmessage :: MonadIO m => String -> m ()
xmessage msg = void . xfork $ do
xmessageBin <- fromMaybe "xmessage" <$> liftIO (lookupEnv "XMONAD_XMESSAGE")
executeFile xmessageBin True
[ "-default", "okay"
, "-xrm", "*international:true"
, "-xrm", "*fontSet:-*-fixed-medium-r-normal-*-18-*-*-*-*-*-*-*,-*-fixed-*-*-*-*-18-*-*-*-*-*-*-*,-*-*-*-*-*-*-18-*-*-*-*-*-*-*"
, msg
] Nothing
-- | This is basically a map function, running a function in the 'X' monad on
-- each workspace with the output of that function being the modified workspace.
runOnWorkspaces :: (WindowSpace -> X WindowSpace) -> X ()
runOnWorkspaces job = do
ws <- gets windowset
h <- mapM job $ hidden ws
c:v <- mapM (\s -> (\w -> s { workspace = w}) <$> job (workspace s))
$ current ws : visible ws
modify $ \s -> s { windowset = ws { current = c, visible = v, hidden = h } }
-- | All the directories that xmonad will use. They will be used for
-- the following purposes:
--
-- * @dataDir@: This directory is used by XMonad to store data files
-- such as the run-time state file.
--
-- * @cfgDir@: This directory is where user configuration files are
-- stored (e.g, the xmonad.hs file). You may also create a @lib@
-- subdirectory in the configuration directory and the default recompile
-- command will add it to the GHC include path.
--
-- * @cacheDir@: This directory is used to store temporary files that
-- can easily be recreated such as the configuration binary and any
-- intermediate object files generated by GHC.
-- Also, the XPrompt history file goes here.
--
-- For how these directories are chosen, see 'getDirectories'.
--
data Directories' a = Directories
{ dataDir :: !a
, cfgDir :: !a
, cacheDir :: !a
}
deriving (Show, Functor, Foldable, Traversable)
-- | Convenient type alias for the most common case in which one might
-- want to use the 'Directories' type.
type Directories = Directories' FilePath
-- | Build up the 'Dirs' that xmonad will use. They are chosen as
-- follows:
--
-- 1. If all three of xmonad's environment variables (@XMONAD_DATA_DIR@,
-- @XMONAD_CONFIG_DIR@, and @XMONAD_CACHE_DIR@) are set, use them.
-- 2. If there is a build script called @build@ or configuration
-- @xmonad.hs@ in @~\/.xmonad@, set all three directories to
-- @~\/.xmonad@.
-- 3. Otherwise, use the @xmonad@ directory in @XDG_DATA_HOME@,
-- @XDG_CONFIG_HOME@, and @XDG_CACHE_HOME@ (or their respective
-- fallbacks). These directories are created if necessary.
--
-- The xmonad configuration file (or the build script, if present) is
-- always assumed to be in @cfgDir@.
--
getDirectories :: IO Directories
getDirectories = xmEnvDirs <|> xmDirs <|> xdgDirs
where
-- | Check for xmonad's environment variables first
xmEnvDirs :: IO Directories
xmEnvDirs = do
let xmEnvs = Directories{ dataDir = "XMONAD_DATA_DIR"
, cfgDir = "XMONAD_CONFIG_DIR"
, cacheDir = "XMONAD_CACHE_DIR"
}
maybe empty pure . sequenceA =<< traverse getEnv xmEnvs
-- | Check whether the config file or a build script is in the
-- @~\/.xmonad@ directory
xmDirs :: IO Directories
xmDirs = do
xmDir <- getAppUserDataDirectory "xmonad"
conf <- doesFileExist $ xmDir </> "xmonad.hs"
build <- doesFileExist $ xmDir </> "build"
-- Place *everything* in ~/.xmonad if yes
guard $ conf || build
pure Directories{ dataDir = xmDir, cfgDir = xmDir, cacheDir = xmDir }
-- | Use XDG directories as a fallback
xdgDirs :: IO Directories
xdgDirs =
for Directories{ dataDir = XdgData, cfgDir = XdgConfig, cacheDir = XdgCache }
$ \dir -> do d <- getXdgDirectory dir "xmonad"
d <$ createDirectoryIfMissing True d
-- | Return the path to the xmonad configuration directory.
getXMonadDir :: X String
getXMonadDir = asks (cfgDir . directories)
{-# DEPRECATED getXMonadDir "Use `asks (cfgDir . directories)' instead." #-}
-- | Return the path to the xmonad cache directory.
getXMonadCacheDir :: X String
getXMonadCacheDir = asks (cacheDir . directories)
{-# DEPRECATED getXMonadCacheDir "Use `asks (cacheDir . directories)' instead." #-}
-- | Return the path to the xmonad data directory.
getXMonadDataDir :: X String
getXMonadDataDir = asks (dataDir . directories)
{-# DEPRECATED getXMonadDataDir "Use `asks (dataDir . directories)' instead." #-}
binFileName, buildDirName :: Directories -> FilePath
binFileName Directories{ cacheDir } = cacheDir </> "xmonad-" <> arch <> "-" <> os
buildDirName Directories{ cacheDir } = cacheDir </> "build-" <> arch <> "-" <> os
errFileName, stateFileName :: Directories -> FilePath
errFileName Directories{ dataDir } = dataDir </> "xmonad.errors"
stateFileName Directories{ dataDir } = dataDir </> "xmonad.state"
srcFileName, libFileName :: Directories -> FilePath
srcFileName Directories{ cfgDir } = cfgDir </> "xmonad.hs"
libFileName Directories{ cfgDir } = cfgDir </> "lib"
buildScriptFileName, stackYamlFileName, nixFlakeFileName, nixDefaultFileName :: Directories -> FilePath
buildScriptFileName Directories{ cfgDir } = cfgDir </> "build"
stackYamlFileName Directories{ cfgDir } = cfgDir </> "stack.yaml"
nixFlakeFileName Directories{ cfgDir } = cfgDir </> "flake.nix"
nixDefaultFileName Directories{ cfgDir } = cfgDir </> "default.nix"
-- | Compilation method for xmonad configuration.
data Compile
= CompileGhc
| CompileCabal
| CompileStackGhc FilePath
| CompileNixFlake
| CompileNixDefault
| CompileScript FilePath
deriving (Show)
-- | Detect compilation method by looking for known file names in xmonad
-- configuration directory.
detectCompile :: Directories -> IO Compile
detectCompile dirs =
tryScript <|> tryStack <|> tryNixFlake <|> tryNixDefault <|> tryCabal <|> useGhc
where
buildScript = buildScriptFileName dirs
stackYaml = stackYamlFileName dirs
flakeNix = nixFlakeFileName dirs
defaultNix = nixDefaultFileName dirs
tryScript = do
guard =<< doesFileExist buildScript
isExe <- isExecutable buildScript
if isExe
then do
trace $ "XMonad will use build script at " <> show buildScript <> " to recompile."
pure $ CompileScript buildScript
else do
trace $ "XMonad will not use build script, because " <> show buildScript <> " is not executable."
trace $ "Suggested resolution to use it: chmod u+x " <> show buildScript
empty
tryNixFlake = do
guard =<< doesFileExist flakeNix
canonNixFlake <- canonicalizePath flakeNix
trace $ "XMonad will use nix flake at " <> show canonNixFlake <> " to recompile"
pure CompileNixFlake
tryNixDefault = do
guard =<< doesFileExist defaultNix
canonNixDefault <- canonicalizePath defaultNix
trace $ "XMonad will use nix file at " <> show canonNixDefault <> " to recompile"
pure CompileNixDefault
tryStack = do
guard =<< doesFileExist stackYaml
canonStackYaml <- canonicalizePath stackYaml
trace $ "XMonad will use stack ghc --stack-yaml " <> show canonStackYaml <> " to recompile."
pure $ CompileStackGhc canonStackYaml
tryCabal = let cwd = cfgDir dirs in listCabalFiles cwd >>= \ case
[] -> do
empty
[name] -> do
trace $ "XMonad will use " <> show name <> " to recompile."
pure CompileCabal
_ : _ : _ -> do
trace $ "XMonad will not use cabal, because there are multiple cabal files in " <> show cwd <> "."
empty
useGhc = do
trace $ "XMonad will use ghc to recompile, because none of "
<> intercalate ", "
[ show buildScript
, show stackYaml
, show flakeNix
, show defaultNix
] <> " nor a suitable .cabal file exist."
pure CompileGhc
listCabalFiles :: FilePath -> IO [FilePath]
listCabalFiles dir = map (dir </>) . Prelude.filter isCabalFile <$> listFiles dir
isCabalFile :: FilePath -> Bool
isCabalFile file = case splitExtension file of
(name, ".cabal") -> not (null name)
_ -> False
listFiles :: FilePath -> IO [FilePath]
listFiles dir = getDirectoryContents dir >>= filterM (doesFileExist . (dir </>))
-- | Determine whether or not the file found at the provided filepath is executable.
isExecutable :: FilePath -> IO Bool
isExecutable f = E.catch (executable <$> getPermissions f) (\(SomeException _) -> return False)
-- | Should we recompile xmonad configuration? Is it newer than the compiled
-- binary?
shouldCompile :: Directories -> Compile -> IO Bool
shouldCompile dirs CompileGhc = do
libTs <- mapM getModTime . Prelude.filter isSource =<< allFiles (libFileName dirs)
srcT <- getModTime (srcFileName dirs)
binT <- getModTime (binFileName dirs)
if any (binT <) (srcT : libTs)
then True <$ trace "XMonad recompiling because some files have changed."
else False <$ trace "XMonad skipping recompile because it is not forced (e.g. via --recompile), and neither xmonad.hs nor any *.hs / *.lhs / *.hsc files in lib/ have been changed."
where
isSource = flip elem [".hs",".lhs",".hsc"] . takeExtension
allFiles t = do
let prep = map (t</>) . Prelude.filter (`notElem` [".",".."])
cs <- prep <$> E.catch (getDirectoryContents t) (\(SomeException _) -> return [])
ds <- filterM doesDirectoryExist cs
concat . ((cs \\ ds):) <$> mapM allFiles ds
shouldCompile _ CompileCabal = return True
shouldCompile dirs CompileStackGhc{} = do
stackYamlT <- getModTime (stackYamlFileName dirs)
binT <- getModTime (binFileName dirs)
if binT < stackYamlT
then True <$ trace "XMonad recompiling because some files have changed."
else shouldCompile dirs CompileGhc
shouldCompile _dirs CompileNixFlake{} = True <$ trace "XMonad recompiling because flake recompilation is being used."
shouldCompile _dirs CompileNixDefault{} = True <$ trace "XMonad recompiling because nix recompilation is being used."
shouldCompile _dirs CompileScript{} =
True <$ trace "XMonad recompiling because a custom build script is being used."
getModTime :: FilePath -> IO (Maybe UTCTime)
getModTime f = E.catch (Just <$> getModificationTime f) (\(SomeException _) -> return Nothing)
-- | Compile the configuration.
compile :: Directories -> Compile -> IO ExitCode
compile dirs method =
bracket_ uninstallSignalHandlers installSignalHandlers $
withFile (errFileName dirs) WriteMode $ \err -> do
let run = runProc err
case method of
CompileGhc -> do
ghc <- fromMaybe "ghc" <$> lookupEnv "XMONAD_GHC"
run ghc ghcArgs
CompileCabal -> run "cabal" ["build"] .&&. copyBinary
where
copyBinary :: IO ExitCode
copyBinary = readProc err "cabal" ["-v0", "list-bin", "."] >>= \ case
Left status -> return status
Right (trim -> path) -> copyBinaryFrom path
CompileStackGhc stackYaml ->
run "stack" ["build", "--silent", "--stack-yaml", stackYaml] .&&.
run "stack" ("ghc" : "--stack-yaml" : stackYaml : "--" : ghcArgs)
CompileNixFlake ->
run "nix" ["build"] >>= andCopyFromResultDir
CompileNixDefault ->
run "nix-build" [] >>= andCopyFromResultDir
CompileScript script ->
run script [binFileName dirs]
where
cwd :: FilePath
cwd = cfgDir dirs
ghcArgs :: [String]
ghcArgs = [ "--make"
, "xmonad.hs"
, "-i" -- only look in @lib@
, "-ilib"
, "-fforce-recomp"
, "-main-is", "main"
, "-v0"
, "-outputdir", buildDirName dirs
, "-o", binFileName dirs
]
andCopyFromResultDir :: ExitCode -> IO ExitCode
andCopyFromResultDir exitCode = do
if exitCode == ExitSuccess then copyFromResultDir else return exitCode
findM :: (Monad m, Foldable t) => (a -> m Bool) -> t a -> m (Maybe a)
findM p = foldr (\x -> ifM (p x) (pure $ Just x)) (pure Nothing)
catchAny :: IO a -> (SomeException -> IO a) -> IO a
catchAny = E.catch
copyFromResultDir :: IO ExitCode
copyFromResultDir = do
let binaryDirectory = cfgDir dirs </> "result" </> "bin"
binFiles <- map (binaryDirectory </>) <$> catchAny (listDirectory binaryDirectory) (\_ -> return [])
mfilepath <- findM isExecutable binFiles
case mfilepath of
Just filepath -> copyBinaryFrom filepath
Nothing -> return $ ExitFailure 1
copyBinaryFrom :: FilePath -> IO ExitCode
copyBinaryFrom filepath = copyFile filepath (binFileName dirs) >> return ExitSuccess
-- waitForProcess =<< System.Process.runProcess, but without closing the err handle
runProc :: Handle -> String -> [String] -> IO ExitCode
runProc err exe args = do
(Nothing, Nothing, Nothing, h) <- createProcess_ "runProc" =<< mkProc err exe args
waitForProcess h
readProc :: Handle -> String -> [String] -> IO (Either ExitCode String)
readProc err exe args = do
spec <- mkProc err exe args
(Nothing, Just out, Nothing, h) <- createProcess_ "readProc" spec{ std_out = CreatePipe }
result <- hGetContents out
hPutStr err result >> hFlush err
waitForProcess h >>= \ case
ExitSuccess -> return $ Right result
status -> return $ Left status
mkProc :: Handle -> FilePath -> [FilePath] -> IO CreateProcess
mkProc err exe args = do
hPutStrLn err $ unwords $ "$" : exe : args
hFlush err
return (proc exe args){ cwd = Just cwd, std_err = UseHandle err }
(.&&.) :: Monad m => m ExitCode -> m ExitCode -> m ExitCode
cmd1 .&&. cmd2 = cmd1 >>= \case
ExitSuccess -> cmd2
e -> pure e
-- | Check GHC output for deprecation warnings and notify the user if there
-- were any. Report success otherwise.
checkCompileWarnings :: Directories -> IO ()
checkCompileWarnings dirs = do
ghcErr <- readFile (errFileName dirs)
if "-Wdeprecations" `isInfixOf` ghcErr
then do
let msg = unlines $
["Deprecations detected while compiling xmonad config: " <> srcFileName dirs]
++ lines ghcErr
++ ["","Please correct them or silence using {-# OPTIONS_GHC -Wno-deprecations #-}."]
trace msg
xmessage msg
else
trace "XMonad recompilation process exited with success!"
-- | Notify the user that compilation failed and what was wrong.
compileFailed :: Directories -> ExitCode -> IO ()
compileFailed dirs status = do
ghcErr <- readFile (errFileName dirs)
let msg = unlines $
["Errors detected while compiling xmonad config: " <> srcFileName dirs]
++ lines (if null ghcErr then show status else ghcErr)
++ ["","Please check the file for errors."]
-- nb, the ordering of printing, then forking, is crucial due to
-- lazy evaluation
trace msg
xmessage msg
-- | Recompile the xmonad configuration file when any of the following apply:
--
-- * force is 'True'
--
-- * the xmonad executable does not exist
--
-- * the xmonad executable is older than @xmonad.hs@ or any file in
-- the @lib@ directory (under the configuration directory)
--
-- * custom @build@ script is being used
--
-- The -i flag is used to restrict recompilation to the xmonad.hs file only,
-- and any files in the aforementioned @lib@ directory.
--
-- Compilation errors (if any) are logged to the @xmonad.errors@ file
-- in the xmonad data directory. If GHC indicates failure with a
-- non-zero exit code, an xmessage displaying that file is spawned.
--
-- 'False' is returned if there are compilation errors.
--
recompile :: MonadIO m => Directories -> Bool -> m Bool
recompile dirs force = io $ do
method <- detectCompile dirs
willCompile <- if force
then True <$ trace "XMonad recompiling (forced)."
else shouldCompile dirs method
if willCompile
then do
status <- compile dirs method
if status == ExitSuccess
then checkCompileWarnings dirs
else compileFailed dirs status
pure $ status == ExitSuccess
else
pure True
-- | Conditionally run an action, using a @Maybe a@ to decide.
whenJust :: Monad m => Maybe a -> (a -> m ()) -> m ()
whenJust mg f = maybe (return ()) f mg
-- | Conditionally run an action, using a 'X' event to decide
whenX :: X Bool -> X () -> X ()
whenX a f = a >>= \b -> when b f
-- | A 'trace' for the 'X' monad. Logs a string to stderr. The result may
-- be found in your .xsession-errors file
trace :: MonadIO m => String -> m ()
trace = io . hPutStrLn stderr
-- | Ignore SIGPIPE to avoid termination when a pipe is full, and SIGCHLD to
-- avoid zombie processes, and clean up any extant zombie processes.
installSignalHandlers :: MonadIO m => m ()
installSignalHandlers = io $ do
installHandler openEndedPipe Ignore Nothing
installHandler sigCHLD Ignore Nothing
(try :: IO a -> IO (Either SomeException a))
$ fix $ \more -> do
x <- getAnyProcessStatus False False
when (isJust x) more
return ()
uninstallSignalHandlers :: MonadIO m => m ()
uninstallSignalHandlers = io $ do
installHandler openEndedPipe Default Nothing
installHandler sigCHLD Default Nothing
return ()
trim :: String -> String
trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace

258
src/XMonad/Layout.hs Normal file
View File

@@ -0,0 +1,258 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternGuards #-}
-- --------------------------------------------------------------------------
-- |
-- Module : XMonad.Layout
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : spencerjanssen@gmail.com
-- Stability : unstable
-- Portability : not portable, mtl, posix
--
-- The collection of core layouts.
--
-----------------------------------------------------------------------------
module XMonad.Layout (
Full(..), Tall(..), Mirror(..),
Resize(..), IncMasterN(..), Choose(..), (|||), CLR(..), ChangeLayout(..), JumpToLayout(..),
mirrorRect, splitVertically,
splitHorizontally, splitHorizontallyBy, splitVerticallyBy,
tile
) where
import XMonad.Core
import Graphics.X11 (Rectangle(..))
import Graphics.X11.Xlib.Extras ( Event(DestroyWindowEvent) )
import qualified XMonad.StackSet as W
import Control.Arrow ((***), second)
import Control.Monad
import Data.Maybe (fromMaybe)
------------------------------------------------------------------------
-- | Change the size of the master pane.
data Resize = Shrink | Expand
-- | Increase the number of clients in the master pane.
newtype IncMasterN = IncMasterN Int
instance Message Resize
instance Message IncMasterN
-- | Simple fullscreen mode. Renders the focused window fullscreen.
data Full a = Full deriving (Show, Read)
instance LayoutClass Full a
-- | The builtin tiling mode of xmonad. Supports 'Shrink', 'Expand' and
-- 'IncMasterN'.
data Tall a = Tall { tallNMaster :: !Int -- ^ The default number of windows in the master pane (default: 1)
, tallRatioIncrement :: !Rational -- ^ Percent of screen to increment by when resizing panes (default: 3/100)
, tallRatio :: !Rational -- ^ Default proportion of screen occupied by master pane (default: 1/2)
}
deriving (Show, Read)
-- TODO should be capped [0..1] ..
-- a nice pure layout, lots of properties for the layout, and its messages, in Properties.hs
instance LayoutClass Tall a where
pureLayout (Tall nmaster _ frac) r s
| frac == 0 = drop nmaster layout
| frac == 1 = take nmaster layout
| otherwise = layout
where ws = W.integrate s
rs = tile frac r nmaster (length ws)
layout = zip ws rs
pureMessage (Tall nmaster delta frac) m =
msum [fmap resize (fromMessage m)
,fmap incmastern (fromMessage m)]
where resize Shrink = Tall nmaster delta (max 0 $ frac-delta)
resize Expand = Tall nmaster delta (min 1 $ frac+delta)
incmastern (IncMasterN d) = Tall (max 0 (nmaster+d)) delta frac
description _ = "Tall"
-- | Compute the positions for windows using the default two-pane tiling
-- algorithm.
--
-- The screen is divided into two panes. All clients are
-- then partitioned between these two panes. One pane, the master, by
-- convention has the least number of windows in it.
tile
:: Rational -- ^ @frac@, what proportion of the screen to devote to the master area
-> Rectangle -- ^ @r@, the rectangle representing the screen
-> Int -- ^ @nmaster@, the number of windows in the master pane
-> Int -- ^ @n@, the total number of windows to tile
-> [Rectangle]
tile f r nmaster n = if n <= nmaster || nmaster == 0
then splitVertically n r
else splitVertically nmaster r1 ++ splitVertically (n-nmaster) r2 -- two columns
where (r1,r2) = splitHorizontallyBy f r
--
-- Divide the screen vertically into n subrectangles
--
splitVertically, splitHorizontally :: Int -> Rectangle -> [Rectangle]
splitVertically n r | n < 2 = [r]
splitVertically n (Rectangle sx sy sw sh) = Rectangle sx sy sw smallh :
splitVertically (n-1) (Rectangle sx (sy+fromIntegral smallh) sw (sh-smallh))
where smallh = sh `div` fromIntegral n --hmm, this is a fold or map.
-- Not used in the core, but exported
splitHorizontally n = map mirrorRect . splitVertically n . mirrorRect
-- Divide the screen into two rectangles, using a rational to specify the ratio
splitHorizontallyBy, splitVerticallyBy :: RealFrac r => r -> Rectangle -> (Rectangle, Rectangle)
splitHorizontallyBy f (Rectangle sx sy sw sh) =
( Rectangle sx sy leftw sh
, Rectangle (sx + fromIntegral leftw) sy (sw-fromIntegral leftw) sh)
where leftw = floor $ fromIntegral sw * f
-- Not used in the core, but exported
splitVerticallyBy f = (mirrorRect *** mirrorRect) . splitHorizontallyBy f . mirrorRect
------------------------------------------------------------------------
-- | Mirror a layout, compute its 90 degree rotated form.
newtype Mirror l a = Mirror (l a) deriving (Show, Read)
instance LayoutClass l a => LayoutClass (Mirror l) a where
runLayout (W.Workspace i (Mirror l) ms) r = (map (second mirrorRect) *** fmap Mirror)
`fmap` runLayout (W.Workspace i l ms) (mirrorRect r)
handleMessage (Mirror l) = fmap (fmap Mirror) . handleMessage l
description (Mirror l) = "Mirror "++ description l
-- | Mirror a rectangle.
mirrorRect :: Rectangle -> Rectangle
mirrorRect (Rectangle rx ry rw rh) = Rectangle ry rx rh rw
------------------------------------------------------------------------
-- LayoutClass selection manager
-- Layouts that transition between other layouts
-- | Messages to change the current layout. Also see 'JumpToLayout'.
data ChangeLayout = FirstLayout | NextLayout deriving (Eq, Show)
instance Message ChangeLayout
-- | A message to jump to a particular layout, specified by its
-- description string.
--
-- The argument given to a 'JumpToLayout' message should be the
-- @description@ of the layout to be selected. If you use
-- "XMonad.Hooks.DynamicLog" from @xmonad-contrib@, this is the name of
-- the layout displayed in your status bar. Alternatively, you can use
-- GHCi to determine the proper name to use. For example:
--
-- > $ ghci
-- > GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help
-- > Loading package base ... linking ... done.
-- > :set prompt "> " -- don't show loaded module names
-- > > :m +XMonad.Core -- load the xmonad core
-- > > :m +XMonad.Layout.Grid -- load whatever module you want to use
-- > > description Grid -- find out what it's called
-- > "Grid"
--
-- As yet another (possibly easier) alternative, you can use the
-- "XMonad.Layout.Renamed" module (also in @xmonad-contrib@) to give
-- custom names to your layouts, and use those.
--
-- For example, if you want to jump directly to the 'Full' layout you
-- can do
--
-- > , ((modm .|. controlMask, xK_f), sendMessage $ JumpToLayout "Full")
--
newtype JumpToLayout = JumpToLayout String
instance Message JumpToLayout
-- | The layout choice combinator
(|||) :: l a -> r a -> Choose l r a
(|||) = Choose CL
infixr 5 |||
-- | A layout that allows users to switch between various layout options.
data Choose l r a = Choose CLR (l a) (r a) deriving (Read, Show)
-- | Choose the current sub-layout (left or right) in 'Choose'.
data CLR = CL | CR deriving (Read, Show, Eq)
data NextNoWrap = NextNoWrap deriving (Eq, Show)
instance Message NextNoWrap
-- | A small wrapper around handleMessage, as it is tedious to write
-- SomeMessage repeatedly.
handle :: (LayoutClass l a, Message m) => l a -> m -> X (Maybe (l a))
handle l m = handleMessage l (SomeMessage m)
-- | A smart constructor that takes some potential modifications, returns a
-- new structure if any fields have changed, and performs any necessary cleanup
-- on newly non-visible layouts.
choose :: (LayoutClass l a, LayoutClass r a)
=> Choose l r a -> CLR -> Maybe (l a) -> Maybe (r a) -> X (Maybe (Choose l r a))
choose (Choose d _ _) d' Nothing Nothing | d == d' = return Nothing
choose (Choose d l r) d' ml mr = f lr
where
(l', r') = (fromMaybe l ml, fromMaybe r mr)
lr = case (d, d') of
(CL, CR) -> (hide l' , return r')
(CR, CL) -> (return l', hide r' )
(_ , _ ) -> (return l', return r')
f (x,y) = Just <$> liftM2 (Choose d') x y
hide x = fromMaybe x <$> handle x Hide
instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
runLayout (W.Workspace i (Choose CL l r) ms) =
fmap (second . fmap $ flip (Choose CL) r) . runLayout (W.Workspace i l ms)
runLayout (W.Workspace i (Choose CR l r) ms) =
fmap (second . fmap $ Choose CR l) . runLayout (W.Workspace i r ms)
description (Choose CL l _) = description l
description (Choose CR _ r) = description r
handleMessage lr m | Just NextLayout <- fromMessage m = do
mlr' <- handle lr NextNoWrap
maybe (handle lr FirstLayout) (return . Just) mlr'
handleMessage c@(Choose d l r) m | Just NextNoWrap <- fromMessage m =
case d of
CL -> do
ml <- handle l NextNoWrap
case ml of
Just _ -> choose c CL ml Nothing
Nothing -> choose c CR Nothing =<< handle r FirstLayout
CR -> choose c CR Nothing =<< handle r NextNoWrap
handleMessage c@(Choose _ l _) m | Just FirstLayout <- fromMessage m =
flip (choose c CL) Nothing =<< handle l FirstLayout
handleMessage c@(Choose d l r) m | Just ReleaseResources <- fromMessage m =
join $ liftM2 (choose c d) (handle l ReleaseResources) (handle r ReleaseResources)
handleMessage c@(Choose d l r) m | Just e@DestroyWindowEvent{} <- fromMessage m =
join $ liftM2 (choose c d) (handle l e) (handle r e)
handleMessage c@(Choose d l r) m | Just (JumpToLayout desc) <- fromMessage m = do
ml <- handleMessage l m
mr <- handleMessage r m
let md | desc == description (fromMaybe l ml) = CL
| desc == description (fromMaybe r mr) = CR
| otherwise = d
choose c md ml mr
handleMessage c@(Choose d l r) m = do
ml' <- case d of
CL -> handleMessage l m
CR -> return Nothing
mr' <- case d of
CL -> return Nothing
CR -> handleMessage r m
choose c d ml' mr'

464
src/XMonad/Main.hs Normal file
View File

@@ -0,0 +1,464 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
----------------------------------------------------------------------------
-- |
-- Module : XMonad.Main
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : spencerjanssen@gmail.com
-- Stability : unstable
-- Portability : not portable, uses mtl, X11, posix
--
-- xmonad, a minimalist, tiling window manager for X11
--
-----------------------------------------------------------------------------
module XMonad.Main (xmonad, buildLaunch, launch) where
import System.Locale.SetLocale
import qualified Control.Exception as E
import Data.Bits
import Data.List ((\\))
import Data.Foldable (traverse_)
import qualified Data.Map as M
import qualified Data.Set as S
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad (filterM, guard, unless, void, when, forever)
import Data.Maybe (fromMaybe, isJust)
import Data.Monoid (getAll)
import Graphics.X11.Xlib hiding (refreshKeyboardMapping)
import Graphics.X11.Xlib.Extras
import XMonad.Core
import qualified XMonad.Config as Default
import XMonad.StackSet (new, floating, member)
import qualified XMonad.StackSet as W
import XMonad.Operations
import System.IO
import System.Directory
import System.Info
import System.Environment (getArgs, getProgName, withArgs)
import System.Posix.Process (executeFile)
import System.Exit (exitFailure)
import System.FilePath
import Paths_xmonad (version)
import Data.Version (showVersion)
import Graphics.X11.Xinerama (compiledWithXinerama)
import Graphics.X11.Xrandr (xrrQueryExtension, xrrUpdateConfiguration)
------------------------------------------------------------------------
-- |
-- | The entry point into xmonad. Attempts to compile any custom main
-- for xmonad, and if it doesn't find one, just launches the default.
xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
xmonad conf = do
installSignalHandlers -- important to ignore SIGCHLD to avoid zombies
dirs <- getDirectories
let launch' args = do
catchIO (buildLaunch dirs)
conf'@XConfig { layoutHook = Layout l }
<- handleExtraArgs conf args conf{ layoutHook = Layout (layoutHook conf) }
withArgs [] $ launch (conf' { layoutHook = l }) dirs
args <- getArgs
case args of
["--help"] -> usage
["--recompile"] -> recompile dirs True >>= flip unless exitFailure
["--restart"] -> sendRestart
["--version"] -> putStrLn $ unwords shortVersion
["--verbose-version"] -> putStrLn . unwords $ shortVersion ++ longVersion
"--replace" : args' -> sendReplace >> launch' args'
_ -> launch' args
where
shortVersion = ["xmonad", showVersion version]
longVersion = [ "compiled by", compilerName, showVersion compilerVersion
, "for", arch ++ "-" ++ os
, "\nXinerama:", show compiledWithXinerama ]
usage :: IO ()
usage = do
self <- getProgName
putStr . unlines $
[ "Usage: " <> self <> " [OPTION]"
, "Options:"
, " --help Print this message"
, " --version Print the version number"
, " --recompile Recompile your xmonad.hs"
, " --replace Replace the running window manager with xmonad"
, " --restart Request a running xmonad process to restart"
]
-- | Build the xmonad configuration file with ghc, then execute it.
-- If there are no errors, this function does not return. An
-- exception is raised in any of these cases:
--
-- * ghc missing
--
-- * both the configuration file and executable are missing
--
-- * xmonad.hs fails to compile
--
-- ** wrong ghc in path (fails to compile)
--
-- ** type error, syntax error, ..
--
-- * Missing XMonad\/XMonadContrib modules due to ghc upgrade
--
buildLaunch :: Directories -> IO ()
buildLaunch dirs = do
whoami <- getProgName
let bin = binFileName dirs
let compiledConfig = takeFileName bin
unless (whoami == compiledConfig) $ do
trace $ concat
[ "XMonad is recompiling and replacing itself with another XMonad process because the current process is called "
, show whoami
, " but the compiled configuration should be called "
, show compiledConfig
]
recompile dirs False
args <- getArgs
executeFile bin False args Nothing
-- | Entry point into xmonad for custom builds.
--
-- This function isn't meant to be called by the typical xmonad user
-- because it:
--
-- * Does not process any command line arguments.
--
-- * Therefore doesn't know how to restart a running xmonad.
--
-- * Does not compile your configuration file since it assumes it's
-- actually running from within your compiled configuration.
--
-- Unless you know what you are doing, you should probably be using
-- the 'xmonad' function instead.
--
-- However, if you are using a custom build environment (such as
-- stack, cabal, make, etc.) you will likely want to call this
-- function instead of 'xmonad'. You probably also want to have a key
-- binding to the 'XMonad.Operations.restart` function that restarts
-- your custom binary with the resume flag set to @True@.
launch :: (LayoutClass l Window, Read (l Window)) => XConfig l -> Directories -> IO ()
launch initxmc drs = do
-- setup locale information from environment
setLocale LC_ALL (Just "")
-- ignore SIGPIPE and SIGCHLD
installSignalHandlers
-- First, wrap the layout in an existential, to keep things pretty:
let xmc = initxmc { layoutHook = Layout $ layoutHook initxmc }
dpy <- openDisplay ""
let dflt = defaultScreen dpy
rootw <- rootWindow dpy dflt
-- If another WM is running, a BadAccess error will be returned. The
-- default error handler will write the exception to stderr and exit with
-- an error.
selectInput dpy rootw $ rootMask initxmc
sync dpy False -- sync to ensure all outstanding errors are delivered
-- turn off the default handler in favor of one that ignores all errors
-- (ugly, I know)
xSetErrorHandler -- in C, I'm too lazy to write the binding: dons
xinesc <- getCleanedScreenInfo dpy
nbc <- do v <- initColor dpy $ normalBorderColor xmc
Just nbc_ <- initColor dpy $ normalBorderColor Default.def
return (fromMaybe nbc_ v)
fbc <- do v <- initColor dpy $ focusedBorderColor xmc
Just fbc_ <- initColor dpy $ focusedBorderColor Default.def
return (fromMaybe fbc_ v)
hSetBuffering stdout NoBuffering
let layout = layoutHook xmc
initialWinset = let padToLen n xs = take (max n (length xs)) $ xs ++ repeat ""
in new layout (padToLen (length xinesc) (workspaces xmc)) $ map SD xinesc
cf = XConf
{ display = dpy
, config = xmc
, theRoot = rootw
, normalBorder = nbc
, focusedBorder = fbc
, keyActions = keys xmc xmc
, buttonActions = mouseBindings xmc xmc
, mouseFocused = False
, mousePosition = Nothing
, currentEvent = Nothing
, directories = drs
}
st = XState
{ windowset = initialWinset
, numberlockMask = 0
, mapped = S.empty
, waitingUnmap = M.empty
, dragging = Nothing
, extensibleState = M.empty
}
allocaXEvent $ \e ->
runX cf st $ do
-- check for serialized state in a file.
serializedSt <- do
path <- asks $ stateFileName . directories
exists <- io (doesFileExist path)
if exists then readStateFile initxmc else return Nothing
-- restore extensibleState if we read it from a file.
let extst = maybe M.empty extensibleState serializedSt
modify (\s -> s {extensibleState = extst})
cacheNumlockMask
grabKeys
grabButtons
io $ sync dpy False
ws <- io $ scan dpy rootw
-- bootstrap the windowset, Operations.windows will identify all
-- the windows in winset as new and set initial properties for
-- those windows. Remove all windows that are no longer top-level
-- children of the root, they may have disappeared since
-- restarting.
let winset = maybe initialWinset windowset serializedSt
windows . const . foldr W.delete winset $ W.allWindows winset \\ ws
-- manage the as-yet-unmanaged windows
mapM_ manage (ws \\ W.allWindows winset)
userCode $ startupHook initxmc
rrData <- io $ xrrQueryExtension dpy
let rrUpdate = when (isJust rrData) . void . xrrUpdateConfiguration
-- main loop, for all you HOF/recursion fans out there.
forever $ prehandle =<< io (nextEvent dpy e >> rrUpdate e >> getEvent e)
return ()
where
-- if the event gives us the position of the pointer, set mousePosition
prehandle e = let mouse = do guard (ev_event_type e `elem` evs)
return (fromIntegral (ev_x_root e)
,fromIntegral (ev_y_root e))
in local (\c -> c { mousePosition = mouse, currentEvent = Just e }) (handleWithHook e)
evs = [ keyPress, keyRelease, enterNotify, leaveNotify
, buttonPress, buttonRelease]
-- | Runs handleEventHook from the configuration and runs the default handler
-- function if it returned True.
handleWithHook :: Event -> X ()
handleWithHook e = do
evHook <- asks (handleEventHook . config)
whenX (userCodeDef True $ getAll `fmap` evHook e) (handle e)
-- ---------------------------------------------------------------------
-- | Event handler. Map X events onto calls into Operations.hs, which
-- modify our internal model of the window manager state.
--
-- Events dwm handles that we don't:
--
-- [ButtonPress] = buttonpress,
-- [Expose] = expose,
-- [PropertyNotify] = propertynotify,
--
handle :: Event -> X ()
-- run window manager command
handle (KeyEvent {ev_event_type = t, ev_state = m, ev_keycode = code})
| t == keyPress = withDisplay $ \dpy -> do
s <- io $ keycodeToKeysym dpy code 0
mClean <- cleanMask m
ks <- asks keyActions
userCodeDef () $ whenJust (M.lookup (mClean, s) ks) id
-- manage a new window
handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
withWindowAttributes dpy w $ \wa -> do -- ignore override windows
-- need to ignore mapping requests by managed windows not on the current workspace
managed <- isClient w
when (not (wa_override_redirect wa) && not managed) $ manage w
-- window destroyed, unmanage it
-- window gone, unmanage it
-- broadcast to layouts
handle e@(DestroyWindowEvent {ev_window = w}) = do
whenX (isClient w) $ do
unmanage w
modify (\s -> s { mapped = S.delete w (mapped s)
, waitingUnmap = M.delete w (waitingUnmap s)})
-- the window is already unmanged, but we broadcast the event to all layouts
-- to trigger garbage-collection in case they hold window-specific resources
broadcastMessage e
-- We track expected unmap events in waitingUnmap. We ignore this event unless
-- it is synthetic or we are not expecting an unmap notification from a window.
handle (UnmapEvent {ev_window = w, ev_send_event = synthetic}) = whenX (isClient w) $ do
e <- gets (fromMaybe 0 . M.lookup w . waitingUnmap)
if synthetic || e == 0
then unmanage w
else modify (\s -> s { waitingUnmap = M.update mpred w (waitingUnmap s) })
where mpred 1 = Nothing
mpred n = Just $ pred n
-- set keyboard mapping
handle e@(MappingNotifyEvent {}) = do
io $ refreshKeyboardMapping e
when (ev_request e `elem` [mappingKeyboard, mappingModifier]) $ do
cacheNumlockMask
grabKeys
-- handle button release, which may finish dragging.
handle e@(ButtonEvent {ev_event_type = t})
| t == buttonRelease = do
drag <- gets dragging
case drag of
-- we're done dragging and have released the mouse:
Just (_,f) -> modify (\s -> s { dragging = Nothing }) >> f
Nothing -> broadcastMessage e
-- handle motionNotify event, which may mean we are dragging.
handle e@(MotionEvent {ev_event_type = _t, ev_x = x, ev_y = y}) = do
drag <- gets dragging
case drag of
Just (d,_) -> d (fromIntegral x) (fromIntegral y) -- we're dragging
Nothing -> broadcastMessage e
-- click on an unfocused window, makes it focused on this workspace
handle e@(ButtonEvent {ev_window = w,ev_event_type = t,ev_button = b })
| t == buttonPress = do
-- If it's the root window, then it's something we
-- grabbed in grabButtons. Otherwise, it's click-to-focus.
dpy <- asks display
isr <- isRoot w
m <- cleanMask $ ev_state e
mact <- asks (M.lookup (m, b) . buttonActions)
case mact of
Just act | isr -> act $ ev_subwindow e
_ -> do
focus w
ctf <- asks (clickJustFocuses . config)
unless ctf $ io (allowEvents dpy replayPointer currentTime)
broadcastMessage e -- Always send button events.
-- entered a normal window: focus it if focusFollowsMouse is set to
-- True in the user's config.
handle e@(CrossingEvent {ev_window = w, ev_event_type = t})
| t == enterNotify && ev_mode e == notifyNormal
= whenX (asks $ focusFollowsMouse . config) $ do
dpy <- asks display
root <- asks theRoot
(_, _, w', _, _, _, _, _) <- io $ queryPointer dpy root
-- when Xlib cannot find a child that contains the pointer,
-- it returns None(0)
when (w' == 0 || w == w') (focus w)
-- left a window, check if we need to focus root
handle e@(CrossingEvent {ev_event_type = t})
| t == leaveNotify
= do rootw <- asks theRoot
when (ev_window e == rootw && not (ev_same_screen e)) $ setFocusX rootw
-- configure a window
handle e@(ConfigureRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
ws <- gets windowset
bw <- asks (borderWidth . config)
if M.member w (floating ws)
|| not (member w ws)
then do io $ configureWindow dpy w (ev_value_mask e) $ WindowChanges
{ wc_x = ev_x e
, wc_y = ev_y e
, wc_width = ev_width e
, wc_height = ev_height e
, wc_border_width = fromIntegral bw
, wc_sibling = ev_above e
, wc_stack_mode = ev_detail e }
when (member w ws) (float w)
else withWindowAttributes dpy w $ \wa -> io $ allocaXEvent $ \ev -> do
setEventType ev configureNotify
setConfigureEvent ev w w
(wa_x wa) (wa_y wa) (wa_width wa)
(wa_height wa) (ev_border_width e) none (wa_override_redirect wa)
sendEvent dpy w False 0 ev
io $ sync dpy False
-- configuration changes in the root may mean display settings have changed
handle (ConfigureEvent {ev_window = w}) = whenX (isRoot w) rescreen
-- property notify
handle event@(PropertyEvent { ev_event_type = t, ev_atom = a })
| t == propertyNotify && a == wM_NAME = asks (logHook . config) >>= userCodeDef () >>
broadcastMessage event
handle e@ClientMessageEvent { ev_message_type = mt } = do
a <- getAtom "XMONAD_RESTART"
if mt == a
then restart "xmonad" True
else broadcastMessage e
handle e = broadcastMessage e -- trace (eventName e) -- ignoring
-- ---------------------------------------------------------------------
-- IO stuff. Doesn't require any X state
-- Most of these things run only on startup (bar grabkeys)
-- | scan for any new windows to manage. If they're already managed,
-- this should be idempotent.
scan :: Display -> Window -> IO [Window]
scan dpy rootw = do
(_, _, ws) <- queryTree dpy rootw
filterM (\w -> ok w `E.catch` skip) ws
-- TODO: scan for windows that are either 'IsViewable' or where WM_STATE ==
-- Iconic
where ok w = do wa <- getWindowAttributes dpy w
a <- internAtom dpy "WM_STATE" False
p <- getWindowProperty32 dpy a w
let ic = case p of
Just (3:_) -> True -- 3 for iconified
_ -> False
return $ not (wa_override_redirect wa)
&& (wa_map_state wa == waIsViewable || ic)
skip :: E.SomeException -> IO Bool
skip _ = return False
-- | Grab the keys back
grabKeys :: X ()
grabKeys = do
XConf { display = dpy, theRoot = rootw } <- ask
io $ ungrabKey dpy anyKey anyModifier rootw
let grab :: (KeyMask, KeyCode) -> X ()
grab (km, kc) = io $ grabKey dpy kc km rootw True grabModeAsync grabModeAsync
traverse_ grab =<< mkGrabs =<< asks (M.keys . keyActions)
-- | Grab the buttons
grabButtons :: X ()
grabButtons = do
XConf { display = dpy, theRoot = rootw } <- ask
let grab button mask = io $ grabButton dpy button mask rootw False buttonPressMask
grabModeAsync grabModeSync none none
io $ ungrabButton dpy anyButton anyModifier rootw
ems <- extraModifiers
ba <- asks buttonActions
mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys ba)

125
src/XMonad/ManageHook.hs Normal file
View File

@@ -0,0 +1,125 @@
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.ManageHook
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : spencerjanssen@gmail.com
-- Stability : unstable
--
-- An EDSL for ManageHooks
--
-----------------------------------------------------------------------------
-- XXX examples required
module XMonad.ManageHook where
import XMonad.Core
import Graphics.X11.Xlib.Extras
import Graphics.X11.Xlib (Display, Window, internAtom, wM_NAME)
import Control.Exception (bracket, SomeException(..))
import qualified Control.Exception as E
import Control.Monad.Reader
import Data.Maybe
import Data.Monoid
import qualified XMonad.StackSet as W
import XMonad.Operations (floatLocation, reveal, isFixedSizeOrTransient)
-- | Lift an 'X' action to a 'Query'.
liftX :: X a -> Query a
liftX = Query . lift
-- | The identity hook that returns the WindowSet unchanged.
idHook :: Monoid m => m
idHook = mempty
-- | Infix 'mappend'. Compose two 'ManageHook' from right to left.
(<+>) :: Monoid m => m -> m -> m
(<+>) = mappend
-- | Compose the list of 'ManageHook's.
composeAll :: Monoid m => [m] -> m
composeAll = mconcat
infix 0 -->
-- | @p --> x@. If @p@ returns 'True', execute the 'ManageHook'.
--
-- > (-->) :: Monoid m => Query Bool -> Query m -> Query m -- a simpler type
(-->) :: (Monad m, Monoid a) => m Bool -> m a -> m a
p --> f = p >>= \b -> if b then f else return mempty
-- | @q =? x@. if the result of @q@ equals @x@, return 'True'.
(=?) :: Eq a => Query a -> a -> Query Bool
q =? x = fmap (== x) q
infixr 3 <&&>, <||>
-- | '&&' lifted to a 'Monad'.
(<&&>) :: Monad m => m Bool -> m Bool -> m Bool
x <&&> y = ifM x y (pure False)
-- | '||' lifted to a 'Monad'.
(<||>) :: Monad m => m Bool -> m Bool -> m Bool
x <||> y = ifM x (pure True) y
-- | Return the window title; i.e., the string returned by @_NET_WM_NAME@,
-- or failing that, the string returned by @WM_NAME@.
title :: Query String
title = ask >>= \w -> liftX $ do
d <- asks display
let
getProp =
(internAtom d "_NET_WM_NAME" False >>= getTextProperty d w)
`E.catch` \(SomeException _) -> getTextProperty d w wM_NAME
extract prop = do l <- wcTextPropertyToTextList d prop
return $ fromMaybe "" $ listToMaybe l
io $ bracket getProp (xFree . tp_value) extract `E.catch` \(SomeException _) -> return ""
-- | Return the application name; i.e., the /first/ string returned by
-- @WM_CLASS@.
appName :: Query String
appName = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resName $ io $ getClassHint d w)
-- | Backwards compatible alias for 'appName'.
resource :: Query String
resource = appName
-- | Return the resource class; i.e., the /second/ string returned by
-- @WM_CLASS@.
className :: Query String
className = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resClass $ io $ getClassHint d w)
-- | A query that can return an arbitrary X property of type 'String',
-- identified by name. Works for ASCII strings only. For the properties
-- @_NET_WM_NAME@/@WM_NAME@ and @WM_CLASS@ the specialised variants 'title'
-- and 'appName'/'className' are preferred.
stringProperty :: String -> Query String
stringProperty p = ask >>= (\w -> liftX $ withDisplay $ \d -> fromMaybe "" <$> getStringProperty d w p)
getStringProperty :: Display -> Window -> String -> X (Maybe String)
getStringProperty d w p = do
a <- getAtom p
md <- io $ getWindowProperty8 d a w
return $ fmap (map (toEnum . fromIntegral)) md
-- | Return whether the window will be a floating window or not
willFloat :: Query Bool
willFloat = ask >>= \w -> liftX $ withDisplay $ \d -> isFixedSizeOrTransient d w
-- | Modify the 'WindowSet' with a pure function.
doF :: (s -> s) -> Query (Endo s)
doF = return . Endo
-- | Move the window to the floating layer.
doFloat :: ManageHook
doFloat = ask >>= \w -> doF . W.float w . snd =<< liftX (floatLocation w)
-- | Map the window and remove it from the 'WindowSet'.
doIgnore :: ManageHook
doIgnore = ask >>= \w -> liftX (reveal w) >> doF (W.delete w)
-- | Move the window to a given workspace
doShift :: WorkspaceId -> ManageHook
doShift i = doF . W.shiftWin i =<< ask

899
src/XMonad/Operations.hs Normal file
View File

@@ -0,0 +1,899 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- --------------------------------------------------------------------------
-- |
-- Module : XMonad.Operations
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : dons@cse.unsw.edu.au
-- Stability : unstable
-- Portability : not portable, mtl, posix
--
-- Operations. A module for functions that don't cleanly fit anywhere else.
--
-----------------------------------------------------------------------------
module XMonad.Operations (
-- * Manage One Window
manage, unmanage, killWindow, kill, isClient,
setInitialProperties, setWMState, setWindowBorderWithFallback,
hide, reveal, tileWindow,
setTopFocus, focus, isFixedSizeOrTransient,
-- * Manage Windows
windows, refresh, rescreen, modifyWindowSet, windowBracket, windowBracket_, clearEvents, getCleanedScreenInfo,
withFocused, withUnfocused,
-- * Keyboard and Mouse
cleanMask, extraModifiers,
mouseDrag, mouseMoveWindow, mouseResizeWindow,
setButtonGrab, setFocusX, cacheNumlockMask, mkGrabs, unGrab,
-- * Messages
sendMessage, broadcastMessage, sendMessageWithNoRefresh,
sendRestart, sendReplace,
-- * Save and Restore State
StateFile (..), writeStateToFile, readStateFile, restart,
-- * Floating Layer
float, floatLocation,
-- * Window Size Hints
D, mkAdjust, applySizeHints, applySizeHints', applySizeHintsContents,
applyAspectHint, applyResizeIncHint, applyMaxSizeHint,
-- * Rectangles
containedIn, nubScreens, pointWithin, scaleRationalRect,
-- * Other Utilities
initColor, pointScreen, screenWorkspace,
setLayout, updateLayout,
) where
import XMonad.Core
import XMonad.Layout (Full(..))
import qualified XMonad.StackSet as W
import Data.Maybe
import Data.Monoid (Endo(..),Any(..))
import Data.List (nub, (\\), find)
import Data.Bits ((.|.), (.&.), complement, setBit, testBit)
import Data.Function (on)
import Data.Ratio
import qualified Data.Map as M
import qualified Data.Set as S
import Control.Arrow (second)
import Control.Monad.Fix (fix)
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad (forM, forM_, guard, join, unless, void, when)
import qualified Control.Exception as C
import System.IO
import System.Directory
import System.Posix.Process (executeFile)
import Graphics.X11.Xlib
import Graphics.X11.Xinerama (getScreenInfo)
import Graphics.X11.Xlib.Extras
-- ---------------------------------------------------------------------
-- Window manager operations
-- | Detect whether a window has fixed size or is transient. This check
-- can be used to determine whether the window should be floating or not
--
isFixedSizeOrTransient :: Display -> Window -> X Bool
isFixedSizeOrTransient d w = do
sh <- io $ getWMNormalHints d w
let isFixedSize = isJust (sh_min_size sh) && sh_min_size sh == sh_max_size sh
isTransient <- isJust <$> io (getTransientForHint d w)
return (isFixedSize || isTransient)
-- |
-- Add a new window to be managed in the current workspace.
-- Bring it into focus.
--
-- Whether the window is already managed, or not, it is mapped, has its
-- border set, and its event mask set.
--
manage :: Window -> X ()
manage w = whenX (not <$> isClient w) $ withDisplay $ \d -> do
shouldFloat <- isFixedSizeOrTransient d w
rr <- snd `fmap` floatLocation w
-- ensure that float windows don't go over the edge of the screen
let adjust (W.RationalRect x y wid h) | x + wid > 1 || y + h > 1 || x < 0 || y < 0
= W.RationalRect (0.5 - wid/2) (0.5 - h/2) wid h
adjust r = r
f ws | shouldFloat = W.float w (adjust rr) . W.insertUp w . W.view i $ ws
| otherwise = W.insertUp w ws
where i = W.tag $ W.workspace $ W.current ws
mh <- asks (manageHook . config)
g <- appEndo <$> userCodeDef (Endo id) (runQuery mh w)
windows (g . f)
-- | A window no longer exists; remove it from the window
-- list, on whatever workspace it is.
--
unmanage :: Window -> X ()
unmanage = windows . W.delete
-- | Kill the specified window. If we do kill it, we'll get a
-- delete notify back from X.
--
-- There are two ways to delete a window. Either just kill it, or if it
-- supports the delete protocol, send a delete event (e.g. firefox)
--
killWindow :: Window -> X ()
killWindow w = withDisplay $ \d -> do
wmdelt <- atom_WM_DELETE_WINDOW ; wmprot <- atom_WM_PROTOCOLS
protocols <- io $ getWMProtocols d w
io $ if wmdelt `elem` protocols
then allocaXEvent $ \ev -> do
setEventType ev clientMessage
setClientMessageEvent ev w wmprot 32 wmdelt currentTime
sendEvent d w False noEventMask ev
else void (killClient d w)
-- | Kill the currently focused client.
kill :: X ()
kill = withFocused killWindow
-- ---------------------------------------------------------------------
-- Managing windows
-- | Modify the current window list with a pure function, and refresh
windows :: (WindowSet -> WindowSet) -> X ()
windows f = do
XState { windowset = old } <- get
let oldvisible = concatMap (W.integrate' . W.stack . W.workspace) $ W.current old : W.visible old
newwindows = W.allWindows ws \\ W.allWindows old
ws = f old
XConf { display = d , normalBorder = nbc, focusedBorder = fbc } <- ask
mapM_ setInitialProperties newwindows
whenJust (W.peek old) $ \otherw -> do
nbs <- asks (normalBorderColor . config)
setWindowBorderWithFallback d otherw nbs nbc
modify (\s -> s { windowset = ws })
-- notify non visibility
let tags_oldvisible = map (W.tag . W.workspace) $ W.current old : W.visible old
gottenhidden = filter (flip elem tags_oldvisible . W.tag) $ W.hidden ws
mapM_ (sendMessageWithNoRefresh Hide) gottenhidden
-- for each workspace, layout the currently visible workspaces
let allscreens = W.screens ws
summed_visible = scanl (++) [] $ map (W.integrate' . W.stack . W.workspace) allscreens
rects <- fmap concat $ forM (zip allscreens summed_visible) $ \ (w, vis) -> do
let wsp = W.workspace w
this = W.view n ws
n = W.tag wsp
tiled = (W.stack . W.workspace . W.current $ this)
>>= W.filter (`M.notMember` W.floating ws)
>>= W.filter (`notElem` vis)
viewrect = screenRect $ W.screenDetail w
-- just the tiled windows:
-- now tile the windows on this workspace, modified by the gap
(rs, ml') <- runLayout wsp { W.stack = tiled } viewrect `catchX`
runLayout wsp { W.stack = tiled, W.layout = Layout Full } viewrect
updateLayout n ml'
let m = W.floating ws
flt = [(fw, scaleRationalRect viewrect r)
| fw <- filter (`M.member` m) (W.index this)
, fw `notElem` vis
, Just r <- [M.lookup fw m]]
vs = flt ++ rs
io $ restackWindows d (map fst vs)
-- return the visible windows for this workspace:
return vs
let visible = map fst rects
mapM_ (uncurry tileWindow) rects
whenJust (W.peek ws) $ \w -> do
fbs <- asks (focusedBorderColor . config)
setWindowBorderWithFallback d w fbs fbc
mapM_ reveal visible
setTopFocus
-- hide every window that was potentially visible before, but is not
-- given a position by a layout now.
mapM_ hide (nub (oldvisible ++ newwindows) \\ visible)
-- all windows that are no longer in the windowset are marked as
-- withdrawn, it is important to do this after the above, otherwise 'hide'
-- will overwrite withdrawnState with iconicState
mapM_ (`setWMState` withdrawnState) (W.allWindows old \\ W.allWindows ws)
isMouseFocused <- asks mouseFocused
unless isMouseFocused $ clearEvents enterWindowMask
asks (logHook . config) >>= userCodeDef ()
-- | Modify the @WindowSet@ in state with no special handling.
modifyWindowSet :: (WindowSet -> WindowSet) -> X ()
modifyWindowSet f = modify $ \xst -> xst { windowset = f (windowset xst) }
-- | Perform an @X@ action and check its return value against a predicate p.
-- If p holds, unwind changes to the @WindowSet@ and replay them using @windows@.
windowBracket :: (a -> Bool) -> X a -> X a
windowBracket p action = withWindowSet $ \old -> do
a <- action
when (p a) . withWindowSet $ \new -> do
modifyWindowSet $ const old
windows $ const new
return a
-- | Perform an @X@ action. If it returns @Any True@, unwind the
-- changes to the @WindowSet@ and replay them using @windows@. This is
-- a version of @windowBracket@ that discards the return value and
-- handles an @X@ action that reports its need for refresh via @Any@.
windowBracket_ :: X Any -> X ()
windowBracket_ = void . windowBracket getAny
-- | Produce the actual rectangle from a screen and a ratio on that screen.
scaleRationalRect :: Rectangle -> W.RationalRect -> Rectangle
scaleRationalRect (Rectangle sx sy sw sh) (W.RationalRect rx ry rw rh)
= Rectangle (sx + scale sw rx) (sy + scale sh ry) (scale sw rw) (scale sh rh)
where scale s r = floor (toRational s * r)
-- | Set a window's WM_STATE property.
setWMState :: Window -> Int -> X ()
setWMState w v = withDisplay $ \dpy -> do
a <- atom_WM_STATE
io $ changeProperty32 dpy w a a propModeReplace [fromIntegral v, fromIntegral none]
-- | Set the border color using the window's color map, if possible;
-- otherwise fall back to the color in @Pixel@.
setWindowBorderWithFallback :: Display -> Window -> String -> Pixel -> X ()
setWindowBorderWithFallback dpy w color basic = io $
C.handle fallback $ do
wa <- getWindowAttributes dpy w
pixel <- setPixelSolid . color_pixel . fst <$> allocNamedColor dpy (wa_colormap wa) color
setWindowBorder dpy w pixel
where
fallback :: C.SomeException -> IO ()
fallback _ = setWindowBorder dpy w basic
-- | Hide a window by unmapping it and setting Iconified.
hide :: Window -> X ()
hide w = whenX (gets (S.member w . mapped)) $ withDisplay $ \d -> do
cMask <- asks $ clientMask . config
io $ do selectInput d w (cMask .&. complement structureNotifyMask)
unmapWindow d w
selectInput d w cMask
setWMState w iconicState
-- this part is key: we increment the waitingUnmap counter to distinguish
-- between client and xmonad initiated unmaps.
modify (\s -> s { waitingUnmap = M.insertWith (+) w 1 (waitingUnmap s)
, mapped = S.delete w (mapped s) })
-- | Show a window by mapping it and setting Normal.
-- This is harmless if the window was already visible.
reveal :: Window -> X ()
reveal w = withDisplay $ \d -> do
setWMState w normalState
io $ mapWindow d w
whenX (isClient w) $ modify (\s -> s { mapped = S.insert w (mapped s) })
-- | Set some properties when we initially gain control of a window.
setInitialProperties :: Window -> X ()
setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
setWMState w iconicState
asks (clientMask . config) >>= io . selectInput d w
bw <- asks (borderWidth . config)
io $ setWindowBorderWidth d w bw
-- we must initially set the color of new windows, to maintain invariants
-- required by the border setting in 'windows'
io $ setWindowBorder d w nb
-- | Render the currently visible workspaces, as determined by
-- the 'StackSet'. Also, set focus to the focused window.
--
-- This is our 'view' operation (MVC), in that it pretty prints our model
-- with X calls.
--
refresh :: X ()
refresh = windows id
-- | Remove all events of a given type from the event queue.
clearEvents :: EventMask -> X ()
clearEvents mask = withDisplay $ \d -> io $ do
sync d False
allocaXEvent $ \p -> fix $ \again -> do
more <- checkMaskEvent d mask p
when more again -- beautiful
-- | Move and resize @w@ such that it fits inside the given rectangle,
-- including its border.
tileWindow :: Window -> Rectangle -> X ()
tileWindow w r = withDisplay $ \d -> withWindowAttributes d w $ \wa -> do
-- give all windows at least 1x1 pixels
let bw = fromIntegral $ wa_border_width wa
least x | x <= bw*2 = 1
| otherwise = x - bw*2
io $ moveResizeWindow d w (rect_x r) (rect_y r)
(least $ rect_width r) (least $ rect_height r)
-- ---------------------------------------------------------------------
-- | Returns 'True' if the first rectangle is contained within, but not equal
-- to the second.
containedIn :: Rectangle -> Rectangle -> Bool
containedIn r1@(Rectangle x1 y1 w1 h1) r2@(Rectangle x2 y2 w2 h2)
= and [ r1 /= r2
, x1 >= x2
, y1 >= y2
, fromIntegral x1 + w1 <= fromIntegral x2 + w2
, fromIntegral y1 + h1 <= fromIntegral y2 + h2 ]
-- | Given a list of screens, remove all duplicated screens and screens that
-- are entirely contained within another.
nubScreens :: [Rectangle] -> [Rectangle]
nubScreens xs = nub . filter (\x -> not $ any (x `containedIn`) xs) $ xs
-- | Clean the list of screens according to the rules documented for
-- nubScreens.
getCleanedScreenInfo :: MonadIO m => Display -> m [Rectangle]
getCleanedScreenInfo = io . fmap nubScreens . getScreenInfo
-- | The screen configuration may have changed (due to -- xrandr),
-- update the state and refresh the screen, and reset the gap.
rescreen :: X ()
rescreen = withDisplay getCleanedScreenInfo >>= \case
[] -> trace "getCleanedScreenInfo returned []"
xinesc:xinescs ->
windows $ \ws@W.StackSet{ W.current = v, W.visible = vs, W.hidden = hs } ->
let (xs, ys) = splitAt (length xinescs) (map W.workspace vs ++ hs)
a = W.Screen (W.workspace v) 0 (SD xinesc)
as = zipWith3 W.Screen xs [1..] $ map SD xinescs
in ws { W.current = a
, W.visible = as
, W.hidden = ys }
-- ---------------------------------------------------------------------
-- | Tell whether or not to intercept clicks on a given window
setButtonGrab :: Bool -> Window -> X ()
setButtonGrab grab w = do
pointerMode <- asks $ \c -> if clickJustFocuses (config c)
then grabModeAsync
else grabModeSync
withDisplay $ \d -> io $ if grab
then forM_ [button1, button2, button3] $ \b ->
grabButton d b anyModifier w False buttonPressMask
pointerMode grabModeSync none none
else ungrabButton d anyButton anyModifier w
-- ---------------------------------------------------------------------
-- Setting keyboard focus
-- | Set the focus to the window on top of the stack, or root
setTopFocus :: X ()
setTopFocus = withWindowSet $ maybe (setFocusX =<< asks theRoot) setFocusX . W.peek
-- | Set focus explicitly to window 'w' if it is managed by us, or root.
-- This happens if X notices we've moved the mouse (and perhaps moved
-- the mouse to a new screen).
focus :: Window -> X ()
focus w = local (\c -> c { mouseFocused = True }) $ withWindowSet $ \s -> do
let stag = W.tag . W.workspace
curr = stag $ W.current s
mnew <- maybe (return Nothing) (fmap (fmap stag) . uncurry pointScreen)
=<< asks mousePosition
root <- asks theRoot
case () of
_ | W.member w s && W.peek s /= Just w -> windows (W.focusWindow w)
| Just new <- mnew, w == root && curr /= new
-> windows (W.view new)
| otherwise -> return ()
-- | Call X to set the keyboard focus details.
setFocusX :: Window -> X ()
setFocusX w = withWindowSet $ \ws -> do
dpy <- asks display
-- clear mouse button grab and border on other windows
forM_ (W.current ws : W.visible ws) $ \wk ->
forM_ (W.index (W.view (W.tag (W.workspace wk)) ws)) $ \otherw ->
setButtonGrab True otherw
-- If we ungrab buttons on the root window, we lose our mouse bindings.
whenX (not <$> isRoot w) $ setButtonGrab False w
hints <- io $ getWMHints dpy w
protocols <- io $ getWMProtocols dpy w
wmprot <- atom_WM_PROTOCOLS
wmtf <- atom_WM_TAKE_FOCUS
currevt <- asks currentEvent
let inputHintSet = wmh_flags hints `testBit` inputHintBit
when (inputHintSet && wmh_input hints || not inputHintSet) $
io $ do setInputFocus dpy w revertToPointerRoot 0
when (wmtf `elem` protocols) $
io $ allocaXEvent $ \ev -> do
setEventType ev clientMessage
setClientMessageEvent ev w wmprot 32 wmtf $ maybe currentTime event_time currevt
sendEvent dpy w False noEventMask ev
where event_time ev =
if ev_event_type ev `elem` timedEvents then
ev_time ev
else
currentTime
timedEvents = [ keyPress, keyRelease, buttonPress, buttonRelease, enterNotify, leaveNotify, selectionRequest ]
cacheNumlockMask :: X ()
cacheNumlockMask = do
dpy <- asks display
ms <- io $ getModifierMapping dpy
xs <- sequence [ do ks <- io $ keycodeToKeysym dpy kc 0
if ks == xK_Num_Lock
then return (setBit 0 (fromIntegral m))
else return (0 :: KeyMask)
| (m, kcs) <- ms, kc <- kcs, kc /= 0
]
modify (\s -> s { numberlockMask = foldr (.|.) 0 xs })
-- | Given a list of keybindings, turn the given 'KeySym's into actual
-- 'KeyCode's and prepare them for grabbing.
mkGrabs :: [(KeyMask, KeySym)] -> X [(KeyMask, KeyCode)]
mkGrabs ks = withDisplay $ \dpy -> do
let (minCode, maxCode) = displayKeycodes dpy
allCodes = [fromIntegral minCode .. fromIntegral maxCode]
-- build a map from keysyms to lists of keysyms (doing what
-- XGetKeyboardMapping would do if the X11 package bound it)
syms <- forM allCodes $ \code -> io (keycodeToKeysym dpy code 0)
let -- keycodeToKeysym returns noSymbol for all unbound keycodes,
-- and we don't want to grab those whenever someone accidentally
-- uses def :: KeySym
keysymMap = M.delete noSymbol $
M.fromListWith (++) (zip syms [[code] | code <- allCodes])
keysymToKeycodes sym = M.findWithDefault [] sym keysymMap
extraMods <- extraModifiers
pure [ (mask .|. extraMod, keycode)
| (mask, sym) <- ks
, keycode <- keysymToKeycodes sym
, extraMod <- extraMods
]
-- | Release XMonad's keyboard grab, so other grabbers can do their thing.
--
-- Start a keyboard action with this if it is going to run something
-- that needs to do a keyboard, pointer, or server grab. For example,
--
-- > , ((modm .|. controlMask, xK_p), unGrab >> spawn "scrot")
--
-- (Other examples are certain screen lockers and "gksu".)
-- This avoids needing to insert a pause/sleep before running the
-- command.
--
-- XMonad retains the keyboard grab during key actions because if they
-- use a submap, they need the keyboard to be grabbed, and if they had
-- to assert their own grab then the asynchronous nature of X11 allows
-- race conditions between XMonad, other clients, and the X server that
-- would cause keys to sometimes be "leaked" to the focused window.
unGrab :: X ()
unGrab = withDisplay $ \d -> io $ do
ungrabKeyboard d currentTime
ungrabPointer d currentTime
sync d False
------------------------------------------------------------------------
-- Message handling
-- | Throw a message to the current 'LayoutClass' possibly modifying how we
-- layout the windows, in which case changes are handled through a refresh.
sendMessage :: Message a => a -> X ()
sendMessage a = windowBracket_ $ do
w <- gets $ W.workspace . W.current . windowset
ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
whenJust ml' $ \l' ->
modifyWindowSet $ \ws -> ws { W.current = (W.current ws)
{ W.workspace = (W.workspace $ W.current ws)
{ W.layout = l' }}}
return (Any $ isJust ml')
-- | Send a message to all layouts, without refreshing.
broadcastMessage :: Message a => a -> X ()
broadcastMessage a = withWindowSet $ \ws -> do
-- this is O(n²), but we can't really fix this as there's code in
-- xmonad-contrib that touches the windowset during handleMessage
-- (returning Nothing for changes to not get overwritten), so we
-- unfortunately need to do this one by one and persist layout states
-- of each workspace separately)
let c = W.workspace . W.current $ ws
v = map W.workspace . W.visible $ ws
h = W.hidden ws
mapM_ (sendMessageWithNoRefresh a) (c : v ++ h)
-- | Send a message to a layout, without refreshing.
sendMessageWithNoRefresh :: Message a => a -> WindowSpace -> X ()
sendMessageWithNoRefresh a w =
handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing >>=
updateLayout (W.tag w)
-- | Update the layout field of a workspace.
updateLayout :: WorkspaceId -> Maybe (Layout Window) -> X ()
updateLayout i ml = whenJust ml $ \l ->
runOnWorkspaces $ \ww -> return $ if W.tag ww == i then ww { W.layout = l} else ww
-- | Set the layout of the currently viewed workspace.
setLayout :: Layout Window -> X ()
setLayout l = do
ss@W.StackSet{ W.current = c@W.Screen{ W.workspace = ws }} <- gets windowset
handleMessage (W.layout ws) (SomeMessage ReleaseResources)
windows $ const $ ss{ W.current = c{ W.workspace = ws{ W.layout = l } } }
-- | Signal xmonad to restart itself.
sendRestart :: IO ()
sendRestart = do
dpy <- openDisplay ""
rw <- rootWindow dpy $ defaultScreen dpy
xmonad_restart <- internAtom dpy "XMONAD_RESTART" False
allocaXEvent $ \e -> do
setEventType e clientMessage
setClientMessageEvent' e rw xmonad_restart 32 []
sendEvent dpy rw False structureNotifyMask e
sync dpy False
-- | Signal compliant window managers to exit.
sendReplace :: IO ()
sendReplace = do
dpy <- openDisplay ""
let dflt = defaultScreen dpy
rootw <- rootWindow dpy dflt
replace dpy dflt rootw
-- | Signal compliant window managers to exit.
replace :: Display -> ScreenNumber -> Window -> IO ()
replace dpy dflt rootw = do
-- check for other WM
wmSnAtom <- internAtom dpy ("WM_S" ++ show dflt) False
currentWmSnOwner <- xGetSelectionOwner dpy wmSnAtom
when (currentWmSnOwner /= 0) $ do
-- prepare to receive destroyNotify for old WM
selectInput dpy currentWmSnOwner structureNotifyMask
-- create off-screen window
netWmSnOwner <- allocaSetWindowAttributes $ \attributes -> do
set_override_redirect attributes True
set_event_mask attributes propertyChangeMask
let screen = defaultScreenOfDisplay dpy
visual = defaultVisualOfScreen screen
attrmask = cWOverrideRedirect .|. cWEventMask
createWindow dpy rootw (-100) (-100) 1 1 0 copyFromParent copyFromParent visual attrmask attributes
-- try to acquire wmSnAtom, this should signal the old WM to terminate
xSetSelectionOwner dpy wmSnAtom netWmSnOwner currentTime
-- SKIPPED: check if we acquired the selection
-- SKIPPED: send client message indicating that we are now the WM
-- wait for old WM to go away
fix $ \again -> do
evt <- allocaXEvent $ \event -> do
windowEvent dpy currentWmSnOwner structureNotifyMask event
get_EventType event
when (evt /= destroyNotify) again
------------------------------------------------------------------------
-- Utilities
-- | Return workspace visible on screen @sc@, or 'Nothing'.
screenWorkspace :: ScreenId -> X (Maybe WorkspaceId)
screenWorkspace sc = withWindowSet $ return . W.lookupWorkspace sc
-- | Apply an 'X' operation to the currently focused window, if there is one.
withFocused :: (Window -> X ()) -> X ()
withFocused f = withWindowSet $ \w -> whenJust (W.peek w) f
-- | Apply an 'X' operation to all unfocused windows on the current workspace, if there are any.
withUnfocused :: (Window -> X ()) -> X ()
withUnfocused f = withWindowSet $ \ws ->
whenJust (W.peek ws) $ \w ->
let unfocusedWindows = filter (/= w) $ W.index ws
in mapM_ f unfocusedWindows
-- | Is the window is under management by xmonad?
isClient :: Window -> X Bool
isClient w = withWindowSet $ return . W.member w
-- | Combinations of extra modifier masks we need to grab keys\/buttons for.
-- (numlock and capslock)
extraModifiers :: X [KeyMask]
extraModifiers = do
nlm <- gets numberlockMask
return [0, nlm, lockMask, nlm .|. lockMask ]
-- | Strip numlock\/capslock from a mask.
cleanMask :: KeyMask -> X KeyMask
cleanMask km = do
nlm <- gets numberlockMask
return (complement (nlm .|. lockMask) .&. km)
-- | Set the 'Pixel' alpha value to 255.
setPixelSolid :: Pixel -> Pixel
setPixelSolid p = p .|. 0xff000000
-- | Get the 'Pixel' value for a named color.
initColor :: Display -> String -> IO (Maybe Pixel)
initColor dpy c = C.handle (\(C.SomeException _) -> return Nothing) $
Just . setPixelSolid . color_pixel . fst <$> allocNamedColor dpy colormap c
where colormap = defaultColormap dpy (defaultScreen dpy)
------------------------------------------------------------------------
-- | A type to help serialize xmonad's state to a file.
data StateFile = StateFile
{ sfWins :: W.StackSet WorkspaceId String Window ScreenId ScreenDetail
, sfExt :: [(String, String)]
} deriving (Show, Read)
-- | Write the current window state (and extensible state) to a file
-- so that xmonad can resume with that state intact.
writeStateToFile :: X ()
writeStateToFile = do
let maybeShow (t, Right (PersistentExtension ext)) = Just (t, show ext)
maybeShow (t, Left str) = Just (t, str)
maybeShow _ = Nothing
wsData = W.mapLayout show . windowset
extState = mapMaybe maybeShow . M.toList . extensibleState
path <- asks $ stateFileName . directories
stateData <- gets (\s -> StateFile (wsData s) (extState s))
catchIO (writeFile path $ show stateData)
-- | Read the state of a previous xmonad instance from a file and
-- return that state. The state file is removed after reading it.
readStateFile :: (LayoutClass l Window, Read (l Window)) => XConfig l -> X (Maybe XState)
readStateFile xmc = do
path <- asks $ stateFileName . directories
-- I'm trying really hard here to make sure we read the entire
-- contents of the file before it is removed from the file system.
sf' <- userCode . io $ do
raw <- withFile path ReadMode readStrict
return $! maybeRead reads raw
io (removeFile path)
return $ do
sf <- join sf'
let winset = W.ensureTags layout (workspaces xmc) $ W.mapLayout (fromMaybe layout . maybeRead lreads) (sfWins sf)
extState = M.fromList . map (second Left) $ sfExt sf
return XState { windowset = winset
, numberlockMask = 0
, mapped = S.empty
, waitingUnmap = M.empty
, dragging = Nothing
, extensibleState = extState
}
where
layout = Layout (layoutHook xmc)
lreads = readsLayout layout
maybeRead reads' s = case reads' s of
[(x, "")] -> Just x
_ -> Nothing
readStrict :: Handle -> IO String
readStrict h = hGetContents h >>= \s -> length s `seq` return s
-- | @restart name resume@ attempts to restart xmonad by executing the program
-- @name@. If @resume@ is 'True', restart with the current window state.
-- When executing another window manager, @resume@ should be 'False'.
restart :: String -> Bool -> X ()
restart prog resume = do
broadcastMessage ReleaseResources
io . flush =<< asks display
when resume writeStateToFile
catchIO (executeFile prog True [] Nothing)
------------------------------------------------------------------------
-- Floating layer support
-- | Given a window, find the screen it is located on, and compute
-- the geometry of that window WRT that screen.
floatLocation :: Window -> X (ScreenId, W.RationalRect)
floatLocation w =
catchX go $ do
-- Fallback solution if `go' fails. Which it might, since it
-- calls `getWindowAttributes'.
sc <- gets $ W.current . windowset
return (W.screen sc, W.RationalRect 0 0 1 1)
where go = withDisplay $ \d -> do
ws <- gets windowset
sh <- io $ getWMNormalHints d w
wa <- io $ getWindowAttributes d w
let bw = (fromIntegral . wa_border_width) wa
point_sc <- pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
managed <- isClient w
-- ignore pointScreen for new windows unless it's the current
-- screen, otherwise the float's relative size is computed against
-- a different screen and the float ends up with the wrong size
let sr_eq = (==) `on` fmap (screenRect . W.screenDetail)
sc = fromMaybe (W.current ws) $
if managed || point_sc `sr_eq` Just (W.current ws) then point_sc else Nothing
sr = screenRect . W.screenDetail $ sc
x = (fi (wa_x wa) - fi (rect_x sr)) % fi (rect_width sr)
y = (fi (wa_y wa) - fi (rect_y sr)) % fi (rect_height sr)
(width, height) = applySizeHintsContents sh (wa_width wa, wa_height wa)
rwidth = fi (width + bw*2) % fi (rect_width sr)
rheight = fi (height + bw*2) % fi (rect_height sr)
-- adjust x/y of unmanaged windows if we ignored or didn't get pointScreen,
-- it might be out of bounds otherwise
rr = if managed || point_sc `sr_eq` Just sc
then W.RationalRect x y rwidth rheight
else W.RationalRect (0.5 - rwidth/2) (0.5 - rheight/2) rwidth rheight
return (W.screen sc, rr)
fi :: (Integral a, Num b) => a -> b
fi = fromIntegral
-- | Given a point, determine the screen (if any) that contains it.
pointScreen :: Position -> Position
-> X (Maybe (W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail))
pointScreen x y = withWindowSet $ return . find p . W.screens
where p = pointWithin x y . screenRect . W.screenDetail
-- | @pointWithin x y r@ returns 'True' if the @(x, y)@ co-ordinate is within
-- @r@.
pointWithin :: Position -> Position -> Rectangle -> Bool
pointWithin x y r = x >= rect_x r &&
x < rect_x r + fromIntegral (rect_width r) &&
y >= rect_y r &&
y < rect_y r + fromIntegral (rect_height r)
-- | Make a tiled window floating, using its suggested rectangle
float :: Window -> X ()
float w = do
(sc, rr) <- floatLocation w
windows $ \ws -> W.float w rr . fromMaybe ws $ do
i <- W.findTag w ws
guard $ i `elem` map (W.tag . W.workspace) (W.screens ws)
f <- W.peek ws
sw <- W.lookupWorkspace sc ws
return (W.focusWindow f . W.shiftWin sw w $ ws)
-- ---------------------------------------------------------------------
-- Mouse handling
-- | Accumulate mouse motion events
mouseDrag :: (Position -> Position -> X ()) -> X () -> X ()
mouseDrag = mouseDragCursor Nothing
-- | Like 'mouseDrag', but with the ability to specify a custom cursor
-- shape.
mouseDragCursor :: Maybe Glyph -> (Position -> Position -> X ()) -> X () -> X ()
mouseDragCursor cursorGlyph f done = do
drag <- gets dragging
case drag of
Just _ -> return () -- error case? we're already dragging
Nothing -> do
XConf { theRoot = root, display = d } <- ask
io $ do cursor <- maybe (pure none) (createFontCursor d) cursorGlyph
grabPointer d root False (buttonReleaseMask .|. pointerMotionMask)
grabModeAsync grabModeAsync none cursor currentTime
modify $ \s -> s { dragging = Just (motion, cleanup) }
where
cleanup = do
withDisplay $ io . flip ungrabPointer currentTime
modify $ \s -> s { dragging = Nothing }
done
motion x y = do z <- f x y
clearEvents pointerMotionMask
return z
-- | Drag the window under the cursor with the mouse while it is dragged.
mouseMoveWindow :: Window -> X ()
mouseMoveWindow w = whenX (isClient w) $ withDisplay $ \d -> do
wa <- io $ getWindowAttributes d w
(_, _, _, ox', oy', _, _, _) <- io $ queryPointer d w
let ox = fromIntegral ox'
oy = fromIntegral oy'
mouseDragCursor
(Just xC_fleur)
(\ex ey -> do
io $ moveWindow d w (fromIntegral (fromIntegral (wa_x wa) + (ex - ox)))
(fromIntegral (fromIntegral (wa_y wa) + (ey - oy)))
float w
)
(float w)
-- | Resize the window under the cursor with the mouse while it is dragged.
mouseResizeWindow :: Window -> X ()
mouseResizeWindow w = whenX (isClient w) $ withDisplay $ \d -> do
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))
mouseDragCursor
(Just xC_bottom_right_corner)
(\ex ey -> do
io $ resizeWindow d w `uncurry`
applySizeHintsContents sh (ex - fromIntegral (wa_x wa),
ey - fromIntegral (wa_y wa))
float w)
(float w)
-- ---------------------------------------------------------------------
-- Support for window size hints
-- | An alias for a (width, height) pair
type D = (Dimension, Dimension)
-- | Given a window, build an adjuster function that will reduce the given
-- dimensions according to the window's border width and size hints.
mkAdjust :: Window -> X (D -> D)
mkAdjust w = withDisplay $ \d -> liftIO $ do
sh <- getWMNormalHints d w
wa <- C.try $ getWindowAttributes d w
case wa of
Left (_ :: C.SomeException) -> return id
Right wa' ->
let bw = fromIntegral $ wa_border_width wa'
in return $ applySizeHints bw sh
-- | Reduce the dimensions if needed to comply to the given SizeHints, taking
-- window borders into account.
applySizeHints :: Integral a => Dimension -> SizeHints -> (a, a) -> D
applySizeHints bw sh =
tmap (+ 2 * bw) . applySizeHintsContents sh . tmap (subtract $ 2 * fromIntegral bw)
where
tmap f (x, y) = (f x, f y)
-- | Reduce the dimensions if needed to comply to the given SizeHints.
applySizeHintsContents :: Integral a => SizeHints -> (a, a) -> D
applySizeHintsContents sh (w, h) =
applySizeHints' sh (fromIntegral $ max 1 w, fromIntegral $ max 1 h)
-- | Use X11 size hints to scale a pair of dimensions.
applySizeHints' :: SizeHints -> D -> D
applySizeHints' sh =
maybe id applyMaxSizeHint (sh_max_size sh)
. maybe id (\(bw, bh) (w, h) -> (w+bw, h+bh)) (sh_base_size sh)
. maybe id applyResizeIncHint (sh_resize_inc sh)
. maybe id applyAspectHint (sh_aspect sh)
. maybe id (\(bw,bh) (w,h) -> (w-bw, h-bh)) (sh_base_size sh)
-- | Reduce the dimensions so their aspect ratio falls between the two given aspect ratios.
applyAspectHint :: (D, D) -> D -> D
applyAspectHint ((minx, miny), (maxx, maxy)) x@(w,h)
| or [minx < 1, miny < 1, maxx < 1, maxy < 1] = x
| w * maxy > h * maxx = (h * maxx `div` maxy, h)
| w * miny < h * minx = (w, w * miny `div` minx)
| otherwise = x
-- | Reduce the dimensions so they are a multiple of the size increments.
applyResizeIncHint :: D -> D -> D
applyResizeIncHint (iw,ih) x@(w,h) =
if iw > 0 && ih > 0 then (w - w `mod` iw, h - h `mod` ih) else x
-- | Reduce the dimensions if they exceed the given maximum dimensions.
applyMaxSizeHint :: D -> D -> D
applyMaxSizeHint (mw,mh) x@(w,h) =
if mw > 0 && mh > 0 then (min w mw,min h mh) else x

View File

@@ -1,8 +1,9 @@
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE DeriveFunctor #-}
-----------------------------------------------------------------------------
-- |
-- Module : StackSet
-- Module : XMonad.StackSet
-- Copyright : (c) Don Stewart 2007
-- License : BSD3-style (see LICENSE)
--
@@ -11,28 +12,38 @@
-- Portability : portable, Haskell 98
--
module StackSet (
module XMonad.StackSet (
-- * Introduction
-- $intro
StackSet(..), Workspace(..), Screen(..), StackOrNot, Stack(..), RationalRect(..),
-- ** The Zipper
-- $zipper
-- ** Xinerama support
-- $xinerama
-- ** Master and Focus
-- $focus
StackSet(..), Workspace(..), Screen(..), Stack(..), RationalRect(..),
-- * Construction
-- $construction
new, view, greedyView,
-- * Xinerama operations
-- $xinerama
lookupWorkspace,
screens, workspaces, allWindows,
screens, workspaces, allWindows, currentTag,
-- * Operations on the current stack
-- $stackOperations
peek, index, integrate, integrate', differentiate,
focusUp, focusDown, focusMaster, focusWindow,
tagMember, renameTag, ensureTags, member, findIndex, mapWorkspace, mapLayout,
focusUp, focusDown, focusUp', focusDown', focusMaster, focusWindow,
tagMember, renameTag, ensureTags, member, findTag, mapWorkspace, mapLayout,
-- * Modifying the stackset
-- $modifyStackset
insertUp, delete, delete', filter,
-- * Setting the master window
-- $settingMW
swapUp, swapDown, swapMaster, modify, modify', float, sink, -- needed by users
swapUp, swapDown, swapMaster, shiftMaster, modify, modify', float, sink, -- needed by users
-- * Composite operations
-- $composite
shift, shiftWin,
@@ -42,9 +53,13 @@ module StackSet (
) where
import Prelude hiding (filter)
import Data.Maybe (listToMaybe,fromJust)
import Control.Applicative.Backwards (Backwards (Backwards, forwards))
import Data.Foldable (foldr, toList)
import Data.Maybe (listToMaybe,isJust,fromMaybe)
import qualified Data.List as L (deleteBy,find,splitAt,filter,nub)
import Data.List ( (\\) )
import qualified Data.List.NonEmpty as NE
import Data.List.NonEmpty (NonEmpty((:|)))
import qualified Data.Map as M (Map,insert,delete,empty)
-- $intro
@@ -65,8 +80,8 @@ import qualified Data.Map as M (Map,insert,delete,empty)
-- Note that workspaces are indexed from 0, windows are numbered
-- uniquely. A '*' indicates the window on each workspace that has
-- focus, and which workspace is current.
--
-- Zipper
-- $zipper
--
-- We encode all the focus tracking directly in the data structure, with a 'zipper':
--
@@ -75,75 +90,45 @@ import qualified Data.Map as M (Map,insert,delete,empty)
-- continuation reified as a data structure.
--
-- The Zipper lets us replace an item deep in a complex data
-- structure, e.g., a tree or a term, without an mutation. The
-- structure, e.g., a tree or a term, without a mutation. The
-- resulting data structure will share as much of its components with
-- the old structure as possible.
--
-- Oleg Kiselyov, 27 Apr 2005, haskell\@, "Zipper as a delimited continuation"
-- <https://mail.haskell.org/pipermail/haskell/2005-April/015769.html Oleg Kiselyov, 27 Apr 2005, haskell\@, "Zipper as a delimited continuation">
--
-- We use the zipper to keep track of the focused workspace and the
-- focused window on each workspace, allowing us to have correct focus
-- by construction. We closely follow Huet's original implementation:
--
-- G. Huet, /Functional Pearl: The Zipper/,
-- 1997, J. Functional Programming 75(5):549-554.
-- and:
-- R. Hinze and J. Jeuring, /Functional Pearl: The Web/.
-- <https://www.st.cs.uni-saarland.de/edu/seminare/2005/advanced-fp/docs/huet-zipper.pdf G. Huet, Functional Pearl: The Zipper; 1997, J. Functional Programming 75(5):549554>
--
-- and Conor McBride's zipper differentiation paper.
-- Another good reference is:
-- and
--
-- The Zipper, Haskell wikibook
-- <https://dspace.library.uu.nl/handle/1874/2532 R. Hinze and J. Jeuring, Functional Pearl: Weaving a Web>
--
-- Xinerama support:
-- and
--
-- <http://strictlypositive.org/diff.pdf Conor McBride, The Derivative of a Regular Type is its Type of One-Hole Contexts>.
--
-- Another good reference is: <https://wiki.haskell.org/Zipper The Zipper, Haskell wikibook>
-- $xinerama
-- Xinerama in X11 lets us view multiple virtual workspaces
-- simultaneously. While only one will ever be in focus (i.e. will
-- receive keyboard events), other workspaces may be passively viewable.
-- We thus need to track which virtual workspaces are associated
-- (viewed) on which physical screens. We use a simple Map Workspace
-- Screen for this.
--
-- Master and Focus
-- receive keyboard events), other workspaces may be passively
-- viewable. We thus need to track which virtual workspaces are
-- associated (viewed) on which physical screens. To keep track of
-- this, 'StackSet' keeps separate lists of visible but non-focused
-- workspaces, and non-visible workspaces.
-- $focus
--
-- Each stack tracks a focused item, and for tiling purposes also tracks
-- a 'master' position. The connection between 'master' and 'focus'
-- needs to be well defined. Particular in relation to 'insert' and
-- needs to be well defined, particularly in relation to 'insert' and
-- 'delete'.
--
-- |
-- API changes from xmonad 0.1:
-- StackSet constructor arguments changed. StackSet workspace window screen
--
-- * new, -- was: empty
--
-- * view,
--
-- * index,
--
-- * peek, -- was: peek\/peekStack
--
-- * focusUp, focusDown, -- was: rotate
--
-- * swapUp, swapDown
--
-- * focus -- was: raiseFocus
--
-- * insertUp, -- was: insert\/push
--
-- * delete,
--
-- * swapMaster, -- was: promote\/swap
--
-- * member,
--
-- * shift,
--
-- * lookupWorkspace, -- was: workspace
--
-- * visibleWorkspaces -- gone.
--
------------------------------------------------------------------------
-- |
-- A cursor into a non-empty list of workspaces.
@@ -156,8 +141,8 @@ import qualified Data.Map as M (Map,insert,delete,empty)
data StackSet i l a sid sd =
StackSet { current :: !(Screen i l a sid sd) -- ^ currently focused workspace
, visible :: [Screen i l a sid sd] -- ^ non-focused workspaces, visible in xinerama
, hidden :: [Workspace i l a] -- ^ workspaces not visible anywhere
, floating :: M.Map a RationalRect -- ^ floating windows
, hidden :: [Workspace i l a] -- ^ workspaces not visible anywhere
, floating :: M.Map a RationalRect -- ^ floating windows
} deriving (Show, Read, Eq)
-- | Visible workspaces, and their Xinerama screens.
@@ -167,17 +152,17 @@ data Screen i l a sid sd = Screen { workspace :: !(Workspace i l a)
deriving (Show, Read, Eq)
-- |
-- A workspace is just a tag - its index - and a stack
-- A workspace is just a tag, a layout, and a stack.
--
data Workspace i l a = Workspace { tag :: !i, layout :: l, stack :: StackOrNot a }
data Workspace i l a = Workspace { tag :: !i, layout :: l, stack :: Maybe (Stack a) }
deriving (Show, Read, Eq)
-- | A structure for window geometries
data RationalRect = RationalRect Rational Rational Rational Rational
data RationalRect = RationalRect !Rational !Rational !Rational !Rational
deriving (Show, Read, Eq)
-- |
-- A stack is a cursor onto a (possibly empty) window list.
-- A stack is a cursor onto a window list.
-- The data structure tracks focus by construction, and
-- the master window is by convention the top-most item.
-- Focus operations will not reorder the list that results from
@@ -194,13 +179,22 @@ data RationalRect = RationalRect Rational Rational Rational Rational
-- structures, it is the differentiation of a [a], and integrating it
-- back has a natural implementation used in 'index'.
--
type StackOrNot a = Maybe (Stack a)
data Stack a = Stack { focus :: !a -- focused thing in this set
, up :: [a] -- clowns to the left
, down :: [a] } -- jokers to the right
deriving (Show, Read, Eq)
deriving (Show, Read, Eq, Functor)
instance Foldable Stack where
toList = integrate
foldr f z = foldr f z . toList
instance Traversable Stack where
traverse f s =
flip Stack
-- 'Backwards' applies the Applicative in reverse order.
<$> forwards (traverse (Backwards . f) (up s))
<*> f (focus s)
<*> traverse f (down s)
-- | this function indicates to catch that an error is expected
abort :: String -> a
@@ -209,22 +203,26 @@ abort x = error $ "xmonad: StackSet: " ++ x
-- ---------------------------------------------------------------------
-- $construction
-- | /O(n)/. Create a new stackset, of empty stacks, with given tags, with
-- 'm' physical screens. 'm' should be less than or equal to the number of
-- workspace tags. The first workspace in the list will be current.
-- | /O(n)/. Create a new stackset, of empty stacks, with given tags,
-- with physical screens whose descriptions are given by 'm'. The
-- number of physical screens (@length 'm'@) should be less than or
-- equal to the number of workspace tags. The first workspace in the
-- list will be current.
--
-- Xinerama: Virtual workspaces are assigned to physical screens, starting at 0.
--
new :: (Integral s) => l -> [i] -> [sd] -> StackSet i l a s sd
new l wids m | not (null wids) && length m <= length wids = StackSet cur visi unseen M.empty
where (seen,unseen) = L.splitAt (length m) $ map (\i -> Workspace i l Nothing) wids
(cur:visi) = [ Screen i s sd | (i, s, sd) <- zip3 seen [0..] m ]
new l (wid:wids) (m:ms) | length ms <= length wids
= StackSet cur visi (map ws unseen) M.empty
where ws i = Workspace i l Nothing
(seen, unseen) = L.splitAt (length ms) wids
cur:visi = Screen (ws wid) 0 m : [ Screen (ws i) s sd | (i, s, sd) <- zip3 seen [1..] ms ]
-- now zip up visibles with their screen id
new _ _ _ = abort "non-positive argument to StackSet.new"
-- |
-- /O(w)/. Set focus to the workspace with index \'i\'.
-- If the index is out of range, return the original StackSet.
-- If the index is out of range, return the original 'StackSet'.
--
-- Xinerama: If the workspace is not visible on any Xinerama screen, it
-- becomes the current screen. If it is in the visible list, it becomes
@@ -232,8 +230,7 @@ new _ _ _ = abort "non-positive argument to StackSet.new"
view :: (Eq s, Eq i) => i -> StackSet i l a s sd -> StackSet i l a s sd
view i s
| not (i `tagMember` s)
|| i == tag (workspace (current s)) = s -- out of bounds or current
| i == currentTag s = s -- current
| Just x <- L.find ((i==).tag.workspace) (visible s)
-- if it is visible, it is just raised
@@ -244,9 +241,9 @@ view i s
= s { current = (current s) { workspace = x }
, hidden = workspace (current s) : L.deleteBy (equating tag) x (hidden s) }
| otherwise = s -- can't happen: all workspaces are either invalid, current, visible, or hidden
| otherwise = s -- not a member of the stackset
where equating f = \x y -> f x == f y
where equating f x y = f x == f y
-- 'Catch'ing this might be hard. Relies on monotonically increasing
-- workspace tags defined in 'new'
@@ -275,7 +272,7 @@ greedyView w ws
-- $xinerama
-- | Find the tag of the workspace visible on Xinerama screen 'sc'.
-- Nothing if screen is out of bounds.
-- 'Nothing' if screen is out of bounds.
lookupWorkspace :: Eq s => s -> StackSet i l a s sd -> Maybe i
lookupWorkspace sc w = listToMaybe [ tag i | Screen i s _ <- current w : visible w, s == sc ]
@@ -292,9 +289,9 @@ with :: b -> (Stack a -> b) -> StackSet i l a s sd -> b
with dflt f = maybe dflt f . stack . workspace . current
-- |
-- Apply a function, and a default value for Nothing, to modify the current stack.
-- Apply a function, and a default value for 'Nothing', to modify the current stack.
--
modify :: StackOrNot a -> (Stack a -> StackOrNot a) -> StackSet i l a s sd -> StackSet i l a s sd
modify :: Maybe (Stack a) -> (Stack a -> Maybe (Stack a)) -> StackSet i l a s sd -> StackSet i l a s sd
modify d f s = s { current = (current s)
{ workspace = (workspace (current s)) { stack = with d f s }}}
@@ -307,34 +304,35 @@ modify' f = modify Nothing (Just . f)
-- |
-- /O(1)/. Extract the focused element of the current stack.
-- Return Just that element, or Nothing for an empty stack.
-- Return 'Just' that element, or 'Nothing' for an empty stack.
--
peek :: StackSet i l a s sd -> Maybe a
peek = with Nothing (return . focus)
-- |
-- /O(n)/. Flatten a Stack into a list.
-- /O(n)/. Flatten a 'Stack' into a list.
--
integrate :: Stack a -> [a]
integrate (Stack x l r) = reverse l ++ x : r
-- |
-- /O(n)/ Flatten a possibly empty stack into a list.
integrate' :: StackOrNot a -> [a]
-- /O(n)/. Flatten a possibly empty stack into a list.
integrate' :: Maybe (Stack a) -> [a]
integrate' = maybe [] integrate
-- |
-- /O(n)/. Texture a list.
--
differentiate :: [a] -> StackOrNot a
-- /O(n)/. Turn a list into a possibly empty stack (i.e., a zipper):
-- the first element of the list is current, and the rest of the list
-- is down.
differentiate :: [a] -> Maybe (Stack a)
differentiate [] = Nothing
differentiate (x:xs) = Just $ Stack x [] xs
-- |
-- /O(n)/. 'filter p s' returns the elements of 's' such that 'p' evaluates to
-- True. Order is preserved, and focus moves as described for 'delete'.
-- 'True'. Order is preserved, and focus moves as described for 'delete'.
--
filter :: (a -> Bool) -> Stack a -> StackOrNot a
filter :: (a -> Bool) -> Stack a -> Maybe (Stack a)
filter p (Stack f ls rs) = case L.filter p (f:rs) of
f':rs' -> Just $ Stack f' (L.filter p ls) rs' -- maybe move focus down
[] -> case L.filter p ls of -- filter back up
@@ -350,31 +348,45 @@ filter p (Stack f ls rs) = case L.filter p (f:rs) of
index :: StackSet i l a s sd -> [a]
index = with [] integrate
-- let is = t : r ++ reverse l in take (length is) (dropWhile (/= m) (cycle is))
-- |
-- /O(1), O(w) on the wrapping case/.
--
-- focusUp, focusDown. Move the window focus up or down the stack,
-- wrapping if we reach the end. The wrapping should model a 'cycle'
-- on the current stack. The 'master' window, and window order,
-- | /O(1), O(w) on the wrapping case/. Move the window focus up the
-- stack, wrapping if we reach the end. The wrapping should model a
-- @cycle@ on the current stack. The @master@ window and window order
-- are unaffected by movement of focus.
--
-- swapUp, swapDown, swap the neighbour in the stack ordering, wrapping
-- if we reach the end. Again the wrapping model should 'cycle' on
-- the current stack.
--
focusUp, focusDown, swapUp, swapDown :: StackSet i l a s sd -> StackSet i l a s sd
focusUp :: StackSet i l a s sd -> StackSet i l a s sd
focusUp = modify' focusUp'
focusDown = modify' (reverseStack . focusUp' . reverseStack)
-- | /O(1), O(w) on the wrapping case/. Like 'focusUp', but move the
-- window focus down the stack.
focusDown :: StackSet i l a s sd -> StackSet i l a s sd
focusDown = modify' focusDown'
-- | /O(1), O(w) on the wrapping case/. Swap the upwards (left)
-- neighbour in the stack ordering, wrapping if we reach the end. Much
-- like for 'focusUp' and 'focusDown', the wrapping model should 'cycle'
-- on the current stack.
swapUp :: StackSet i l a s sd -> StackSet i l a s sd
swapUp = modify' swapUp'
-- | /O(1), O(w) on the wrapping case/. Like 'swapUp', but for swapping
-- the downwards (right) neighbour.
swapDown :: StackSet i l a s sd -> StackSet i l a s sd
swapDown = modify' (reverseStack . swapUp' . reverseStack)
focusUp', swapUp' :: Stack a -> Stack a
-- | A variant of 'focusUp' with the same asymptotics that works on a
-- 'Stack' rather than an entire 'StackSet'.
focusUp' :: Stack a -> Stack a
focusUp' (Stack t (l:ls) rs) = Stack l ls (t:rs)
focusUp' (Stack t [] rs) = Stack x xs [] where (x:xs) = reverse (t:rs)
focusUp' (Stack t [] rs) = Stack x xs []
where (x :| xs) = NE.reverse (t :| rs)
-- | A variant of 'focusDown' with the same asymptotics that works on a
-- 'Stack' rather than an entire 'StackSet'.
focusDown' :: Stack a -> Stack a
focusDown' = reverseStack . focusUp' . reverseStack
-- | A variant of 'spawUp' with the same asymptotics that works on a
-- 'Stack' rather than an entire 'StackSet'.
swapUp' :: Stack a -> Stack a
swapUp' (Stack t (l:ls) rs) = Stack t ls (l:rs)
swapUp' (Stack t [] rs) = Stack t (reverse rs) []
@@ -388,32 +400,38 @@ reverseStack (Stack t ls rs) = Stack t rs ls
--
focusWindow :: (Eq s, Eq a, Eq i) => a -> StackSet i l a s sd -> StackSet i l a s sd
focusWindow w s | Just w == peek s = s
| otherwise = maybe s id $ do
n <- findIndex w s
| otherwise = fromMaybe s $ do
n <- findTag w s
return $ until ((Just w ==) . peek) focusUp (view n s)
-- | Get a list of all screens in the StackSet.
-- | Get a list of all screens in the 'StackSet'.
screens :: StackSet i l a s sd -> [Screen i l a s sd]
screens s = current s : visible s
-- | Get a list of all workspaces in the StackSet.
-- | Get a list of all workspaces in the 'StackSet'.
workspaces :: StackSet i l a s sd -> [Workspace i l a]
workspaces s = workspace (current s) : map workspace (visible s) ++ hidden s
-- | Get a list of all windows in the StackSet in no particular order
-- | Get a list of all windows in the 'StackSet' in no particular order
allWindows :: Eq a => StackSet i l a s sd -> [a]
allWindows = L.nub . concatMap (integrate' . stack) . workspaces
-- | Is the given tag present in the StackSet?
-- | Get the tag of the currently focused workspace.
currentTag :: StackSet i l a s sd -> i
currentTag = tag . workspace . current
-- | Is the given tag present in the 'StackSet'?
tagMember :: Eq i => i -> StackSet i l a s sd -> Bool
tagMember t = elem t . map tag . workspaces
-- | Rename a given tag if present in the StackSet.
-- | Rename a given tag if present in the 'StackSet'.
renameTag :: Eq i => i -> i -> StackSet i l a s sd -> StackSet i l a s sd
renameTag o n = mapWorkspace rename
where rename w = if tag w == o then w { tag = n } else w
-- | Ensure that a given set of tags is present.
-- | Ensure that a given set of workspace tags is present by renaming
-- existing workspaces and\/or creating new hidden workspaces as
-- necessary.
ensureTags :: Eq i => l -> [i] -> StackSet i l a s sd -> StackSet i l a s sd
ensureTags l allt st = et allt (map tag (workspaces st) \\ allt) st
where et [] _ s = s
@@ -421,29 +439,29 @@ ensureTags l allt st = et allt (map tag (workspaces st) \\ allt) st
et (i:is) [] s = et is [] (s { hidden = Workspace i l Nothing : hidden s })
et (i:is) (r:rs) s = et is rs $ renameTag r i s
-- | Map a function on all the workspaces in the StackSet.
-- | Map a function on all the workspaces in the 'StackSet'.
mapWorkspace :: (Workspace i l a -> Workspace i l a) -> StackSet i l a s sd -> StackSet i l a s sd
mapWorkspace f s = s { current = updScr (current s)
, visible = map updScr (visible s)
, hidden = map f (hidden s) }
where updScr scr = scr { workspace = f (workspace scr) }
-- | Map a function on all the layouts in the StackSet.
-- | Map a function on all the layouts in the 'StackSet'.
mapLayout :: (l -> l') -> StackSet i l a s sd -> StackSet i l' a s sd
mapLayout f (StackSet v vs hs m) = StackSet (fScreen v) (map fScreen vs) (map fWorkspace hs) m
where
fScreen (Screen ws s sd) = Screen (fWorkspace ws) s sd
fWorkspace (Workspace t l s) = Workspace t (f l) s
-- | /O(n)/. Is a window in the StackSet.
-- | /O(n)/. Is a window in the 'StackSet'?
member :: Eq a => a -> StackSet i l a s sd -> Bool
member a s = maybe False (const True) (findIndex a s)
member a s = isJust (findTag a s)
-- | /O(1) on current window, O(n) in general/.
-- Return Just the workspace index of the given window, or Nothing
-- if the window is not in the StackSet.
findIndex :: Eq a => a -> StackSet i l a s sd -> Maybe i
findIndex a s = listToMaybe
-- Return 'Just' the workspace tag of the given window, or 'Nothing'
-- if the window is not in the 'StackSet'.
findTag :: Eq a => a -> StackSet i l a s sd -> Maybe i
findTag a s = listToMaybe
[ tag w | w <- workspaces s, has a (stack w) ]
where has _ Nothing = False
has x (Just (Stack t l r)) = x `elem` (t : l ++ r)
@@ -452,12 +470,10 @@ findIndex a s = listToMaybe
-- $modifyStackset
-- |
-- /O(n)/. (Complexity due to duplicate check). Insert a new element into
-- the stack, above the currently focused element.
--
-- The new element is given focus, and is set as the master window.
-- The previously focused element is moved down. The previously
-- 'master' element is forgotten. (Thus, 'insert' will cause a retiling).
-- /O(n)/. (Complexity due to duplicate check). Insert a new element
-- into the stack, above the currently focused element. The new
-- element is given focus; the previously focused element is moved
-- down.
--
-- If the element is already in the stackset, the original stackset is
-- returned unmodified.
@@ -478,22 +494,26 @@ insertUp a s = if member a s then s else insert
-- /O(1) on current window, O(n) in general/. Delete window 'w' if it exists.
-- There are 4 cases to consider:
--
-- * delete on an Nothing workspace leaves it Nothing
-- * delete on an 'Nothing' workspace leaves it Nothing
--
-- * otherwise, try to move focus to the down
--
-- * otherwise, try to move focus to the up
-- * otherwise, you've got an empty workspace, becomes Nothing
--
-- * otherwise, you've got an empty workspace, becomes 'Nothing'
--
-- Behaviour with respect to the master:
--
-- * deleting the master window resets it to the newly focused window
--
-- * otherwise, delete doesn't affect the master.
--
delete :: (Ord a, Eq s) => a -> StackSet i l a s sd -> StackSet i l a s sd
delete :: (Ord a) => a -> StackSet i l a s sd -> StackSet i l a s sd
delete w = sink w . delete' w
-- | Only temporarily remove the window from the stack, thereby not destroying special
-- information saved in the Stackset
delete' :: (Eq a, Eq s) => a -> StackSet i l a s sd -> StackSet i l a s sd
-- information saved in the 'Stackset'
delete' :: (Eq a) => a -> StackSet i l a s sd -> StackSet i l a s sd
delete' w s = s { current = removeFromScreen (current s)
, visible = map removeFromScreen (visible s)
, hidden = map removeFromWorkspace (hidden s) }
@@ -503,7 +523,7 @@ delete' w s = s { current = removeFromScreen (current s)
------------------------------------------------------------------------
-- | Given a window, and its preferred rectangle, set it as floating
-- A floating window should already be managed by the StackSet.
-- A floating window should already be managed by the 'StackSet'.
float :: Ord a => a -> RationalRect -> StackSet i l a s sd -> StackSet i l a s sd
float w r s = s { floating = M.insert w r (floating s) }
@@ -519,16 +539,25 @@ sink w s = s { floating = M.delete w (floating s) }
-- Focus stays with the item moved.
swapMaster :: StackSet i l a s sd -> StackSet i l a s sd
swapMaster = modify' $ \c -> case c of
Stack _ [] _ -> c -- already master.
Stack t ls rs -> Stack t [] (xs ++ x : rs) where (x:xs) = reverse ls
Stack _ [] _ -> c -- already master.
Stack t (l:ls) rs -> Stack t [] (xs ++ x : rs) where (x :| xs) = NE.reverse (l :| ls)
-- natural! keep focus, move current to the top, move top to current.
-- | /O(s)/. Set the master window to the focused window.
-- The other windows are kept in order and shifted down on the stack, as if you
-- just hit mod-shift-k a bunch of times.
-- Focus stays with the item moved.
shiftMaster :: StackSet i l a s sd -> StackSet i l a s sd
shiftMaster = modify' $ \c -> case c of
Stack _ [] _ -> c -- already master.
Stack t ls rs -> Stack t [] (reverse ls ++ rs)
-- | /O(s)/. Set focus to the master window.
focusMaster :: StackSet i l a s sd -> StackSet i l a s sd
focusMaster = modify' $ \c -> case c of
Stack _ [] _ -> c
Stack t ls rs -> Stack x [] (xs ++ t : rs) where (x:xs) = reverse ls
Stack _ [] _ -> c
Stack t (l:ls) rs -> Stack x [] (xs ++ t : rs) where (x :| xs) = NE.reverse (l :| ls)
--
-- ---------------------------------------------------------------------
@@ -541,10 +570,7 @@ focusMaster = modify' $ \c -> case c of
-- element on the current stack, the original stackSet is returned.
--
shift :: (Ord a, Eq s, Eq i) => i -> StackSet i l a s sd -> StackSet i l a s sd
shift n s | n `tagMember` s && n /= curtag = maybe s go (peek s)
| otherwise = s
where go w = view curtag . insertUp w . view n . delete' w $ s
curtag = tag (workspace (current s))
shift n s = maybe s (\w -> shiftWin n w s) (peek s)
-- | /O(n)/. shiftWin. Searches for the specified window 'w' on all workspaces
-- of the stackSet and moves it to stack 'n', leaving it as the focused
@@ -552,14 +578,12 @@ shift n s | n `tagMember` s && n /= curtag = maybe s go (peek s)
-- focused element on that workspace.
-- The actual focused workspace doesn't change. If the window is not
-- found in the stackSet, the original stackSet is returned.
-- TODO how does this duplicate 'shift's behaviour?
shiftWin :: (Ord a, Eq a, Eq s, Eq i) => i -> a -> StackSet i l a s sd -> StackSet i l a s sd
shiftWin n w s | from == Nothing = s -- not found
| n `tagMember` s && (Just n) /= from = go
| otherwise = s
where from = findIndex w s
go = on n (insertUp w) . on (fromJust from) (delete' w) $ s
curtag = tag (workspace (current s))
on i f = view curtag . f . view i
shiftWin :: (Ord a, Eq s, Eq i) => i -> a -> StackSet i l a s sd -> StackSet i l a s sd
shiftWin n w s = case findTag w s of
Just from | n `tagMember` s && n /= from -> go from s
_ -> s
where go from = onWorkspace n (insertUp w) . onWorkspace from (delete' w)
onWorkspace :: (Eq i, Eq s) => i -> (StackSet i l a s sd -> StackSet i l a s sd)
-> (StackSet i l a s sd -> StackSet i l a s sd)
onWorkspace n f s = view (currentTag s) . f . view n $ s

15
stack.yaml Normal file
View File

@@ -0,0 +1,15 @@
resolver: lts-21.12
packages:
- ./
extra-deps:
- X11-1.10
nix:
packages:
- zlib
- xorg.libX11
- xorg.libXrandr
- xorg.libXScrnSaver
- xorg.libXext

140
tests/Instances.hs Normal file
View File

@@ -0,0 +1,140 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Instances where
import Test.QuickCheck
import Utils
import XMonad.StackSet
import Control.Monad
import Data.List (nub, genericLength)
import Debug.Trace
import Graphics.X11 (Rectangle(Rectangle))
import Control.Applicative
--
-- The all important Arbitrary instance for StackSet.
--
instance (Integral i, Integral s, Eq a, Arbitrary a, Arbitrary l, Arbitrary sd)
=> Arbitrary (StackSet i l a s sd) where
arbitrary = do
-- TODO: Fix this to be a reasonable higher number, Possibly use PositiveSized
numWs <- choose (1, 20) -- number of workspaces, there must be at least 1.
numScreens <- choose (1, numWs) -- number of physical screens, there must be at least 1
lay <- arbitrary -- pick any layout
wsIdxInFocus <- choose (1, numWs) -- pick index of WS to be in focus
-- The same screen id's will be present in the list, with high possibility.
screens <- replicateM numScreens arbitrary
-- Generate a list of "windows" for each workspace.
wsWindows <- vector numWs :: Gen [[a]]
-- Pick a random window "number" in each workspace, to give focus.
focus <- sequence [ if null windows
then return Nothing
else Just <$> choose (0, length windows - 1)
| windows <- wsWindows ]
let tags = [1 .. fromIntegral numWs]
focusWsWindows = zip focus wsWindows
wss = zip tags focusWsWindows -- tmp representation of a workspace (tag, windows)
initSs = new lay tags screens
return $
view (fromIntegral wsIdxInFocus) $
foldr (\(tag, (focus, windows)) ss -> -- Fold through all generated (tags,windows).
-- set workspace active by tag and fold through all
-- windows while inserting them. Apply the given number
-- of `focusUp` on the resulting StackSet.
applyN focus focusUp $ foldr insertUp (view tag ss) windows
) initSs wss
--
-- Just generate StackSets with Char elements.
--
type Tag = Int
type Window = Char
type T = StackSet Tag Int Window Int Int
newtype EmptyStackSet = EmptyStackSet T
deriving Show
instance Arbitrary EmptyStackSet where
arbitrary = do
(NonEmptyNubList ns) <- arbitrary
(NonEmptyNubList sds) <- arbitrary
l <- arbitrary
-- there cannot be more screens than workspaces:
return . EmptyStackSet . new l ns $ take (min (length ns) (length sds)) sds
newtype NonEmptyWindowsStackSet = NonEmptyWindowsStackSet T
deriving Show
instance Arbitrary NonEmptyWindowsStackSet where
arbitrary =
NonEmptyWindowsStackSet <$> (arbitrary `suchThat` (not . null . allWindows))
instance Arbitrary Rectangle where
arbitrary = Rectangle <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
newtype SizedPositive = SizedPositive Int
deriving (Eq, Ord, Show, Read)
instance Arbitrary SizedPositive where
arbitrary = sized $ \s -> do x <- choose (1, max 1 s)
return $ SizedPositive x
newtype NonEmptyNubList a = NonEmptyNubList [a]
deriving ( Eq, Ord, Show, Read )
instance (Eq a, Arbitrary a) => Arbitrary (NonEmptyNubList a) where
arbitrary = NonEmptyNubList <$> ((nub <$> arbitrary) `suchThat` (not . null))
-- | Pull out an arbitrary tag from the StackSet. This removes the need for the
-- precondition "n `tagMember x` in many properties and thus reduces the number
-- of discarded tests.
--
-- n <- arbitraryTag x
--
-- We can do the reverse with a simple `suchThat`:
--
-- n <- arbitrary `suchThat` \n' -> not $ n' `tagMember` x
arbitraryTag :: T -> Gen Tag
arbitraryTag x = do
let ts = tags x
-- There must be at least 1 workspace, thus at least 1 tag.
idx <- choose (0, length ts - 1)
return $ ts!!idx
-- | Pull out an arbitrary window from a StackSet that is guaranteed to have a
-- non empty set of windows. This eliminates the precondition "i `member` x" in
-- a few properties.
--
--
-- foo (nex :: NonEmptyWindowsStackSet) = do
-- let NonEmptyWindowsStackSet x = nex
-- w <- arbitraryWindow nex
-- return $ .......
--
-- We can do the reverse with a simple `suchThat`:
--
-- n <- arbitrary `suchThat` \n' -> not $ n `member` x
arbitraryWindow :: NonEmptyWindowsStackSet -> Gen Window
arbitraryWindow (NonEmptyWindowsStackSet x) = do
let ws = allWindows x
-- We know that there are at least 1 window in a NonEmptyWindowsStackSet.
idx <- choose (0, length ws - 1)
return $ ws!!idx

View File

@@ -1,8 +0,0 @@
module Main where
import qualified Properties
-- This will run all of the QC files for xmonad core. Currently, that's just
-- Properties. If any more get added, sequence the main actions together.
main = do
Properties.main

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,70 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Properties.Delete where
import Test.QuickCheck
import Instances
import Utils
import XMonad.StackSet hiding (filter)
-- ---------------------------------------------------------------------
-- 'delete'
-- deleting the current item removes it.
prop_delete x =
case peek x of
Nothing -> True
Just i -> not (member i (delete i x))
where _ = x :: T
-- delete is reversible with 'insert'.
-- It is the identity, except for the 'master', which is reset on insert and delete.
--
prop_delete_insert (x :: T) =
case peek x of
Nothing -> True
Just n -> insertUp n (delete n y) == y
where
y = swapMaster x
-- delete should be local
prop_delete_local (x :: T) =
case peek x of
Nothing -> True
Just i -> hidden_spaces x == hidden_spaces (delete i x)
-- delete should not affect focus unless the focused element is what is being deleted
prop_delete_focus = do
-- There should be at least two windows. One in focus, and some to try and
-- delete (doesn't have to be windows on the current workspace). We generate
-- our own, since we can't rely on NonEmptyWindowsStackSet returning one in
-- the argument with at least two windows.
x <- arbitrary `suchThat` \x' -> length (allWindows x') >= 2
w <- arbitraryWindow (NonEmptyWindowsStackSet x)
-- Make sure we pick a window that is NOT the currently focused
`suchThat` \w' -> Just w' /= peek x
return $ peek (delete w x) == peek x
-- focus movement in the presence of delete:
-- when the last window in the stack set is focused, focus moves `up'.
-- usual case is that it moves 'down'.
prop_delete_focus_end = do
-- Generate a StackSet with at least two windows on the current workspace.
x <- arbitrary `suchThat` \(x' :: T) -> length (index x') >= 2
let w = last (index x)
y = focusWindow w x -- focus last window in stack
return $ peek (delete w y) == peek (focusUp y)
-- focus movement in the presence of delete:
-- when not in the last item in the stack, focus moves down
prop_delete_focus_not_end = do
x <- arbitrary
-- There must be at least two windows and the current focused is not the
-- last one in the stack.
`suchThat` \(x' :: T) ->
let currWins = index x'
in length currWins >= 2 && peek x' /= Just (last currWins)
-- This is safe, as we know there are >= 2 windows
let Just n = peek x
return $ peek (delete n x) == peek (focusDown x)

View File

@@ -0,0 +1,30 @@
module Properties.Failure where
import XMonad.StackSet hiding (filter)
import qualified Control.Exception as C
import System.IO.Unsafe
import Data.List (isPrefixOf)
-- ---------------------------------------------------------------------
-- testing for failure and help out hpc
--
-- Since base 4.9.0.0 `error` appends a stack trace. The tests below
-- use `isPrefixOf` to only test equality on the error message.
--
prop_abort :: Int -> Bool
prop_abort _ = unsafePerformIO $ C.catch (abort "fail") check
where
check (C.SomeException e) =
return $ "xmonad: StackSet: fail" `isPrefixOf` show e
-- new should fail with an abort
prop_new_abort :: Int -> Bool
prop_new_abort _ = unsafePerformIO $ C.catch f check
where
f = new undefined{-layout-} [] [] `seq` return False
check (C.SomeException e) =
return $ "xmonad: StackSet: non-positive argument to StackSet.new" `isPrefixOf` show e
-- TODO: Fix this?
-- prop_view_should_fail = view {- with some bogus data -}

View File

@@ -0,0 +1,36 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Properties.Floating where
import Test.QuickCheck
import Instances
import XMonad.StackSet hiding (filter)
import qualified Data.Map as M
------------------------------------------------------------------------
-- properties for the floating layer:
prop_float_reversible (nex :: NonEmptyWindowsStackSet) = do
let NonEmptyWindowsStackSet x = nex
w <- arbitraryWindow nex
return $ sink w (float w geom x) == x
where
geom = RationalRect 100 100 100 100
prop_float_geometry (nex :: NonEmptyWindowsStackSet) = do
let NonEmptyWindowsStackSet x = nex
w <- arbitraryWindow nex
let s = float w geom x
return $ M.lookup w (floating s) == Just geom
where
geom = RationalRect 100 100 100 100
prop_float_delete (nex :: NonEmptyWindowsStackSet) = do
let NonEmptyWindowsStackSet x = nex
w <- arbitraryWindow nex
let s = float w geom x
t = delete w s
return $ not (w `member` t)
where
geom = RationalRect 100 100 100 100

74
tests/Properties/Focus.hs Normal file
View File

@@ -0,0 +1,74 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Properties.Focus where
import Test.QuickCheck
import Instances
import Utils
import XMonad.StackSet hiding (filter)
import Data.Maybe (fromJust)
-- ---------------------------------------------------------------------
-- rotating focus
--
-- master/focus
--
-- The tiling order, and master window, of a stack is unaffected by focus changes.
--
prop_focus_left_master (SizedPositive n) (x::T) =
index (applyN (Just n) focusUp x) == index x
prop_focus_right_master (SizedPositive n) (x::T) =
index (applyN (Just n) focusDown x) == index x
prop_focus_master_master (SizedPositive n) (x::T) =
index (applyN (Just n) focusMaster x) == index x
prop_focusWindow_master (NonNegative n) (x :: T) =
case peek x of
Nothing -> True
Just _ -> let s = index x
i = n `mod` length s
in index (focusWindow (s !! i) x) == index x
-- shifting focus is trivially reversible
prop_focus_left (x :: T) = focusUp (focusDown x) == x
prop_focus_right (x :: T) = focusDown (focusUp x) == x
-- focus master is idempotent
prop_focusMaster_idem (x :: T) = focusMaster x == focusMaster (focusMaster x)
-- focusWindow actually leaves the window focused...
prop_focusWindow_works (NonNegative (n :: Int)) (x :: T) =
case peek x of
Nothing -> True
Just _ -> let s = index x
i = fromIntegral n `mod` length s
in (focus . fromJust . stack . workspace . current) (focusWindow (s !! i) x) == (s !! i)
-- rotation through the height of a stack gets us back to the start
prop_focus_all_l (x :: T) = foldr (const focusUp) x [1..n] == x
where n = length (index x)
prop_focus_all_r (x :: T) = foldr (const focusDown) x [1..n] == x
where n = length (index x)
-- prop_rotate_all (x :: T) = f (f x) == f x
-- f x' = foldr (\_ y -> rotate GT y) x' [1..n]
-- focus is local to the current workspace
prop_focus_down_local (x :: T) = hidden_spaces (focusDown x) == hidden_spaces x
prop_focus_up_local (x :: T) = hidden_spaces (focusUp x) == hidden_spaces x
prop_focus_master_local (x :: T) = hidden_spaces (focusMaster x) == hidden_spaces x
prop_focusWindow_local (NonNegative (n :: Int)) (x::T ) =
case peek x of
Nothing -> True
Just _ -> let s = index x
i = fromIntegral n `mod` length s
in hidden_spaces (focusWindow (s !! i) x) == hidden_spaces x
-- On an invalid window, the stackset is unmodified
prop_focusWindow_identity (x::T ) = do
n <- arbitrary `suchThat` \n' -> not $ n' `member` x
return $ focusWindow n x == x

View File

@@ -0,0 +1,44 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Properties.GreedyView where
import Test.QuickCheck
import Instances
import Utils
import XMonad.StackSet hiding (filter)
import Data.List (sortBy)
-- ---------------------------------------------------------------------
-- greedyViewing workspaces
-- greedyView sets the current workspace to 'n'
prop_greedyView_current (x :: T) = do
n <- arbitraryTag x
return $ currentTag (greedyView n x) == n
-- greedyView leaves things unchanged for invalid workspaces
prop_greedyView_current_id (x :: T) = do
n <- arbitrary `suchThat` \n' -> not $ n' `tagMember` x
return $ currentTag (greedyView n x) == currentTag x
-- greedyView *only* sets the current workspace, and touches Xinerama.
-- no workspace contents will be changed.
prop_greedyView_local (x :: T) = do
n <- arbitraryTag x
return $ workspaces x == workspaces (greedyView n x)
where
workspaces a = sortBy (\s t -> tag s `compare` tag t) $
workspace (current a)
: map workspace (visible a) ++ hidden a
-- greedyView is idempotent
prop_greedyView_idem (x :: T) = do
n <- arbitraryTag x
return $ greedyView n (greedyView n x) == greedyView n x
-- greedyView is reversible, though shuffles the order of hidden/visible
prop_greedyView_reversible (x :: T) = do
n <- arbitraryTag x
return $ normal (greedyView n' (greedyView n x)) == normal x
where n' = currentTag x

View File

@@ -0,0 +1,52 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Properties.Insert where
import Test.QuickCheck
import Instances
import Utils
import XMonad.StackSet hiding (filter)
import Data.List (nub)
-- ---------------------------------------------------------------------
-- 'insert'
-- inserting a item into an empty stackset means that item is now a member
prop_insert_empty i (EmptyStackSet x)= member i (insertUp i x)
-- insert should be idempotent
prop_insert_idem i (x :: T) = insertUp i x == insertUp i (insertUp i x)
-- insert when an item is a member should leave the stackset unchanged
prop_insert_duplicate (nex :: NonEmptyWindowsStackSet) = do
let NonEmptyWindowsStackSet x = nex
w <- arbitraryWindow nex
return $ insertUp w x == x
-- push shouldn't change anything but the current workspace
prop_insert_local (x :: T) = do
i <- arbitrary `suchThat` \i' -> not $ i' `member` x
return $ hidden_spaces x == hidden_spaces (insertUp i x)
-- Inserting a (unique) list of items into an empty stackset should
-- result in the last inserted element having focus.
prop_insert_peek (EmptyStackSet x) (NonEmptyNubList is) =
peek (foldr insertUp x is) == Just (head is)
-- insert >> delete is the identity, when i `notElem` .
-- Except for the 'master', which is reset on insert and delete.
--
prop_insert_delete x = do
n <- arbitrary `suchThat` \n -> not $ n `member` x
return $ delete n (insertUp n y) == (y :: T)
where
y = swapMaster x -- sets the master window to the current focus.
-- otherwise, we don't have a rule for where master goes.
-- inserting n elements increases current stack size by n
prop_size_insert is (EmptyStackSet x) =
size (foldr insertUp x ws) == length ws
where
ws = nub is
size = length . index

View File

@@ -0,0 +1,34 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Properties.Layout.Full where
import Test.QuickCheck
import Instances
import XMonad.StackSet hiding (filter)
import XMonad.Core
import XMonad.Layout
import Data.Maybe
------------------------------------------------------------------------
-- Full layout
-- pureLayout works for Full
prop_purelayout_full rect = do
x <- (arbitrary :: Gen T) `suchThat` (isJust . peek)
let layout = Full
st = fromJust . stack . workspace . current $ x
ts = pureLayout layout rect st
return $
length ts == 1 -- only one window to view
&&
snd (head ts) == rect -- and sets fullscreen
&&
fst (head ts) == fromJust (peek x) -- and the focused window is shown
-- what happens when we send an IncMaster message to Full --- Nothing
prop_sendmsg_full (NonNegative k) =
isNothing (Full `pureMessage` SomeMessage (IncMasterN k))
prop_desc_full = description Full == show Full

View File

@@ -0,0 +1,140 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Properties.Layout.Tall where
import Test.QuickCheck
import Instances
import Utils
import XMonad.StackSet hiding (filter)
import XMonad.Core
import XMonad.Layout
import Graphics.X11.Xlib.Types (Rectangle(..))
import Control.Applicative
import Data.List (sort)
import Data.Maybe
import Data.Ratio
------------------------------------------------------------------------
-- The Tall layout
-- 1 window should always be tiled fullscreen
prop_tile_fullscreen rect = tile pct rect 1 1 == [rect]
where pct = 1/2
-- multiple windows
prop_tile_non_overlap rect windows nmaster = noOverlaps (tile pct rect nmaster windows)
where _ = rect :: Rectangle
pct = 3 % 100
-- with a ratio of 1, no stack windows are drawn of there is at least
-- one master window around.
prop_tile_max_ratio = extremeRatio 1 drop
-- with a ratio of 0, no master windows are drawn at all if there are
-- any stack windows around.
prop_tile_min_ratio = extremeRatio 0 take
extremeRatio amount getRects rect = do
w@(NonNegative windows) <- arbitrary `suchThat` (> NonNegative 0)
NonNegative nmaster <- arbitrary `suchThat` (< w)
let tiled = tile amount rect nmaster windows
pure $ if nmaster == 0
then prop_tile_non_overlap rect windows nmaster
else all ((== 0) . rect_width) $ getRects nmaster tiled
-- splitting horizontally yields sensible results
prop_split_horizontal (NonNegative n) x =
noOverflows (+) (rect_x x) (rect_width x) ==>
sum (map rect_width xs) == rect_width x
&&
all (\s -> rect_height s == rect_height x) xs
&&
map rect_x xs == sort (map rect_x xs)
where
xs = splitHorizontally n x
-- splitting vertically yields sensible results
prop_split_vertical (r :: Rational) x =
rect_x x == rect_x a && rect_x x == rect_x b
&&
rect_width x == rect_width a && rect_width x == rect_width b
where
(a,b) = splitVerticallyBy r x
-- pureLayout works.
prop_purelayout_tall n d r rect = do
x <- (arbitrary :: Gen T) `suchThat` (isJust . peek)
let layout = Tall n d r
st = fromJust . stack . workspace . current $ x
ts = pureLayout layout rect st
ntotal = length (index x)
return $
(if r == 0 then
-- (<=) for Bool is the logical implication
(0 <= n && n <= ntotal) <= (length ts == ntotal - n)
else if r == 1 then
(0 <= n && n <= ntotal) <= (length ts == n)
else
length ts == ntotal)
&&
noOverlaps (map snd ts)
&&
description layout == "Tall"
-- Test message handling of Tall
-- what happens when we send a Shrink message to Tall
prop_shrink_tall (NonNegative n) (Positive delta) (NonNegative frac) =
n == n' && delta == delta' -- these state components are unchanged
&& frac' <= frac && (if frac' < frac then frac' == 0 || frac' == frac - delta
else frac == 0 )
-- remaining fraction should shrink
where
l1 = Tall n delta frac
Just l2@(Tall n' delta' frac') = l1 `pureMessage` SomeMessage Shrink
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
-- what happens when we send a Shrink message to Tall
prop_expand_tall (NonNegative n)
(Positive delta)
(NonNegative n1)
(Positive d1) =
n == n'
&& delta == delta' -- these state components are unchanged
&& frac' >= frac
&& (if frac' > frac
then frac' == 1 || frac' == frac + delta
else frac == 1 )
-- remaining fraction should shrink
where
frac = min 1 (n1 % d1)
l1 = Tall n delta frac
Just l2@(Tall n' delta' frac') = l1 `pureMessage` SomeMessage Expand
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
-- what happens when we send an IncMaster message to Tall
prop_incmaster_tall (NonNegative n) (Positive delta) (NonNegative frac)
(NonNegative k) =
delta == delta' && frac == frac' && n' == n + k
where
l1 = Tall n delta frac
Just l2@(Tall n' delta' frac') = l1 `pureMessage` SomeMessage (IncMasterN k)
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
-- toMessage LT = SomeMessage Shrink
-- toMessage EQ = SomeMessage Expand
-- toMessage GT = SomeMessage (IncMasterN 1)
prop_desc_mirror n r1 r2 = description (Mirror $! t) == "Mirror Tall"
where t = Tall n r1 r2

View File

@@ -0,0 +1,72 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Properties.Screen where
import Utils
import Test.QuickCheck
import Instances
import Control.Applicative
import XMonad.StackSet hiding (filter)
import XMonad.Operations
import Graphics.X11.Xlib.Types (Dimension)
import Graphics.X11 (Rectangle(Rectangle))
import XMonad.Layout
prop_screens (x :: T) = n `elem` screens x
where
n = current x
-- screens makes sense
prop_screens_works (x :: T) = screens x == current x : visible x
------------------------------------------------------------------------
-- Hints
prop_resize_inc (Positive inc_w,Positive inc_h) b@(w,h) =
w' `mod` inc_w == 0 && h' `mod` inc_h == 0
where (w',h') = applyResizeIncHint a b
a = (inc_w,inc_h)
prop_resize_inc_extra ((NonNegative inc_w)) b@(w,h) =
(w,h) == (w',h')
where (w',h') = applyResizeIncHint a b
a = (-inc_w,0::Dimension)-- inc_h)
prop_resize_max (Positive inc_w,Positive inc_h) b@(w,h) =
w' <= inc_w && h' <= inc_h
where (w',h') = applyMaxSizeHint a b
a = (inc_w,inc_h)
prop_resize_max_extra ((NonNegative inc_w)) b@(w,h) =
(w,h) == (w',h')
where (w',h') = applyMaxSizeHint a b
a = (-inc_w,0::Dimension)-- inc_h)
prop_aspect_hint_shrink hint (w,h) = case applyAspectHint hint (w,h) of
(w',h') -> w' <= w && h' <= h
-- applyAspectHint does nothing when the supplied (x,y) fits
-- the desired range
prop_aspect_fits =
forAll ((,,,) <$> pos <*> pos <*> pos <*> pos) $ \ (x,y,a,b) ->
let f = applyAspectHint ((x, y+a), (x+b, y))
in noOverflows (*) x (y+a) && noOverflows (*) (x+b) y
==> f (x,y) == (x,y)
where pos = choose (0, 65535)
prop_point_within r@(Rectangle x y w h) =
forAll ((,) <$>
choose (0, fromIntegral w - 1) <*>
choose (0, fromIntegral h - 1)) $
\(dx,dy) ->
and [ dx > 0, dy > 0,
noOverflows (\ a b -> a + abs b) x w,
noOverflows (\ a b -> a + abs b) y h ]
==> pointWithin (x+dx) (y+dy) r
prop_point_within_mirror r (x,y) = pointWithin x y r == pointWithin y x (mirrorRect r)

70
tests/Properties/Shift.hs Normal file
View File

@@ -0,0 +1,70 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Properties.Shift where
import Test.QuickCheck
import Instances
import Utils
import XMonad.StackSet hiding (filter)
import qualified Data.List as L
-- ---------------------------------------------------------------------
-- shift
-- shift is fully reversible on current window, when focus and master
-- are the same. otherwise, master may move.
prop_shift_reversible (x :: T) = do
i <- arbitraryTag x
case peek y of
Nothing -> return True
Just _ -> return $ normal ((view n . shift n . view i . shift i) y) == normal y
where
y = swapMaster x
n = currentTag y
------------------------------------------------------------------------
-- shiftMaster
-- focus/local/idempotent same as swapMaster:
prop_shift_master_focus (x :: T) = peek x == peek (shiftMaster x)
prop_shift_master_local (x :: T) = hidden_spaces x == hidden_spaces (shiftMaster x)
prop_shift_master_idempotent (x :: T) = shiftMaster (shiftMaster x) == shiftMaster x
-- ordering is constant modulo the focused window:
prop_shift_master_ordering (x :: T) = case peek x of
Nothing -> True
Just m -> L.delete m (index x) == L.delete m (index $ shiftMaster x)
-- ---------------------------------------------------------------------
-- shiftWin
-- shiftWin on current window is the same as shift
prop_shift_win_focus (x :: T) = do
n <- arbitraryTag x
case peek x of
Nothing -> return True
Just w -> return $ shiftWin n w x == shift n x
-- shiftWin on a non-existent window is identity
prop_shift_win_indentity (x :: T) = do
n <- arbitraryTag x
w <- arbitrary `suchThat` \w' -> not (w' `member` x)
return $ shiftWin n w x == x
-- shiftWin leaves the current screen as it is, if neither n is the tag
-- of the current workspace nor w on the current workspace
prop_shift_win_fix_current = do
x <- arbitrary `suchThat` \(x' :: T) ->
-- Invariant, otherWindows are NOT in the current workspace.
let otherWindows = allWindows x' L.\\ index x'
in length (tags x') >= 2 && not (null otherWindows)
-- Sadly we have to construct `otherWindows` again, for the actual StackSet
-- that got chosen.
let otherWindows = allWindows x L.\\ index x
-- We know such tag must exists, due to the precondition
n <- arbitraryTag x `suchThat` (/= currentTag x)
-- we know length is >= 1, from above precondition
idx <- choose (0, length otherWindows - 1)
let w = otherWindows !! idx
return $ current x == current (shiftWin n w x)

77
tests/Properties/Stack.hs Normal file
View File

@@ -0,0 +1,77 @@
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Properties.Stack where
import Test.QuickCheck
import Instances
import XMonad.StackSet hiding (filter)
import qualified XMonad.StackSet as S (filter)
import Data.Maybe
import Data.Proxy
import Test.QuickCheck.Classes (
Laws (lawsTypeclass, lawsProperties), Proxy1 (Proxy1),
foldableLaws, traversableLaws,
)
-- The list returned by index should be the same length as the actual
-- windows kept in the zipper
prop_index_length (x :: T) =
case stack . workspace . current $ x of
Nothing -> null (index x)
Just it -> length (index x) == length (focus it : up it ++ down it)
-- For all windows in the stackSet, findTag should identify the
-- correct workspace
prop_findIndex (x :: T) =
and [ tag w == fromJust (findTag i x)
| w <- workspace (current x) : map workspace (visible x) ++ hidden x
, t <- maybeToList (stack w)
, i <- focus t : up t ++ down t
]
prop_allWindowsMember (NonEmptyWindowsStackSet x) = do
-- Reimplementation of arbitraryWindow, but to make sure that
-- implementation doesn't change in the future, and stop using allWindows,
-- which is a key component in this test (together with member).
let ws = allWindows x
-- We know that there are at least 1 window in a NonEmptyWindowsStackSet.
idx <- choose (0, length ws - 1)
return $ member (ws!!idx) x
-- preserve order
prop_filter_order (x :: T) =
case stack $ workspace $ current x of
Nothing -> True
Just s@(Stack i _ _) -> integrate' (S.filter (/= i) s) == filter (/= i) (integrate' (Just s))
-- differentiate should return Nothing if the list is empty or Just stack, with
-- the first element of the list is current, and the rest of the list is down.
prop_differentiate xs =
if null xs then isNothing (differentiate xs)
else differentiate xs == Just (Stack (head xs) [] (tail xs))
where _ = xs :: [Int]
-- Check type class laws of 'Data.Foldable.Foldable' and 'Data.Traversable.Traversable'.
newtype TestStack a = TestStack (Stack a)
deriving (Eq, Read, Show, Foldable, Functor)
instance (Arbitrary a) => Arbitrary (TestStack a) where
arbitrary = TestStack <$> (Stack <$> arbitrary <*> arbitrary <*> arbitrary)
shrink = traverse shrink
instance Traversable TestStack where
traverse f (TestStack sx) = fmap TestStack (traverse f sx)
prop_laws_Stack = format (foldableLaws p) <> format (traversableLaws p)
where
p = Proxy :: Proxy TestStack
format laws = [ ("Stack: " <> lawsTypeclass laws <> ": " <> name, prop)
| (name, prop) <- lawsProperties laws ]

View File

@@ -0,0 +1,135 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Properties.StackSet where
import Test.QuickCheck
import Instances
import Utils
import XMonad.StackSet hiding (filter)
import Data.Maybe
import Data.List (nub)
-- ---------------------------------------------------------------------
-- QuickCheck properties for the StackSet
-- Some general hints for creating StackSet properties:
--
-- * ops that mutate the StackSet are usually local
-- * most ops on StackSet should either be trivially reversible, or
-- idempotent, or both.
------------------------------------------------------------------------
-- Basic data invariants of the StackSet
--
-- With the new zipper-based StackSet, tracking focus is no longer an
-- issue: the data structure enforces focus by construction.
--
-- But we still need to ensure there are no duplicates, and master/and
-- the xinerama mapping aren't checked by the data structure at all.
--
-- * no element should ever appear more than once in a StackSet
-- * the xinerama screen map should be:
-- -- keys should always index valid workspaces
-- -- monotonically ascending in the elements
-- * the current workspace should be a member of the xinerama screens
--
invariant (s :: T) = and
-- no duplicates
[ noDuplicates
-- TODO: Fix this.
-- all this xinerama stuff says we don't have the right structure
-- , validScreens
-- , validWorkspaces
-- , inBounds
]
where
ws = concat [ focus t : up t ++ down t
| w <- workspace (current s) : map workspace (visible s) ++ hidden s
, t <- maybeToList (stack w)] :: [Char]
noDuplicates = nub ws == ws
-- validScreens = monotonic . sort . M. . (W.current s : W.visible : W$ s
-- validWorkspaces = and [ w `elem` allworkspaces | w <- (M.keys . screens) s ]
-- where allworkspaces = map tag $ current s : prev s ++ next s
-- inBounds = and [ w >=0 && w < size s | (w,sc) <- M.assocs (screens s) ]
monotonic [] = True
monotonic [x] = True
monotonic (x:y:zs) | x == y-1 = monotonic (y:zs)
| otherwise = False
prop_invariant = invariant
-- and check other ops preserve invariants
prop_empty_I (SizedPositive n) l = forAll (choose (1, fromIntegral n)) $ \m ->
forAll (vector m) $ \ms ->
invariant $ new l [0..fromIntegral n-1] ms
prop_view_I n (x :: T) =
invariant $ view n x
prop_greedyView_I n (x :: T) =
invariant $ greedyView n x
prop_focusUp_I (SizedPositive n) (x :: T) =
invariant $ applyN (Just n) focusUp x
prop_focusMaster_I (SizedPositive n) (x :: T) =
invariant $ applyN (Just n) focusMaster x
prop_focusDown_I (SizedPositive n) (x :: T) =
invariant $ applyN (Just n) focusDown x
prop_focus_I (SizedPositive n) (x :: T) =
case peek x of
Nothing -> True
Just _ -> let w = focus . fromJust . stack . workspace . current $
applyN (Just n) focusUp x
in invariant $ focusWindow w x
prop_insertUp_I n (x :: T) = invariant $ insertUp n x
prop_delete_I (x :: T) = invariant $
case peek x of
Nothing -> x
Just i -> delete i x
prop_swap_master_I (x :: T) = invariant $ swapMaster x
prop_swap_left_I (SizedPositive n) (x :: T) =
invariant $ applyN (Just n) swapUp x
prop_swap_right_I (SizedPositive n) (x :: T) =
invariant $ applyN (Just n) swapDown x
prop_shift_I (x :: T) = do
n <- arbitraryTag x
return $ invariant $ shift (fromIntegral n) x
prop_shift_win_I (nex :: NonEmptyWindowsStackSet) = do
let NonEmptyWindowsStackSet x = nex
w <- arbitraryWindow nex
n <- arbitraryTag x
return $ invariant $ shiftWin n w x
-- ---------------------------------------------------------------------
-- empty StackSets have no windows in them
prop_empty (EmptyStackSet x) =
all (== Nothing) [ stack w | w <- workspace (current x)
: map workspace (visible x) ++ hidden x ]
-- empty StackSets always have focus on first workspace
prop_empty_current (EmptyStackSet x) = currentTag x == head (tags x)
-- no windows will be a member of an empty workspace
prop_member_empty i (EmptyStackSet x) = not (member i x)
-- peek either yields nothing on the Empty workspace, or Just a valid window
prop_member_peek (x :: T) =
case peek x of
Nothing -> True {- then we don't know anything -}
Just i -> member i x

47
tests/Properties/Swap.hs Normal file
View File

@@ -0,0 +1,47 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Properties.Swap where
import Test.QuickCheck
import Instances
import Utils
import XMonad.StackSet hiding (filter)
-- ---------------------------------------------------------------------
-- swapUp, swapDown, swapMaster: reordiring windows
-- swap is trivially reversible
prop_swap_left (x :: T) = swapUp (swapDown x) == x
prop_swap_right (x :: T) = swapDown (swapUp x) == x
-- TODO swap is reversible
-- swap is reversible, but involves moving focus back the window with
-- master on it. easy to do with a mouse...
{-
prop_promote_reversible x b = (not . null . fromMaybe [] . flip index x . current $ x) ==>
(raiseFocus y . promote . raiseFocus z . promote) x == x
where _ = x :: T
dir = if b then LT else GT
(Just y) = peek x
(Just (z:_)) = flip index x . current $ x
-}
-- swap doesn't change focus
prop_swap_master_focus (x :: T) = peek x == peek (swapMaster x)
-- = case peek x of
-- Nothing -> True
-- Just f -> focus (stack (workspace $ current (swap x))) == f
prop_swap_left_focus (x :: T) = peek x == peek (swapUp x)
prop_swap_right_focus (x :: T) = peek x == peek (swapDown x)
-- swap is local
prop_swap_master_local (x :: T) = hidden_spaces x == hidden_spaces (swapMaster x)
prop_swap_left_local (x :: T) = hidden_spaces x == hidden_spaces (swapUp x)
prop_swap_right_local (x :: T) = hidden_spaces x == hidden_spaces (swapDown x)
-- rotation through the height of a stack gets us back to the start
prop_swap_all_l (x :: T) = foldr (const swapUp) x [1..n] == x
where n = length (index x)
prop_swap_all_r (x :: T) = foldr (const swapDown) x [1..n] == x
where n = length (index x)
prop_swap_master_idempotent (x :: T) = swapMaster (swapMaster x) == swapMaster x

47
tests/Properties/View.hs Normal file
View File

@@ -0,0 +1,47 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Properties.View where
import Test.QuickCheck
import Instances
import Utils
import XMonad.StackSet hiding (filter)
import Data.List (sortBy)
-- ---------------------------------------------------------------------
-- viewing workspaces
-- view sets the current workspace to 'n'
prop_view_current (x :: T) = do
n <- arbitraryTag x
return $ (tag . workspace . current . view n) x == n
-- view *only* sets the current workspace, and touches Xinerama.
-- no workspace contents will be changed.
prop_view_local (x :: T) = do
n <- arbitraryTag x
return $ workspaces x == workspaces (view n x)
where
workspaces a = sortBy (\s t -> tag s `compare` tag t) $
workspace (current a)
: map workspace (visible a) ++ hidden a
-- TODO: Fix this
-- view should result in a visible xinerama screen
-- prop_view_xinerama (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
-- M.member i (screens (view i x))
-- where
-- i = fromIntegral n
-- view is idempotent
prop_view_idem (x :: T) = do
n <- arbitraryTag x
return $ view n (view n x) == view n x
-- view is reversible, though shuffles the order of hidden/visible
prop_view_reversible (x :: T) = do
n <- arbitraryTag x
return $ normal (view n' (view n x)) == normal x
where
n' = currentTag x

View File

@@ -0,0 +1,65 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Properties.Workspace where
import Test.QuickCheck
import Instances
import Utils
import XMonad.StackSet hiding (filter)
import Data.Maybe
-- looking up the tag of the current workspace should always produce a tag.
prop_lookup_current (x :: T) = lookupWorkspace scr x == Just tg
where
(Screen (Workspace tg _ _) scr _) = current x
-- looking at a visible tag
prop_lookup_visible = do
-- make sure we have some xinerama screens.
x <- arbitrary `suchThat` \(x' :: T) -> visible x' /= []
let tags = [ tag (workspace y) | y <- visible x ]
scr = last [ screen y | y <- visible x ]
return $ fromJust (lookupWorkspace scr x) `elem` tags
prop_currentTag (x :: T) =
currentTag x == tag (workspace (current x))
-- Rename a given tag if present in the StackSet.
prop_rename1 (x::T) = do
o <- arbitraryTag x
n <- arbitrary `suchThat` \n' -> not $ n' `tagMember` x
-- Rename o to n
let y = renameTag o n x
return $ n `tagMember` y
-- Ensure that a given set of workspace tags is present by renaming
-- existing workspaces and\/or creating new hidden workspaces as
-- necessary.
--
prop_ensure (x :: T) l xs = let y = ensureTags l xs x
in and [ n `tagMember` y | n <- xs ]
-- adding a tag should create a new hidden workspace
prop_ensure_append (x :: T) l = do
n <- arbitrary `suchThat` \n' -> not $ n' `tagMember` x
let ts = tags x
y = ensureTags l (n:ts) x
return $ hidden y /= hidden x -- doesn't append, renames
&& and [ isNothing (stack z) && layout z == l | z <- hidden y, tag z == n ]
prop_mapWorkspaceId (x::T) = x == mapWorkspace id x
prop_mapWorkspaceInverse (x::T) = x == mapWorkspace predTag (mapWorkspace succTag x)
where predTag w = w { tag = pred $ tag w }
succTag w = w { tag = succ $ tag w }
prop_mapLayoutId (x::T) = x == mapLayout id x
prop_mapLayoutInverse (x::T) = x == mapLayout pred (mapLayout succ x)

47
tests/Utils.hs Normal file
View File

@@ -0,0 +1,47 @@
{-# LANGUAGE RankNTypes #-}
module Utils where
import XMonad.StackSet hiding (filter)
import Graphics.X11.Xlib.Types (Rectangle(..))
import Data.List (sortBy)
-- Useful operation, the non-local workspaces
hidden_spaces x = map workspace (visible x) ++ hidden x
-- normalise workspace list
normal s = s { hidden = sortBy g (hidden s), visible = sortBy f (visible s) }
where
f a b = tag (workspace a) `compare` tag (workspace b)
g a b = tag a `compare` tag b
noOverlaps [] = True
noOverlaps [_] = True
noOverlaps xs = and [ verts a `notOverlap` verts b
| a <- xs
, b <- filter (a /=) xs
]
where
verts (Rectangle a b w h) = (a,b,a + fromIntegral w - 1, b + fromIntegral h - 1)
notOverlap (left1,bottom1,right1,top1)
(left2,bottom2,right2,top2)
= (top1 < bottom2 || top2 < bottom1)
|| (right1 < left2 || right2 < left1)
applyN :: (Integral n) => Maybe n -> (a -> a) -> a -> a
applyN Nothing f v = v
applyN (Just 0) f v = v
applyN (Just n) f v = applyN (Just $ n-1) f (f v)
tags x = map tag $ workspaces x
-- | noOverflows op a b is True if @a `op` fromIntegral b@ overflows (or
-- otherwise gives the same answer when done using Integer
noOverflows :: (Integral b, Integral c) =>
(forall a. Integral a => a -> a -> a) -> b -> c -> Bool
noOverflows op a b = toInteger (a `op` fromIntegral b) == toInteger a `op` toInteger b

View File

@@ -5,9 +5,9 @@ main = do foo <- getContents
let actual_loc = filter (not.null) $ filter isntcomment $
map (dropWhile (==' ')) $ lines foo
loc = length actual_loc
putStrLn $ show loc
print loc
-- uncomment the following to check for mistakes in isntcomment
-- putStr $ unlines $ actual_loc
-- print actual_loc
isntcomment ('-':'-':_) = False
isntcomment ('{':'-':_) = False -- pragmas

92
util/GenerateManpage.hs Normal file → Executable file
View File

@@ -1,47 +1,73 @@
#!/usr/bin/env runhaskell
-- Reads markdown (man/xmonad.1.markdown) from stdin, substitutes
-- ___KEYBINDINGS___ for key-binding definitions generated from
-- src/XMonad/Config.hs, prints result to stdout.
--
-- Generates man/xmonad.1 from man/xmonad.1.in by filling the list of
-- keybindings with values scraped from Config.hs
--
-- Format for the docstrings in Config.hs takes the following form:
--
-- -- mod-x %! Frob the whatsit
--
-- "Frob the whatsit" will be used as the description for keybinding "mod-x"
--
-- If the keybinding name is omitted, it will try to guess from the rest of the
-- line. For example:
--
-- [ ((modMask .|. shiftMask, xK_Return), spawn "xterm") -- %! Launch an xterm
--
-- Here, mod-shift-return will be used as the keybinding name.
--
import Control.Monad
import Text.Regex.Posix
-- Unlike the rest of xmonad, this file is released under the GNU General
-- Public License version 2 or later. (Historical reasons, used to link with
-- GPL-licensed pandoc.)
import Data.Char
import Data.List
trim :: String -> String
trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace
main :: IO ()
main = do
keybindings <- guessBindings
interact $ unlines . replace "___KEYBINDINGS___" keybindings . lines
guessKeys line = concat $ intersperse "-" (modifiers ++ [map toLower key])
where modifiers = map (!!1) (line =~ "(mod|shift|control)Mask")
(_, _, _, [key]) = line =~ "xK_(\\w+)" :: (String, String, String, [String])
-- | The format for the docstrings in "Config.hs" takes the following form:
--
-- @
-- -- mod-x %! Frob the whatsit
-- @
--
-- "Frob the whatsit" will be used as the description for keybinding "mod-x".
-- If the name of the key binding is omitted, the function tries to guess it
-- from the rest of the line. For example:
--
-- @
-- [ ((modMask .|. shiftMask, xK_Return), spawn "xterm") -- %! Launch an xterm
-- @
--
-- Here, "mod-shift-return" will be used as the key binding name.
binding :: [String] -> (String, String)
binding [ _, bindingLine, "", desc ] = (guessKeys bindingLine, desc)
binding [ _, _, keyCombo, desc ] = (keyCombo, desc)
guessBindings :: IO String
guessBindings = do
buf <- readFile "./src/XMonad/Config.hs"
return (intercalate "\n\n" (map markdownDefn (allBindings buf)))
allBindings :: String -> [(String, String)]
allBindings xs = map (binding . map trim) (xs =~ "(.*)--(.*)%!(.*)")
allBindings = concatMap parseLine . lines
where
parseLine :: String -> [(String, String)]
parseLine l
| " -- " `isInfixOf` l
, Just d <- parseDesc l = [(intercalate "-" (parseKeys l), d)]
| otherwise = []
parseDesc :: String -> Maybe String
parseDesc = fmap (trim . drop 4) . find (" %! " `isPrefixOf`) . tails
parseKeys :: String -> [String]
parseKeys l = case lex l of
[("", _)] -> []
[("--", rest)] -> case words rest of
k : "%!" : _ -> [k]
_ -> []
[(k, rest)] -> parseKey k ++ parseKeys rest
parseKey :: String -> [String]
parseKey k | "Mask" `isSuffixOf` k = [reverse (drop 4 (reverse k))]
| "xK_" `isPrefixOf` k = [map toLower (drop 3 k)]
| otherwise = []
-- FIXME: What escaping should we be doing on these strings?
troff :: (String, String) -> String
troff (key, desc) = ".IP\n \\fB" ++ key ++ "\\fR\n" ++ desc ++ "\n"
markdownDefn :: (String, String) -> String
markdownDefn (key, desc) = key ++ "\n: " ++ desc
replace :: Eq a => a -> a -> [a] -> [a]
replace x y = map (\a -> if a == x then y else a)
main = do
troffBindings <- (concatMap troff . allBindings) `liftM` readFile "./Config.hs"
let sed = unlines . replace "___KEYBINDINGS___" troffBindings . lines
readFile "./man/xmonad.1.in" >>= return . sed >>= writeFile "./man/xmonad.1"
trim :: String -> String
trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace

33
util/hpcReport.sh Normal file
View File

@@ -0,0 +1,33 @@
#!/bin/bash
set -e
if [[ ! ( -e xmonad.cabal && -e dist/hpc/tix/properties/properties.tix ) ]]; then
echo "run in the same dir as xmonad.cabal after having run
cabal configure --enable-tests --enable-library-coverage; cabal test
"
exit 1
fi
propsExclude=$(find tests/Properties -name '*.hs' \
| sed -e 's_/_._g' -e 's_.hs$__' -e 's_^tests._--exclude=_' )
hpcFlags="
--hpcdir=dist/hpc/mix/
dist/hpc/tix/properties/properties.tix
"
if [[ ! (-e dist/hpc/mix/Main.mix) ]]; then
mv dist/hpc/mix/properties/* dist/hpc/mix/
mv dist/hpc/mix/xmonad-*/xmonad-*/* dist/hpc/mix/xmonad-*/
fi
hpc markup --destdir=dist/hpc $hpcFlags > /dev/null
echo "see dist/hpc/hpc_index.html
"
hpc report $hpcFlags

View File

@@ -1,30 +1,136 @@
name: xmonad
version: 0.4
homepage: http://xmonad.org
synopsis: A lightweight X11 window manager.
description:
xmonad is a tiling window manager for X. Windows are arranged
automatically to tile the screen without gaps or overlap, maximising
screen use. All features of the window manager are accessible from
the keyboard: a mouse is strictly optional. xmonad is written and
extensible in Haskell. Custom layout algorithms, and other
extensions, may be written by the user in config files. Layouts are
applied dynamically, and different layouts may be used on each
workspace. Xinerama is fully supported, allowing windows to be tiled
on several screens.
category: System
version: 0.18.0.9
synopsis: A tiling window manager
description: xmonad is a tiling window manager for X. Windows are arranged
automatically to tile the screen without gaps or overlap, maximising
screen use. All features of the window manager are accessible from the
keyboard: a mouse is strictly optional. xmonad is written and
extensible in Haskell. Custom layout algorithms, and other extensions,
may be written by the user in config files. Layouts are applied
dynamically, and different layouts may be used on each workspace.
Xinerama is fully supported, allowing windows to be tiled on several
screens.
license: BSD3
license-file: LICENSE
author: Spencer Janssen
maintainer: sjanssen@cse.unl.edu
build-depends: base>=2.0, X11>=1.2.1, X11-extras>=0.4, mtl>=1.0, unix>=1.0
extra-source-files: README TODO tests/loc.hs tests/Properties.hs man/xmonad.1.in
Config.hs-boot util/GenerateManpage.hs man/xmonad.1 man/xmonad.html
author: Spencer Janssen, Don Stewart, Adam Vogt, David Roundy, Jason Creighton,
Brent Yorgey, Peter Jones, Peter Simons, Andrea Rossato, Devin Mullins,
Lukas Mai, Alec Berryman, Stefan O'Rear, Daniel Wagner, Peter J. Jones,
Daniel Schoepe, Karsten Schoelzel, Neil Mitchell, Joachim Breitner,
Peter De Wachter, Eric Mertens, Geoff Reedy, Michiel Derhaeg,
Philipp Balzarek, Valery V. Vorotyntsev, Alex Tarkovsky, Fabian Beuke,
Felix Hirn, Michael Sloan, Tomas Janousek, Vanessa McHale, Nicolas Pouillard,
Aaron Denney, Austin Seipp, Benno Fünfstück, Brandon S Allbery, Chris Mears,
Christian Thiemann, Clint Adams, Daniel Neri, David Lazar, Ferenc Wagner,
Francesco Ariis, Gábor Lipták, Ivan N. Veselov, Ivan Tarasov, Javran Cheng,
Jens Petersen, Joey Hess, Jonne Ransijn, Josh Holland, Khudyakov Alexey,
Klaus Weidner, Michael G. Sloan, Mikkel Christiansen, Nicolas Dudebout,
Ondřej Súkup, Paul Hebble, Shachaf Ben-Kiki, Siim Põder, Tim McIver,
Trevor Elliott, Wouter Swierstra, Conrad Irwin, Tim Thelion, Tony Zorman
maintainer: xmonad@haskell.org
tested-with: GHC == 8.6.5 || == 8.8.4 || == 8.10.7 || == 9.0.2 || == 9.2.8 || == 9.4.8 || == 9.6.7 || == 9.8.4 || == 9.10.2 || == 9.12.2
category: System
homepage: http://xmonad.org
bug-reports: https://github.com/xmonad/xmonad/issues
build-type: Simple
extra-source-files: README.md
CHANGES.md
CONTRIBUTING.md
INSTALL.md
MAINTAINERS.md
TUTORIAL.md
man/xmonad.1.markdown
man/xmonad.1
man/xmonad.1.html
man/xmonad.hs
util/hpcReport.sh
cabal-version: 1.12
executable: xmonad
main-is: Main.hs
other-modules: Config Operations StackSet XMonad
ghc-options: -funbox-strict-fields -O2 -fasm -Wall -optl-Wl,-s
ghc-prof-options: -prof -auto-all
extensions: GeneralizedNewtypeDeriving
-- Also requires deriving Typeable, PatternGuards
source-repository head
type: git
location: https://github.com/xmonad/xmonad
flag pedantic
description: Be pedantic (-Werror and the like)
default: False
manual: True
library
exposed-modules: XMonad
XMonad.Config
XMonad.Core
XMonad.Layout
XMonad.Main
XMonad.ManageHook
XMonad.Operations
XMonad.StackSet
other-modules: Paths_xmonad
hs-source-dirs: src
build-depends: base >= 4.12 && < 5
, X11 >= 1.10 && < 1.11
, containers
, data-default-class
, directory
, filepath
, mtl
, process
, setlocale
, time
, transformers >= 0.3
, unix
ghc-options: -funbox-strict-fields -Wall -Wno-unused-do-bind
default-language: Haskell2010
-- Keep this in sync with the oldest version in 'tested-with'
if impl(ghc > 8.6.5)
ghc-options: -Wno-unused-imports
if flag(pedantic)
ghc-options: -Werror
executable xmonad
main-is: Main.hs
build-depends: base, xmonad
ghc-options: -Wall -Wno-unused-do-bind
default-language: Haskell2010
-- Keep this in sync with the oldest version in 'tested-with'
if impl(ghc > 8.6.5)
ghc-options: -Wno-unused-imports
if flag(pedantic)
ghc-options: -Werror
test-suite properties
type: exitcode-stdio-1.0
main-is: Properties.hs
other-modules: Instances
Properties.Delete
Properties.Failure
Properties.Floating
Properties.Focus
Properties.GreedyView
Properties.Insert
Properties.Layout.Full
Properties.Layout.Tall
Properties.Screen
Properties.Shift
Properties.Stack
Properties.StackSet
Properties.Swap
Properties.View
Properties.Workspace
Utils
hs-source-dirs: tests
build-depends: base
, QuickCheck >= 2
, quickcheck-classes >= 0.4.3
, X11
, containers
, xmonad
default-language: Haskell2010
if impl(ghc > 9.8)
ghc-options: -Wno-x-partial
if flag(pedantic)
ghc-options: -Werror