322 Commits
v0.2 ... v0.4

Author SHA1 Message Date
Spencer Janssen
bec871d254 Bump XMonadContrib version 2007-10-16 21:52:44 +00:00
Spencer Janssen
258f85dd08 Bump X11, X11-extras versions in the README 2007-10-16 21:26:36 +00:00
l.mai
16abab4241 reformat comments 2007-10-16 16:29:20 +00:00
Spencer Janssen
01cf4a5581 Whitespace fixes for Properties.hs 2007-10-15 02:27:57 +00:00
Spencer Janssen
bd6a52e587 Clean up trailing whitespace 2007-10-15 02:23:22 +00:00
Devin Mullins
0938298f29 explain numlockMask 2007-10-14 00:55:25 +00:00
Devin Mullins
5bd96a8e1a whitespace cleanup in Config.hs 2007-10-14 00:53:42 +00:00
Don Stewart
874e6f80f0 bump the version tag to 0.4, we're almost there 2007-10-13 23:27:58 +00:00
Don Stewart
6fecf7c425 document, and use better names, for serialising/existential-dispatch framework 2007-10-13 23:21:50 +00:00
Don Stewart
3a18204adb typo in comment 2007-10-13 23:08:28 +00:00
Don Stewart
2599706141 more todos 2007-10-13 22:52:00 +00:00
Don Stewart
2edc5a92c2 done 2007-10-13 22:35:36 +00:00
Don Stewart
b6d36f3c70 release tasks 2007-10-13 22:33:47 +00:00
Don Stewart
5c9850bf6d some more layout clean ups 2007-10-13 22:23:17 +00:00
Don Stewart
3f3b4251c2 clean up Layout code a little more 2007-10-13 22:10:24 +00:00
Don Stewart
2de6cc7cf1 restore magic markup comments 2007-10-13 21:23:51 +00:00
Don Stewart
3aa746c0db defer to sjanssen's manageHook comment 2007-10-13 21:03:46 +00:00
Don Stewart
48b001f9a2 Heads up: rework the Config.hs file comments, and some variable names. Please manually resync your Config.hs if you're tracking the darcs branch 2007-10-13 21:01:49 +00:00
Don Stewart
775172983b clean up names of layout code 2007-10-13 20:43:00 +00:00
Spencer Janssen
f5bec53b83 Another manageHook example 2007-10-13 20:56:05 +00:00
Spencer Janssen
77e3876d07 Better comment for the default manageHook 2007-10-13 20:33:40 +00:00
Don Stewart
f439e766a4 add can't happen case to silence incomplete patterns in StackSet.hs 2007-10-13 18:55:25 +00:00
Spencer Janssen
c436e63a15 Bump X11-extras dependency 2007-10-12 20:37:21 +00:00
Spencer Janssen
d610407cf8 Respect ExitExceptions, fixes a regression where exitWith had no effect 2007-10-12 15:28:01 +00:00
Spencer Janssen
f7d6f6b6f7 Make runX return XState 2007-10-12 15:15:24 +00:00
David Roundy
6dba9ddeb3 fix potential hole in userCode.
This makes userCode catch errors even when the
user does something like (return undefined).
2007-10-12 15:02:53 +00:00
Andrea Rossato
49f64197b2 Haddox fix 2007-10-12 10:05:51 +00:00
Spencer Janssen
1f625a6c0d Add userCode function for the popular m catchX return () 2007-10-12 01:42:17 +00:00
David Roundy
1eaee82e85 catch exceptions when calling user-written code.
This is a minimal approach that only catches error
in actual user-written code.
2007-10-12 01:33:05 +00:00
David Roundy
07be5998c0 use the right catch in catchX.
Don't ask *me* why the prelude includes a version of
catch that is worse than useless (because it lulls you
into a feeling of safety).
2007-10-12 01:14:50 +00:00
David Roundy
6d7307030a fix one last bug w.r.t. issue 55. 2007-10-12 01:05:09 +00:00
Don Stewart
6c94b3b217 more comments 2007-10-06 15:43:51 +00:00
David Roundy
1a48b527ff one more comment. 2007-10-11 15:44:23 +00:00
David Roundy
75874040cc add comments in XMonad.
This change also removes readLayout as a top level function,
since it's only used once.
2007-10-11 15:29:42 +00:00
Spencer Janssen
e331dd4a82 Nuke old TODOs, add a documentation TODO 2007-10-11 02:21:27 +00:00
Spencer Janssen
3cf5c1f9d4 Set the border color of new windows, nice catch by mauke 2007-10-11 02:16:27 +00:00
Spencer Janssen
4cfe583f63 Bump required X11-extras version to 0.3.1 2007-10-10 16:57:05 +00:00
Spencer Janssen
d348f2ae72 Only adjust floating windows that are actually larger than the screen
Also, fix a typo caught by Xiao-Yong Jin on the mailing list.
2007-10-10 06:26:04 +00:00
Shachaf Ben-Kiki
41063f2e57 Add LANGUAGE pragmas
It seems that GHC 6.6 just enables -fglasgow-exts when it sees any LANGUAGE
pragma, so not all of them were added; this patch adds the rest of them, which
is necessary for xmonad to compile in GHC >=6.7.
2007-10-08 02:11:07 +00:00
Ferenc Wagner
42dde26d4d The empty line isntcomment.
There is a separate filter for that case.
2007-10-06 19:12:31 +00:00
Christian Thiemann
c66ff8335e Add event handler for PropertyNotifyEvent that calls logHook if window title changed 2007-10-06 17:54:58 +00:00
Spencer Janssen
f7ecf70a35 Moving to code.haskell.org 2007-10-06 19:18:43 +00:00
Don Stewart
fd10c198e6 comments need to be given for all top level bindings 2007-10-06 15:41:27 +00:00
Don Stewart
c49b8f567f a bunch of things in XMonad.hs are missing top level comments! 2007-10-06 15:36:08 +00:00
Devin Mullins
1d0191184f add mapWorkspace tests
(just completely duplicated the two mapLayout tests :)
2007-10-06 07:31:29 +00:00
Don Stewart
6c38226553 change email 2007-10-06 10:49:01 +00:00
Don Stewart
5cd9094f58 style on layout class code 2007-10-06 10:46:06 +00:00
Don Stewart
053f1adb7c avoid name class with forever in 6.8 2007-10-06 10:35:30 +00:00
David Roundy
6294e6adf5 add pureMessage. 2007-10-05 14:05:53 +00:00
Don Stewart
1f9e77bd90 polish some syntax 2007-10-06 10:29:18 +00:00
Devin Mullins
8d3b6fa304 oops, need to export 2007-10-06 05:50:59 +00:00
Devin Mullins
6316d4f2ff darcs setpref test
Fix, per that Main extraction I made the other day.
2007-10-06 05:43:33 +00:00
Devin Mullins
a88a0b1b8b (cleanup) extract mapWorkspace out of renameTag 2007-10-06 05:41:04 +00:00
Don Stewart
bab04b71d3 comment out type error'd property 2007-10-06 10:22:25 +00:00
Don Stewart
d83ce46a1e add floating property 2007-10-06 10:06:54 +00:00
Don Stewart
aaaeae54c3 mention C headers 2007-10-06 09:40:06 +00:00
Spencer Janssen
e0bcad162f Comment only 2007-10-05 03:44:45 +00:00
Spencer Janssen
b07e334405 Move grabButtons/Keys into X 2007-10-05 03:41:02 +00:00
Spencer Janssen
c237441003 Make WindowSet serialization robust to layout changes 2007-10-05 00:00:31 +00:00
Spencer Janssen
42b691d515 Add mapLayout 2007-10-04 23:45:37 +00:00
Devin Mullins
65f3f4db8a extract Properties module for re-use by contrib tests
I want to reuse Properties' Arbitrary instance (as well as the T and
NonNegative types) in an upcoming set of SwapWorkspaces QC props.
`module Main where import Main` doesn't work too well. :)

If this patch is accepted, the darcs 'test' pref should be modified to
"-itests tests/Main.hs".
2007-10-04 07:58:52 +00:00
Spencer Janssen
172e046e84 Remove commented code 2007-10-04 20:02:00 +00:00
Spencer Janssen
7dac92057d manageHook: use the curry style, better documentation 2007-10-03 16:24:04 +00:00
Spencer Janssen
8dbf8896c9 Pointfree 2007-10-03 16:16:43 +00:00
Spencer Janssen
f11ce95528 Remove unused import 2007-10-03 16:16:21 +00:00
Spencer Janssen
d57aab25ef Float Gimp too 2007-10-03 16:13:05 +00:00
Spencer Janssen
89645e0999 List possibleLayouts last, because users are less likely to modify it 2007-10-02 21:47:08 +00:00
Spencer Janssen
ab0ebe1050 Docs for defaultLayout and defaultLayouts 2007-10-02 21:45:17 +00:00
David Roundy
8e303a6bea clean up Config a bit. 2007-10-02 20:36:36 +00:00
David Roundy
e70fb29efc some renaming of classes and data types. 2007-09-29 19:13:20 +00:00
Spencer Janssen
2afa8c3a7a Don't manage kdesktop either 2007-10-02 18:24:55 +00:00
Spencer Janssen
ce0d5d376d Refactor, ignore desktop_window too 2007-10-02 17:52:58 +00:00
Spencer Janssen
4bcad8fe60 Automatically float MPlayer windows 2007-10-02 17:47:22 +00:00
Spencer Janssen
da3db68b59 Add rules for gnome-panel and kicker 2007-10-02 17:42:43 +00:00
Spencer Janssen
045ed777a2 Pass window name and class info to manageHook 2007-10-02 17:40:24 +00:00
Spencer Janssen
e8bbba9694 Send ClassHints to manageHook 2007-10-01 17:52:46 +00:00
Spencer Janssen
8b3dc01e53 Operations.windows is responsible for setting initial properties, remove redundant code from Main 2007-10-01 17:06:28 +00:00
Spencer Janssen
8b8433a9e7 First cut at manageHook 2007-10-01 16:46:27 +00:00
Spencer Janssen
acbe7976d7 Add StackSet.allWindows 2007-10-01 16:39:59 +00:00
David Roundy
d05b01431d set border color more judiciously, so layouts can customize this. 2007-09-28 23:53:46 +00:00
Don Stewart
60a40be09e deeper test for differentiate. back to 100% coverage 2007-09-30 07:50:18 +00:00
Don Stewart
2b6a200ad3 properties for tag renaming 2007-09-30 07:46:41 +00:00
Don Stewart
2196ab7469 test lookupWorkspace more deeply 2007-09-30 07:38:22 +00:00
Aaron Denney
ff1918ad20 On change of keyboard mapping, grabKeys from the root window. 2007-09-29 22:47:55 +00:00
Andrea Rossato
5e3317b28e Operation: coding style conformance 2007-09-28 11:27:44 +00:00
Spencer Janssen
306b3c11c3 StackSet uses PatternGuards 2007-09-28 18:25:10 +00:00
David Roundy
5ef7c5f5d0 define defaultLayout in Config.hs. 2007-09-28 02:02:08 +00:00
Don Stewart
16d4ce5706 merge, update test hook 2007-09-29 14:20:41 +00:00
Don Stewart
6640e434bf 100% coverage of alternative branches 2007-09-28 23:57:45 +00:00
Don Stewart
cee31df81d add some more properties for failure cases 2007-09-28 23:32:30 +00:00
Don Stewart
029dd68860 polish 2007-09-28 23:28:39 +00:00
Don Stewart
bd64d169fe comments and formatting only 2007-09-28 22:05:23 +00:00
Spencer Janssen
d0d81db6de Use LANGUAGE pragmas over -fglasgow-exts 2007-09-28 18:14:38 +00:00
David Roundy
f1c1e982a2 merge old workspace tags with new on restart. 2007-09-26 18:33:09 +00:00
Spencer Janssen
60dda50181 SomeLayout: use the description of the wrapped layout 2007-09-28 05:23:44 +00:00
Spencer Janssen
3b64981c78 LayoutSelection: describe the active layout only 2007-09-28 05:18:58 +00:00
David Roundy
a7c4c38ba8 put transients completely on the screen when possible. 2007-09-27 21:10:14 +00:00
Spencer Janssen
34bbbf59c4 setLayout should not call sendMessage, because sendMessage calls windows 2007-09-28 01:15:10 +00:00
Spencer Janssen
4fd7353d8e Add setLayout to the core 2007-09-28 00:22:41 +00:00
Spencer Janssen
7e8de677cb Document otherPossibleLayouts 2007-09-28 00:02:50 +00:00
Spencer Janssen
3a9dc57c69 Minor formatting 2007-09-28 00:00:25 +00:00
Spencer Janssen
5f9222efb4 otherPossibleLayouts is empty by default 2007-09-27 23:58:45 +00:00
Spencer Janssen
d1fdd4a020 Update kind changes in the -class branch 2007-09-27 22:27:30 +00:00
Spencer Janssen
2ab2195782 Refactor floating code in manage 2007-09-27 19:55:34 +00:00
David Roundy
71bce5e525 fix bug where ReleaseResources wasn't getting sent to all layouts. 2007-09-25 21:58:16 +00:00
Spencer Janssen
9c78ba538b Simplify readLayout, comment on surprising behavior 2007-09-25 21:17:08 +00:00
David Roundy
68c72b34e1 fix bug in reading of SomeLayouts. 2007-09-25 20:28:01 +00:00
David Roundy
2caf68ee69 add support for parseable layouts not in the default. 2007-09-25 17:41:34 +00:00
David Roundy
f420ae881d rename modifyLayout to handleMessage. 2007-09-25 18:29:06 +00:00
David Roundy
e062265b38 make it easier to define pure layouts. 2007-09-25 17:05:03 +00:00
David Roundy
9c35abaa46 Make a String description part of each Layout. 2007-09-24 18:57:53 +00:00
Andrea Rossato
ee39e7fdb8 broadcast a ReleaseResources before restarting 2007-09-24 19:39:15 +00:00
Andrea Rossato
e6fb743e5a Added LayoutMessages
This patch adds some more messages to manage layout: Hide is sent to
layouts in that are not visible anymore. ReleaseReasourses is sent
before a restart.
2007-09-24 19:35:13 +00:00
David Roundy
0b11d6666d update screens for new kind of StackSet. 2007-09-24 13:45:45 +00:00
David Roundy
4a7ec374d0 create default modifyLayout that ignores messages. 2007-09-23 11:52:19 +00:00
David Roundy
1c603ebc4b add layout selection back into core xmonad using LayoutSelection.
This is just a reimplementation of LayoutChoice.
2007-09-21 21:21:59 +00:00
David Roundy
3af0ccf73c make layouts preserved over restart 2007-09-21 20:43:16 +00:00
David Roundy
fe397edf4a move Layout into StackSet.
WARNING! This changes the format of StackSet, and
will definitely mess up your xmonad state, requiring
at minimum a restart!
2007-09-20 22:12:48 +00:00
David Roundy
70282f23dc add (unused) Layout to StackSet. 2007-09-20 21:28:43 +00:00
David Roundy
f3f12383f0 remove unneeded Ord constraint. 2007-09-20 21:05:27 +00:00
David Roundy
cb13207644 eliminate a few Eq a constraints in StackSet. 2007-09-20 21:01:43 +00:00
Spencer Janssen
eb1e38405d Pointfree Mirror and SomeLayout instances 2007-09-20 21:10:42 +00:00
Spencer Janssen
d43384cfc7 Use derived Show and Read instances for Mirror 2007-09-20 20:57:11 +00:00
David Roundy
197c834331 define readLayout to create a SomeLayout based on a set of possible layout types. 2007-09-20 18:15:06 +00:00
David Roundy
5f12ca0faa add Read instance to Layout. 2007-09-20 17:45:29 +00:00
David Roundy
b4929576e7 add Show instance to Layout 2007-09-20 16:12:08 +00:00
David Roundy
0e5f8b03e8 eliminate ugly OldLayout. 2007-09-20 15:52:37 +00:00
David Roundy
3f03dcb5c1 move Layout stuff into class (hokey first cut). 2007-09-14 21:59:59 +00:00
Don Stewart
bee79c83e6 add prop for 'differentiate' 2007-09-27 23:19:28 +00:00
Karsten Schoelzel
29a5256c10 document shiftWin 2007-09-27 13:42:05 +00:00
Don Stewart
6cff2dddcf new QC properties: floating a window is reversible, screens includes current screen 2007-09-27 22:04:31 +00:00
Don Stewart
d1ad738f6b Add 3 QC properties for focusMaster: local, idempotent, preserves invariant 2007-09-27 21:44:01 +00:00
Don Stewart
3b6bfbf54c no regents in xmonad license 2007-09-27 21:43:17 +00:00
Don Stewart
f8c0ae5407 note that we use pattern guards in the .cabal file 2007-09-27 21:42:30 +00:00
Don Stewart
f1aa00f96f Add StackSet.focusMaster (mod-m) to move focus to master 2007-09-27 21:39:37 +00:00
Don Stewart
5e943d512c use hPrint instead of hPutStrLn 2007-09-27 21:39:01 +00:00
Spencer Janssen
019315e70c Split float up 2007-09-24 09:06:06 +00:00
Spencer Janssen
bc525b79e3 Use the new StackSet.screens in windows 2007-09-24 09:05:23 +00:00
Spencer Janssen
f67ebbf495 Add StackSet.screens 2007-09-24 09:04:25 +00:00
Don Stewart
3060c36d00 fmt, and tiny comment seeking clarification 2007-09-17 23:46:58 +00:00
Spencer Janssen
c6f346f887 Eliminate Operations.sink too 2007-09-17 21:40:52 +00:00
Spencer Janssen
e87a111a50 Remove Operations functions which have StackSet equivalents, just use 'windows foo' instead 2007-09-17 21:19:53 +00:00
Alex Tarkovsky
3b5ca225f6 Change manpage token @@ to %! to avoid conflicts with Haddock (xmonad) 2007-09-16 23:52:29 +00:00
Spencer Janssen
46ef80ad06 Haddockify delete' comments 2007-09-17 19:41:14 +00:00
Karsten Schoelzel
b72c096bc6 Fix float behaviour, add shiftWin.
First, if float is called with window which is on a hidden workspace,
then the window will remain on that hidden workspace.

Now the focus should change more as expected:
float w = (view current) . (shiftWin ws w)
    where
        current is the current screen/workspace
        shiftWin ws w is: - view the workspace w is on
            - set focus on w
            - shift ws
            - set focus back to window it was on that workspace
                unless w was focused

shiftWin was add to StackSet.hs
2007-09-10 09:03:29 +00:00
Karsten Schoelzel
0842194940 Add delete' for use in shift
Rename delete to delete' so we can clear floating status in delete,
thus removing one special handling. 
At the moment delete' is only used in shift, but is useful for temporarily
removing a window from the stack.
2007-09-10 11:38:35 +00:00
Don Stewart
2b207a28ef update description field of cabal file 2007-09-16 02:30:16 +00:00
Don Stewart
54af88d5f6 pointfree looks nicer here 2007-09-11 05:19:28 +00:00
Spencer Janssen
b6f00e9aab Remove redundant reveal 2007-09-10 21:38:07 +00:00
Alex Tarkovsky
874a4264c3 Add missing insert markers for generate-configs.sh in Config.hs 2007-09-07 12:04:14 +00:00
Karsten Schoelzel
6898a0e583 Move lower boundary check into applySizeHints, because all users of applySizeHints
do this manually.
2007-09-05 19:21:25 +00:00
Ivan Tarasov
f668b6238a export getAtom from XMonad. 2007-08-25 17:41:56 +00:00
Spencer Janssen
bccf8dd5f8 Use show rather than string hacks 2007-09-05 20:28:16 +00:00
David Roundy
41e3b073c8 switch WorkspaceId to String. 2007-08-20 11:36:58 +00:00
Spencer Janssen
82dd5b8119 Alex Tarkovsky's docstring patch updated for conflicts 2007-09-05 19:35:58 +00:00
Don Stewart
1fb52ce2cc tasks done 2007-09-05 00:49:01 +00:00
Spencer Janssen
dede0a2ce9 README: spelling 2007-09-04 19:30:42 +00:00
Spencer Janssen
74441202a0 Bump version to 0.3 2007-09-04 19:28:41 +00:00
Spencer Janssen
bda704297c Add a link to XMonadContrib 2007-09-04 19:27:59 +00:00
Spencer Janssen
2819adfef4 Point to X11-extras-0.3 in the README 2007-09-04 19:26:43 +00:00
Spencer Janssen
8146dd46dd Depend on X11-extras >= 0.3 2007-09-03 21:52:49 +00:00
Spencer Janssen
92a1335cff Add location of X11-extras to README 2007-08-24 16:09:35 +00:00
Spencer Janssen
49cebc6130 Add docstrings for mouse controls 2007-08-24 04:59:39 +00:00
Don Stewart
314b5ee6bd todos 2007-08-22 02:28:15 +00:00
Don Stewart
aaba52043d comment only: example of 2 monitor gaps 2007-08-21 03:25:38 +00:00
David Roundy
6ec342ff75 don't refresh when setting focus to already focussed window. 2007-08-20 15:02:25 +00:00
David Roundy
8a8438a5c2 clear out motion events when processing one motion event.
This is important if the hook is slow (e.g. try adding "float w"
to the window-dragging hook), as it allows xmonad to keep up with
the motion of the mouse.
2007-08-20 00:23:51 +00:00
David Roundy
2716b1ada6 remove unneeded do. 2007-08-13 14:37:21 +00:00
David Roundy
34d8d51a77 make splitHorizontallyBy accept any RealFrac. 2007-08-13 14:37:07 +00:00
Spencer Janssen
ca0d87664b Fix new bug in screen switching 2007-08-16 21:56:29 +00:00
Don Stewart
6a273c2afa -Wall police 2007-08-16 03:31:32 +00:00
Spencer Janssen
6dcd66f16e Comment only 2007-08-15 22:40:31 +00:00
David Roundy
df4c18a181 simplify code in StackSet. 2007-08-14 01:04:22 +00:00
David Roundy
ec0995a3a6 change workspaces to [WorkspaceId] 2007-08-14 00:37:22 +00:00
Spencer Janssen
919774dff8 Operations.windows: minor refactor 2007-08-15 03:15:21 +00:00
Spencer Janssen
447d662d1d Cleanup 2007-08-10 21:39:40 +00:00
David Roundy
fae3cbebb1 move event loop out of mouseDrag. 2007-08-07 20:16:16 +00:00
David Roundy
4c40661047 only display any given window once.
This change goes along with the sticky window work.  It makes xmonad
display each window once and only once, with preference given to the
focussed screen.  It has no effect when there are no duplicate windows,
except to make things less efficient.  We could do better using Data.Set
(or Data.Map) to store the set of windows that are visible.
2007-07-24 14:13:10 +00:00
Spencer Janssen
2f3ccd7ab6 Add greedyView, make it the default action for mod-wer 2007-08-15 02:55:04 +00:00
Spencer Janssen
8bb313ea53 Remove 'Eq' constraint from StackSet.index 2007-08-07 14:43:46 +00:00
Don Stewart
2e7aa7d055 trailing whitespace only 2007-08-05 07:27:16 +00:00
Andrea Rossato
6875437c44 added workspaces to hs-boot (needed by XMonadContrib.Commands and possibly other modules) 2007-07-28 13:17:56 +00:00
Karsten Schoelzel
808894c217 QuickCheck filter preserves order 2007-07-28 18:45:34 +00:00
Karsten Schoelzel
84c6432c82 Bugfix: reordering when filtering out the last window on a workspace
Say you have three windows A B C* on a workspace with * marking the focus.
If you close C or move it to another workspace, the resulting order will be B* A,
thus reordering the other windows, defying the comment of filter.
2007-07-28 13:25:07 +00:00
Spencer Janssen
bf4388e3aa shift: use guards instead of if 2007-07-24 15:23:40 +00:00
Spencer Janssen
cc3527a975 Remove unnecessary Integral constraints 2007-07-24 15:22:57 +00:00
David Roundy
9a2f57552e make delete work when window is in multiple workspaces. 2007-07-24 14:20:45 +00:00
Spencer Janssen
189c2d31f9 Remove redundant 'n >= 0' check from shift. (from David Roundy's 'simplify shift, removing unneeded check.' patch) 2007-07-24 14:59:27 +00:00
Michael G. Sloan
5068bd27f0 Cleanup of shift code 2007-07-22 20:53:37 +00:00
Don Stewart
fc70bed46b use $HOME in examples 2007-07-19 06:33:48 +00:00
Peter De Wachter
d0482810b3 Tweak dmenu binding
Add an "eval", so quotes and environment variables get evaluated
according to sh rules.
2007-07-17 19:07:22 +00:00
Jason Creighton
c146940154 restore focus to currently focused window after "float" (closes #32) 2007-07-10 04:26:31 +00:00
Spencer Janssen
bfd638d818 Operations.screenWorkspace: return Nothing when the screen does not exist 2007-07-07 22:38:42 +00:00
Spencer Janssen
a48ec57cd9 Operations.rescreen: screen indexes start at zero 2007-07-07 22:33:34 +00:00
Spencer Janssen
54c024583f Note and workaround bugs in Operations.float 2007-07-05 19:52:13 +00:00
Spencer Janssen
2efa369dfc refresh after starting 2007-06-30 05:03:46 +00:00
Spencer Janssen
e74e8050d0 UPGRADE X11-Extras! Manage iconified windows 2007-06-30 02:10:26 +00:00
Spencer Janssen
ab830ec227 Move screen details into StackSet 2007-06-29 21:39:17 +00:00
Jason Creighton
bb12b08239 Change a window's workspace when dragging across screens (closes #30) 2007-06-28 02:50:23 +00:00
David Roundy
61d7524bcd support self-modifying layouts. 2007-06-23 20:14:47 +00:00
Don Stewart
d0566a28be comment for (dubious?) integrate' 2007-06-26 05:24:31 +00:00
David Roundy
6f9a060118 broadcast unidentified events.
This change is independent of the doLayout change I just sent in, but fixes
the problem that change introduces in Decoration, by ensuring that all
Layouts get redraw events.  I think this is the correct behavior.
2007-06-23 21:41:25 +00:00
Don Stewart
dbdf0fd5e4 add 2 properties to state where focus goes on delete of focused window 2007-06-26 04:09:07 +00:00
Don Stewart
ce28fc1eb2 fix empty case in 'filter', and note differences in semantics wrt. focus to 'delete' 2007-06-26 03:57:41 +00:00
Don Stewart
977f8328fc clean up 'StackSet.filter' for style 2007-06-26 03:32:02 +00:00
Don Stewart
776886660b minor tweaks, ideas from joachim.fasting@ 2007-06-21 03:36:13 +00:00
Don Stewart
c6da7fc14a only perform mouse events on managed windows. closes #28 2007-06-21 01:17:00 +00:00
Spencer Janssen
e99d7431c8 Update Layout documentation 2007-06-20 15:08:58 +00:00
Spencer Janssen
5dea6605fc -Wall police 2007-06-20 15:08:23 +00:00
Spencer Janssen
6091bfd0fe Stack windows in the order they are returned by doLayout 2007-06-20 15:04:19 +00:00
Don Stewart
d411736ded remove out of date `(Included with GHC)' text in README 2007-06-20 06:04:30 +00:00
David Roundy
e517aedfa1 make Layouts able to layout whatever they like. 2007-06-19 15:08:16 +00:00
Peter De Wachter
b84a9b875b float fixed size windows 2007-06-18 21:46:57 +00:00
Spencer Janssen
33bb745880 Remove all references to 'exec' 2007-06-18 20:15:32 +00:00
Don Stewart
be08dd80ec -Wall police, and turn on -fno-warn-orphans 2007-06-17 05:23:22 +00:00
David Roundy
dbd58faffe make workspace tag not need to be a Num.
This change also removes the barely used 'size' field, and replaces
it with a tagMember predicate.  The idea is to move towards the ability
to make the workspace tag be a String, which by default might be "1".."9",
but could also be customized to be something meaningful to the user.
2007-06-14 14:07:09 +00:00
Spencer Janssen
a2c5aa3612 Fix float stacking 2007-06-14 21:34:12 +00:00
Spencer Janssen
fa2b56c14e Remove 'temporary work around' in 'windows' 2007-06-14 21:14:50 +00:00
Andrea Rossato
7d1a23698f haddock tuning for StackSet.hs
with this patch the documentation of StackSet.hs will have a nice TOC
2007-06-14 06:45:11 +00:00
Jason Creighton
8169445cbd move initColor to Operations and only store the Pixel value of colors
Moving initColor to Operations allows it to be used by extensions.

The Pixel component of the color is the only thing we need, so it's simpler
just to deal with that.
2007-06-13 23:45:01 +00:00
Andrea Rossato
753b42ae65 haddick fine tuning 2007-06-13 18:59:02 +00:00
Spencer Janssen
d1e4699944 Indentation 2007-06-13 04:30:18 +00:00
Jason Creighton
62344287da prevent keyboard focus from getting lost in some cases 2007-06-13 02:53:50 +00:00
David Roundy
8cdcceab48 resolve conflict in Operations. 2007-06-12 17:06:25 +00:00
David Roundy
194a934c37 add catchX to catch exceptions. 2007-06-12 15:42:53 +00:00
David Roundy
5f8202e79e make focus, up and down complete functions.
This is a rerun of my change to make (Stack a) never be empty.  Gives
us more type-safety.
2007-06-12 15:05:55 +00:00
David Roundy
4ffee115e1 add differentiate function to StackSet to go [a] -> Stack a. 2007-06-12 13:28:53 +00:00
Spencer Janssen
00e1038d71 Make 'view' a total function 2007-06-12 14:32:48 +00:00
Don Stewart
7158a58792 fmt 2007-06-12 13:49:38 +00:00
Stefan O'Rear
c0a9636f3b -Wall police 2007-06-12 06:05:46 +00:00
Stefan O'Rear
ff6b48382c Use a more descriptive name for the layout reversal message 2007-06-12 05:58:59 +00:00
Stefan O'Rear
bb9e46df6c Use broadcastMessage in windows and switchLayout, should improve Xinerama for tabbed and make xmonad robust in the presence of state-altering unlayout hooks 2007-06-12 05:55:10 +00:00
Stefan O'Rear
f68a954fc3 Add a broadcastMessage function, which sends to all visible workspaces without refreshing. (+6 loc) 2007-06-12 05:53:39 +00:00
Spencer Janssen
d21e61a315 TODO for scan 2007-06-11 21:42:17 +00:00
Spencer Janssen
4b9bacb1f9 Set withdrawn state after calling windows 2007-06-11 21:33:27 +00:00
Spencer Janssen
9992737e84 Remove obsolete 'layout' function 2007-06-11 20:36:22 +00:00
Spencer Janssen
615a4a1af1 -Wall police 2007-06-11 20:20:07 +00:00
Spencer Janssen
33447129dd Comment only 2007-06-11 19:58:27 +00:00
Spencer Janssen
14971546bb Hide windows that are not supposed to be visible 2007-06-11 19:18:09 +00:00
Spencer Janssen
6f7030f875 -Wall police 2007-06-11 18:57:08 +00:00
Spencer Janssen
4f5c307d6f API CHANGE: Give doLayout a Stack rather than a flattened list 2007-06-11 18:26:29 +00:00
Spencer Janssen
e7b37ca646 -Wall police 2007-06-11 18:01:23 +00:00
Spencer Janssen
5f3e91676a Add StackSet.filter 2007-06-11 16:51:54 +00:00
Spencer Janssen
b3bdbf3588 Use catchIO in 'restart' 2007-06-11 16:11:52 +00:00
Spencer Janssen
00b930b09e Rename safeIO to catchIO 2007-06-11 16:06:08 +00:00
David Roundy
0d17ca9436 add safeIO which catches and logs exceptions. 2007-06-11 15:36:50 +00:00
Spencer Janssen
b668133c08 Ensure windows get at least 1 pixel for width/height 2007-06-11 06:19:30 +00:00
Spencer Janssen
330179ea20 Restrict the master/slave ratio to [0, 1] 2007-06-11 05:32:30 +00:00
Jason Creighton
854d3239cc comment only 2007-06-11 02:02:49 +00:00
David Roundy
c8b6388fb8 a few modifications to event-sending to make Tabbed layout work. 2007-06-10 15:38:36 +00:00
David Roundy
b97e8836e2 send message when "windows" is called. 2007-06-10 01:35:31 +00:00
David Roundy
ab6f210300 implement Spencer's decoration suggestion. 2007-06-10 01:22:37 +00:00
Andrea Rossato
e1885f27e1 haddock compatibility 2007-06-10 12:37:46 +00:00
Don Stewart
6365601c77 Move state logging into Config.hs, via logHook :: X () 2007-06-10 06:19:32 +00:00
Don Stewart
3bfa0930af polish serialisation code (-7 lines) 2007-06-10 04:55:51 +00:00
David Roundy
0d4a7d098f cut incorrect comment. 2007-06-09 17:34:47 +00:00
David Roundy
16c8622fbf doLayout cleanup and commented exception-handling. 2007-06-09 14:50:36 +00:00
Stefan O'Rear
1c3931a0d6 Give refresh sole responsibility for establishing window properties (-3 loc) 2007-06-09 18:58:35 +00:00
Stefan O'Rear
7706f38dc8 Give refresh sole responsibility for establishing window properties (-3 loc) 2007-06-09 18:58:35 +00:00
Don Stewart
0ada17c34a HEADS UP: (logging format change). use a custom pretty printer, for an easier format to parse, than 'show' produces 2007-06-09 13:17:16 +00:00
Don Stewart
a21c4d02f1 Add notes on using X11-extras from darcs 2007-06-09 02:50:45 +00:00
Spencer Janssen
cf9828cbcd Fix unmap handling
According to the ICCCM, clients should send a synthetic unmap event when they
initiate an unmap.  The old code waited for these synthetic unmaps to unmanage
windows.  However, certain 'obsolete' clients do not send synthetic unmaps
(notably xpdf's find dialog).  These windows entered a zombified state: xmonad
does not manage them, yet they are still mapped and raised on screen.

The new algorithm (derived from wmii):
 - track windows that are mapped on screen
 - track the number of expected unmap events for each window, increment every
   time 'hide' is called on a window that is not mapped.
 - decrement the expected unmap counter on each unmap event
 - treat an unmap event as genuine (ie. unmap the window) when:
    - the event is synthetic (per ICCCM)
    - OR there are no expected unmap events for this window
2007-06-06 21:40:06 +00:00
Don Stewart
b257658781 dead import 2007-06-06 02:52:26 +00:00
Jason Creighton
5da458c755 move extraModifiers/cleanMask to Operations.hs
so XMonadContrib can use them
2007-06-06 00:50:56 +00:00
Don Stewart
d7d8c586cb temporary workaround for delete/focus issue in fullscreen mode 2007-06-06 02:49:38 +00:00
Don Stewart
86ea7f7bc0 whitespace 2007-06-06 02:48:57 +00:00
Don Stewart
a2a0670397 simplify code 2007-06-06 00:46:03 +00:00
Don Stewart
02a9e4c589 mention why StackSet needs -fglasgow-exts (for deriving Typeable) 2007-06-05 09:26:59 +00:00
Don Stewart
d4676d93e8 comments only 2007-06-05 09:18:03 +00:00
Don Stewart
0010a23c18 clean size hint code 2007-06-05 09:13:54 +00:00
Don Stewart
7ae7029b50 Enable logging of state changes to stdout 2007-06-05 08:37:35 +00:00
Don Stewart
21e09361a6 remove accidental logging of events 2007-06-05 08:14:52 +00:00
Don Stewart
8f200d408f Fix lost eventNotifyMask bug
When resuming, we were (implicitly) relying on 'scan' to find all
windows, and reset their event masks and WM_STATE. When we moved to
Iconfified hidden workspaces, 'scan' would only find and reset states on 
the current workspace.

The result being that hidden workspace windows no longer received
enterNotify events.

Fix this by traversing the StackSet serialised during a restart, setting
the intial X states for each window, whether visible or hidden.
2007-06-05 04:30:40 +00:00
Don Stewart
d3632eb8fe whitespace only 2007-06-05 00:07:23 +00:00
Spencer Janssen
b22ebceb80 Comment only 2007-06-04 21:19:56 +00:00
Spencer Janssen
ba14f07093 Wibble. 2007-06-04 21:18:16 +00:00
Spencer Janssen
2a6d6d4ed7 -Wall police 2007-06-04 21:15:31 +00:00
Peter De Wachter
68b2859aa2 apply size hints to floating windows 2007-06-04 19:29:43 +00:00
Peter De Wachter
a10f11f623 size hints infrastructure 2007-06-04 19:27:53 +00:00
Spencer Janssen
6a2f9d739d Delete stale comment 2007-06-04 20:46:17 +00:00
Spencer Janssen
4d74099851 Comment only 2007-06-04 20:36:59 +00:00
Spencer Janssen
26b388189e Use 'windows' in 'focus' 2007-06-04 20:16:39 +00:00
l.mai
1c609288dd realign guard 2007-06-04 18:20:45 +00:00
Spencer Janssen
68a63688ad swapUp/Down are also mirrored 2007-06-04 18:35:35 +00:00
Spencer Janssen
6b8e9570c2 Remove redundant cases in swapUp/Down 2007-06-04 18:33:44 +00:00
Spencer Janssen
85e57cfa47 focusUp/Down are the same, in reversed order 2007-06-04 18:31:43 +00:00
Spencer Janssen
89a31eaf52 Simplify focusUp/Down 2007-06-04 18:22:28 +00:00
Spencer Janssen
fc70307325 Integral implies Eq 2007-06-04 18:07:45 +00:00
Spencer Janssen
f9110c999b Comment typo. 2007-06-04 18:05:54 +00:00
Spencer Janssen
795e96d353 Dump state at launch (commented for now) 2007-06-04 16:24:50 +00:00
Spencer Janssen
fec58e8a09 Small clean up 2007-06-04 06:44:18 +00:00
Spencer Janssen
d01623db88 Merge windows and refresh 2007-06-04 06:36:57 +00:00
Spencer Janssen
1912feee50 Use the new integrate function 2007-06-04 06:26:53 +00:00
Spencer Janssen
cad36baa19 Add integrate 2007-06-04 06:25:01 +00:00
Spencer Janssen
77da6e4c72 Delete stale comments 2007-06-04 06:17:19 +00:00
Spencer Janssen
71349314c5 Remove inaccurate warnings about 'hide' 2007-06-04 06:06:11 +00:00
Spencer Janssen
f2eb5ac6bb base >= 2.0 means we can use forM_ 2007-06-04 05:09:14 +00:00
Stefan O'Rear
e4e1724842 Remove no-longer-needed 'dimensions' state (-5 loc) 2007-06-04 04:47:15 +00:00
Stefan O'Rear
cd73165c63 Set WM_STATE, iconify invisible windows (+9 loc)
Note that this breaks compatibility with certain programs described as
"obsolete" in the ICCCM (1994).  See the command above the UnmapEvent handler
for details.
2007-06-04 04:23:43 +00:00
Don Stewart
225a2e89a3 clean up Main.hs slightly 2007-06-04 03:56:37 +00:00
Don Stewart
9b429f4f41 whitespace 2007-06-04 01:55:32 +00:00
Don Stewart
ca896686a1 -Wall 2007-06-04 01:46:30 +00:00
Stefan O'Rear
f06d042b56 do not cache atom values within Xmonad, instead let Xlib worry about caching (a documented feature) 2007-06-04 01:39:38 +00:00
Spencer Janssen
29a32bc146 Honor configure requests from unmanaged windows 2007-06-03 23:47:30 +00:00
Spencer Janssen
27b7cccd3a -Wall police 2007-06-03 21:20:55 +00:00
Stefan O'Rear
b20a9cff7f Correctly handle resize requests (-12 +22)
Xmonad now implements resize requests in a consistent manner.

* If the window is FLOATING, we implement the program's request, and
  correctly update the StackSet; so it will keep the new size.  This
  should work correctly even for non-current windows.

* Otherwise, we ignore the request.  As per ICCCM, we send a fake
  ConfigureNotify containing the new (unchanged) geometry.  This is
  perfectly ICCCM compliant, and if it breaks your client, it's your
  own fault.

This patch requires setConfigureEvent, which is added to X11-extras by
a patch approximately contemporaneous with this one.
2007-06-03 20:31:53 +00:00
Don Stewart
8f3258a348 comments only 2007-06-03 07:15:56 +00:00
Don Stewart
0226ba3441 Polish core layout code. Lifts limitation on nmaster > 1. it may be 0 now 2007-06-03 06:43:06 +00:00
Don Stewart
84f22f7102 heads up: polish config.hs. moves tiling-local values into lexical scope. removes wide' as an explicit mode (it's mirror tall') 2007-06-03 05:47:40 +00:00
Don Stewart
7e9fbf5883 set build-depends base>=2.0 so people can't miss the missing Read instance issue 2007-06-03 03:23:19 +00:00
Chris Mears
7ae4bc8f39 Fix out-of-date comment in Config.hs. 2007-06-02 11:43:12 +00:00
Jason Creighton
a6098f6010 only grab button{1,2,3} for click-to-focus (scrollwheel shouldn't focus) 2007-06-02 05:26:05 +00:00
Jason Creighton
72a50ead89 make mouse bindings configurable 2007-06-02 04:06:47 +00:00
Don Stewart
0be589ae8c commented out implementation state logging. if someone has a client, we can enable this 2007-06-01 08:56:26 +00:00
Jason Creighton
b46a449baf ignore numlock/capslock on mouse bindings 2007-06-01 01:51:37 +00:00
Don Stewart
9669c26fdc now we handle transients properly, and restack windows, refresh from focus is ok 2007-06-01 02:23:29 +00:00
glasser
ddffd109ce Rename withWorkspace to withWindowSet. 2007-06-01 00:13:25 +00:00
Spencer Janssen
68e6643356 Revert accidental change to border color 2007-05-31 14:55:09 +00:00
Don Stewart
7246a9e2d2 comments on why fullscreen tiling doesn't work with `implicit' floating 2007-05-31 09:05:37 +00:00
Don Stewart
777cf28bdf clean up mouse code a bit 2007-05-31 08:53:08 +00:00
Jason Creighton
3cb64d7461 first shot at a floating layer
This is a first attempting at a floating layer:

mod-button1: move window
mod-button2: swapMaster
mod-button3: resize window

mod-t: make floating window tiled again

Moving or resizing a window automatically makes it floating.

Known issues:

Hard to manage stacking order. You can promote a window to move it to the top,
(which you can do with mod-button2) but it should be easier than that.

Moving a window by dragging it to a different Xinerama screen does not move it
to that workspace.

Code is ugly.
2007-05-31 04:47:33 +00:00
Jason Creighton
1d764ecf2e remove LOC cap (but still print count after tests) 2007-05-31 04:34:17 +00:00
15 changed files with 1710 additions and 682 deletions

274
Config.hs
View File

@@ -3,139 +3,257 @@
-- Module : Config.hs
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : dons@cse.unsw.edu.au
--
-- 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-shift-ctrl-q) xmonad,
-- 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
-- The number of workspaces (virtual screens)
workspaces :: Int
workspaces = 9
-- % Extension-provided imports
-- 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.
-- | 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
-- When resizing a window, this ratio specifies by what percent to
-- resize in a single step
defaultDelta :: Rational
defaultDelta = 3%100
-- The default number of windows in the master area
defaultWindowsInMaster :: Int
defaultWindowsInMaster = 1
-- 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)
--
-- Fields are: top, bottom, left, right.
--
defaultGaps :: [(Int,Int,Int,Int)]
defaultGaps = [(0,0,0,0)] -- 15 for default dzen
-- numlock handling:
--
-- The mask for the numlock key. You may need to change this on some systems.
-- | 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)
-- > $ 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
-- Border colors for unfocused and focused windows, respectively.
-- | 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"
-- Width of the window border in pixels
borderWidth :: Dimension
borderWidth = 1
-- The default set of Layouts:
defaultLayouts :: [Layout]
defaultLayouts = [ tall defaultWindowsInMaster defaultDelta (1%2)
, wide defaultWindowsInMaster defaultDelta (1%2)
, full ]
-- | 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)
--
-- The key bindings list.
-- 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` && exec $exe") -- @@ Launch dmenu
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun") -- @@ Launch gmrun
, ((modMask .|. shiftMask, xK_c ), kill) -- @@ Close the focused window
[ ((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 ), switchLayout) -- @@ Rotate through the available layout algorithms
, ((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
, ((modMask, xK_n ), refresh) -- %! Resize viewed windows to the correct size
-- move focus up or down the window stack
, ((modMask, xK_Tab ), focusDown) -- @@ Move focus to the next window
, ((modMask, xK_j ), focusDown) -- @@ Move focus to the next window
, ((modMask, xK_k ), focusUp ) -- @@ Move focus to the previous window
, ((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), swapMaster) -- @@ Swap the focused window and the master window
, ((modMask .|. shiftMask, xK_j ), swapDown ) -- @@ Swap the focused window with the next window
, ((modMask .|. shiftMask, xK_k ), swapUp ) -- @@ Swap the focused window with the previous window
, ((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
, ((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
, ((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
, ((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 ), restart Nothing True) -- @@ Restart xmonad
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit xmonad
, ((modMask , xK_q ), broadcastMessage ReleaseResources >> restart Nothing True) -- %! Restart xmonad
] ++
-- mod-[1..9] @@ Switch to workspace N
-- mod-shift-[1..9] @@ Move client to workspace N
[((m .|. modMask, k), f i)
| (i, k) <- zip [0 .. fromIntegral workspaces - 1] [xK_1 ..]
, (f, m) <- [(view, 0), (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
-- % Extension-provided key bindings
]
++
[((m .|. modMask, key), screenWorkspace sc >>= f)
-- 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) <- [(view, 0), (shift, shiftMask)]]
, (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,3 +1,10 @@
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]

View File

@@ -14,7 +14,7 @@ are met:
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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

209
Main.hs
View File

@@ -1,4 +1,4 @@
-----------------------------------------------------------------------------
----------------------------------------------------------------------------
-- |
-- Module : Main.hs
-- Copyright : (c) Spencer Janssen 2007
@@ -8,14 +8,18 @@
-- Stability : unstable
-- Portability : not portable, uses mtl, X11, posix
--
-----------------------------------------------------------------------------
--
-- xmonad, a minimalist, tiling window manager for X11
--
-----------------------------------------------------------------------------
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)
@@ -25,46 +29,51 @@ import Graphics.X11.Xinerama (getScreenInfo)
import XMonad
import Config
import StackSet (new)
import Operations (manage, unmanage, focus, setFocusX, full, isClient, rescreen)
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
initcolor c = fst `liftM` allocNamedColor dpy (defaultColormap dpy dflt) c
rootw <- rootWindow dpy dflt
wmdelt <- internAtom dpy "WM_DELETE_WINDOW" False
wmprot <- internAtom dpy "WM_PROTOCOLS" False
xinesc <- getScreenInfo dpy
nbc <- initcolor normalBorderColor
fbc <- initcolor focusedBorderColor
nbc <- initColor dpy normalBorderColor
fbc <- initColor dpy focusedBorderColor
hSetBuffering stdout NoBuffering
args <- getArgs
let winset | ("--resume" : s : _) <- args
, [(x, "")] <- reads s = x
| otherwise = new (fromIntegral workspaces) (fromIntegral $ length xinesc)
safeLayouts = case defaultLayouts of [] -> (full, []); (x:xs) -> (x, xs)
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
, wmdelete = wmdelt
, wmprotocols = wmprot
-- fromIntegral needed for X11 versions that use Int instead of CInt.
, normalBorder = nbc
, focusedBorder = fbc
}
, focusedBorder = fbc }
st = XState
{ windowset = winset
, layouts = M.fromList [(w, safeLayouts) | w <- [0 .. W workspaces - 1]]
, statusGaps = take (length xinesc) $ defaultGaps ++ repeat (0,0,0,0)
, xineScreens = xinesc
, dimensions = (fromIntegral (displayWidth dpy dflt),
fromIntegral (displayHeight dpy dflt)) }
{ windowset = initialWinset
, mapped = S.empty
, waitingUnmap = M.empty
, dragging = Nothing }
xSetErrorHandler -- in C, I'm too lazy to write the binding: dons
@@ -72,44 +81,72 @@ main = do
sync dpy False
selectInput dpy rootw $ substructureRedirectMask .|. substructureNotifyMask
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
grabKeys dpy rootw
sync dpy False
ws <- scan dpy rootw
allocaXEvent $ \e ->
runX cf st $ do
mapM_ manage ws
-- main loop, for all you HOF/recursion fans out there.
forever $ handle =<< io (nextEvent dpy e >> getEvent e)
where forever a = a >> forever a
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 initial windows to manage
-- | 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
&& (wa_map_state wa == waIsViewable || ic)
-- | Grab the keys back
grabKeys :: Display -> Window -> IO ()
grabKeys dpy rootw = do
ungrabKey dpy anyKey anyModifier rootw
flip mapM_ (M.keys keys) $ \(mask,sym) -> do
kc <- keysymToKeycode dpy sym
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 .|.)) $
[0, numlockMask, lockMask, numlockMask .|. lockMask]
when (kc /= '\0') $ mapM_ (grab kc . (mask .|.)) extraModifiers
where grab kc m = grabKey dpy kc m rootw True grabModeAsync grabModeAsync
-- | 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
@@ -128,25 +165,57 @@ handle :: Event -> X ()
handle (KeyEvent {ev_event_type = t, ev_state = m, ev_keycode = code})
| t == keyPress = withDisplay $ \dpy -> do
s <- io $ keycodeToKeysym dpy code 0
whenJust (M.lookup (complement (numlockMask .|. lockMask) .&. m,s) keys) id
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
when (not (wa_override_redirect wa)) $ manage w
-- 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
handle (UnmapEvent {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 {ev_window = w}) = do
handle e@(MappingNotifyEvent {}) = do
io $ refreshKeyboardMapping e
when (ev_request e == mappingKeyboard) $ withDisplay $ io . flip grabKeys w
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 (ButtonEvent {ev_window = w, ev_event_type = t}) | t == buttonPress = focus w
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})
@@ -160,20 +229,34 @@ handle e@(CrossingEvent {ev_event_type = t})
when (ev_window e == rootw && not (ev_same_screen e)) $ setFocusX rootw
-- configure a window
handle e@(ConfigureRequestEvent {}) = withDisplay $ \dpy -> do
io $ configureWindow dpy (ev_window e) (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 = ev_border_width e
, wc_sibling = ev_above e
-- this fromIntegral is only necessary with the old X11 version that uses
-- Int instead of CInt. TODO delete it when there is a new release of X11
, wc_stack_mode = fromIntegral $ ev_detail e }
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
-- the root may have configured
-- configuration changes in the root may mean display settings have changed
handle (ConfigureEvent {ev_window = w}) = whenX (isRoot w) rescreen
handle _ = return () -- trace (eventName e) -- ignoring
-- 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

View File

@@ -1,13 +1,18 @@
{-# OPTIONS -fglasgow-exts #-}
-----------------------------------------------------------------------------
{-# 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, mtl, posix
-- Portability : not portable, Typeable deriving, mtl, posix
--
-- Operations.
--
-----------------------------------------------------------------------------
@@ -15,67 +20,74 @@ module Operations where
import XMonad
import qualified StackSet as W
import {-# SOURCE #-} Config (borderWidth)
import {-# SOURCE #-} Config (borderWidth,logHook,manageHook,numlockMask,serialisedLayouts)
import Data.Maybe
import Data.List (genericIndex, intersectBy)
import Data.Bits ((.|.))
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 System.Mem (performGC)
import Control.Monad.State
import Control.Monad.Reader
import Control.Arrow
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. If the window is already managed, nothing happens.
-- 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 = do
withDisplay $ \d -> io $ do
selectInput d w $ structureNotifyMask .|. enterWindowMask .|. propertyChangeMask
mapWindow d w
setWindowBorderWidth d w borderWidth
windows $ W.insertUp w
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 = windows . W.delete
-- | focus. focus window up or down. or swap various windows.
focusUp, focusDown, swapUp, swapDown, swapMaster :: X ()
focusUp = windows W.focusUp
focusDown = windows W.focusDown
swapUp = windows W.swapUp
swapDown = windows W.swapDown
swapMaster = windows W.swapMaster
-- | shift. Move a window to a new workspace, 0 indexed.
shift :: WorkspaceId -> X ()
shift n = withFocused hide >> windows (W.shift n)
-- refresh will raise it if we didn't need to move it.
-- | view. Change the current workspace to workspace at offset n (0 indexed).
view :: WorkspaceId -> X ()
view = windows . W.view
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
XState { windowset = ws, statusGaps = gaps } <- get
let n = fromIntegral $ W.screen (W.current ws)
(a,i:b) = splitAt n gaps
modify $ \s -> s { statusGaps = a ++ f n i : b }
refresh
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.
@@ -85,7 +97,8 @@ modifyGap f = do
--
kill :: X ()
kill = withDisplay $ \d -> withFocused $ \w -> do
XConf {wmdelete = wmdelt, wmprotocols = wmprot} <- ask
wmdelt <- atom_WM_DELETE_WINDOW ; wmprot <- atom_WM_PROTOCOLS
protocols <- io $ getWMProtocols d w
io $ if wmdelt `elem` protocols
then allocaXEvent $ \ev -> do
@@ -97,33 +110,112 @@ kill = withDisplay $ \d -> withFocused $ \w -> do
-- ---------------------------------------------------------------------
-- 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
old <- gets windowset
let new = f old
modify (\s -> s { windowset = new })
refresh
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 })
-- We now go to some effort to compute the minimal set of windows to hide.
-- The minimal set being only those windows which weren't previously hidden,
-- which is the intersection of previously visible windows with those now hidden
mapM_ hide . concatMap (integrate . W.stack) $
intersectBy (\w x -> W.tag w == W.tag x)
(map W.workspace $ W.current old : W.visible old)
(W.hidden new)
-- 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
clearEnterEvents
-- 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))
-- TODO: move this into StackSet. This isn't exactly the usual integrate.
where integrate W.Empty = []
integrate (W.Node x l r) = x : l ++ r
-- 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)
-- | hide. Hide a window by moving it off screen.
-- 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 = withDisplay $ \d -> do
(sw,sh) <- gets dimensions
io $ moveWindow d w sw sh
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.
@@ -132,47 +224,27 @@ hide w = withDisplay $ \d -> do
-- with X calls.
--
refresh :: X ()
refresh = do
XState { windowset = ws, layouts = fls, xineScreens = xinesc, statusGaps = gaps } <- get
d <- asks display
refresh = windows id
-- for each workspace, layout the currently visible workspaces
(`mapM_` (W.current ws : W.visible ws)) $ \w -> do
let n = W.tag (W.workspace w)
this = W.view n ws
Just l = fmap fst $ M.lookup n fls
(Rectangle sx sy sw sh) = genericIndex xinesc (W.screen w)
(gt,gb,gl,gr) = genericIndex gaps (W.screen w)
-- now tile the windows on this workspace, modified by the gap
rs <- doLayout l (Rectangle (sx + fromIntegral gl)
(sy + fromIntegral gt)
(sw - fromIntegral (gl + gr))
(sh - fromIntegral (gt + gb))) (W.index this)
mapM_ (\(win,rect) -> io (tileWindow d win rect)) rs
-- and raise the focused window if there is one.
whenJust (W.peek this) $ io . raiseWindow d
setTopFocus
clearEnterEvents
-- io performGC -- really helps
-- | clearEnterEvents. Remove all window entry events from the event queue.
clearEnterEvents :: X ()
clearEnterEvents = withDisplay $ \d -> io $ do
-- | 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 enterWindowMask p
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 :: Display -> Window -> Rectangle -> IO ()
tileWindow d w r = do
bw <- (fromIntegral . wa_border_width) `liftM` getWindowAttributes d w
moveResizeWindow d w (rect_x r) (rect_y r)
(rect_width r - bw*2) (rect_height r - bw*2)
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
-- ---------------------------------------------------------------------
@@ -182,181 +254,403 @@ rescreen :: X ()
rescreen = do
xinesc <- withDisplay (io . getScreenInfo)
-- TODO: This stuff is necessary because Xlib apparently caches screen
-- width/height. Find a better solution later. I hate Xlib.
let sx = maximum $ map (\r -> rect_x r + fromIntegral (rect_width r)) xinesc
sy = maximum $ map (\r -> rect_y r + fromIntegral (rect_height r)) xinesc
modify (\s -> s { xineScreens = xinesc , dimensions = (sx, sy)
, statusGaps = take (length xinesc) $ (statusGaps s) ++ repeat (0,0,0,0) })
windows $ \ws@(W.StackSet { W.current = v, W.visible = vs, W.hidden = hs }) ->
let (x:xs, ys) = splitAt (length xinesc) $ map W.workspace (v:vs) ++ hs
in ws { W.current = W.Screen x 0
, W.visible = zipWith W.Screen xs [1 ..]
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 }
-- ---------------------------------------------------------------------
buttonsToGrab :: [Button]
buttonsToGrab = [button1, button2, button3]
-- | setButtonGrab. Tell whether or not to intercept clicks on a given window
setButtonGrab :: Bool -> Window -> X ()
setButtonGrab grab w = withDisplay $ \d -> io $ (`mapM_` buttonsToGrab) $ \b ->
if grab then grabButton d b anyModifier w False (buttonPressMask .|. buttonReleaseMask)
grabModeAsync grabModeSync none none
else ungrabButton d b anyModifier w
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 = withWorkspace $ maybe (setFocusX =<< asks theRoot) setFocusX . W.peek
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 = withWorkspace $ \s -> do
if W.member w s then modify (\st -> st { windowset = W.focusWindow w s }) >> setFocusX w -- >> refresh
else whenX (isRoot w) $ setFocusX w -- we could refresh here, moving gap too.
-- XXX a focus change could be caused by switching workspaces in xinerama.
-- if so, and the gap is in use, the gap should probably follow the
-- cursor to the new screen.
--
-- to get the gap though, you need to trigger a refresh.
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 = withWorkspace $ \ws -> do
XConf { display = dpy , normalBorder = nbc, focusedBorder = fbc } <- ask
setFocusX w = withWindowSet $ \ws -> do
dpy <- asks display
-- clear mouse button grab and border on other windows
(`mapM_` (W.current ws : W.visible ws)) $ \wk -> do
(`mapM_` (W.index (W.view (W.tag (W.workspace wk)) ws))) $ \otherw -> do
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
io $ setWindowBorder dpy otherw (color_pixel nbc)
-- 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
setButtonGrab False w
io $ setWindowBorder dpy w (color_pixel fbc)
-- ---------------------------------------------------------------------
-- Managing layout
------------------------------------------------------------------------
-- Message handling
-- | switchLayout. Switch to another layout scheme. Switches the
-- layout of the current workspace. By convention, a window set as
-- master in Tall mode remains as master in Wide mode. When switching
-- from full screen to a tiling mode, the currently focused window
-- becomes a master. When switching back , the focused window is
-- uppermost.
--
switchLayout :: X ()
switchLayout = layout (\(x, xs) -> let xs' = xs ++ [x] in (head xs', tail xs'))
-- | Throw an (extensible) message value to the current Layout scheme,
-- possibly modifying how we layout the windows, then refresh.
--
-- TODO, this will refresh on Nothing.
--
-- | Throw a message to the current LayoutClass possibly modifying how we
-- layout the windows, then refresh.
sendMessage :: Message a => a -> X ()
sendMessage a = layout $ \x@(l, ls) -> maybe x (flip (,) ls) (modifyLayout l (SomeMessage a))
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:
-- | Builtin layout algorithms:
--
-- > fullscreen mode
-- > tall mode
--
-- fullscreen mode
-- tall mode
-- wide mode
--
-- The latter algorithms support the following operations:
--
-- Shrink
-- Expand
-- > 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
data Resize = Shrink | Expand deriving Typeable
instance Message Resize
data IncMasterN = IncMasterN Int deriving Typeable
instance Message IncMasterN
full :: Layout
full = Layout { doLayout = \sc ws -> return [ (w,sc) | w <- ws ]
, modifyLayout = const Nothing } -- no changes
-- | Simple fullscreen mode, just render all windows fullscreen.
data Full a = Full deriving (Show, Read)
tall, wide :: Int -> Rational -> Rational -> Layout
wide nmaster delta frac = mirrorLayout (tall nmaster delta frac)
instance LayoutClass Full a
tall nmaster delta frac =
Layout { doLayout = \r -> return . ap zip (tile frac r nmaster . length)
, modifyLayout = \m -> fmap resize (fromMessage m) `mplus`
fmap incmastern (fromMessage m) }
-- | The inbuilt tiling mode of xmonad, and its operations.
data Tall a = Tall Int Rational Rational deriving (Show, Read)
where resize Shrink = tall nmaster delta (frac-delta)
resize Expand = tall nmaster delta (frac+delta)
incmastern (IncMasterN d) = tall (max 1 (nmaster+d)) delta frac
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
mirrorLayout :: Layout -> Layout
mirrorLayout (Layout { doLayout = dl, modifyLayout = ml }) =
Layout { doLayout = \sc w -> map (second mirrorRect) `fmap` dl (mirrorRect sc) w
, modifyLayout = fmap mirrorLayout . ml }
-- | Mirror a layout, compute its 90 degree rotated form.
data Mirror l a = Mirror (l a) deriving (Show, Read)
-- | tile. Compute the positions for windows in our default tiling modes
-- Tiling algorithms in the core should satisify the constraint that
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.
--
-- * no windows overlap
-- * no gaps exist between windows.
-- 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 | n <= nmaster = splitVertically n r
| otherwise = splitVertically nmaster r1 ++ splitVertically (n-nmaster) r2
where (r1,r2) = splitHorizontallyBy f r
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
splitHorizontally n r = map mirrorRect $ splitVertically n $ mirrorRect r
where smallh = sh `div` fromIntegral n --hmm, this is a fold or map.
splitHorizontallyBy, splitVerticallyBy :: Rational -> Rectangle -> (Rectangle, Rectangle)
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
splitVerticallyBy f r = (\(a,b)->(mirrorRect a,mirrorRect b)) $ splitHorizontallyBy f $ mirrorRect r
( Rectangle sx sy leftw sh
, Rectangle (sx + fromIntegral leftw) sy (sw-fromIntegral leftw) sh)
where leftw = floor $ fromIntegral sw * f
------------------------------------------------------------------------
-- | layout. Modify the current workspace's layout with a pure
-- function and refresh.
layout :: ((Layout, [Layout]) -> (Layout, [Layout])) -> X ()
layout f = do
modify $ \s ->
let n = W.tag . W.workspace . W.current . windowset $ s
(Just fl) = M.lookup n $ layouts s
in s { layouts = M.insert n (f fl) (layouts s) }
refresh
-- | XXX comment me
splitVerticallyBy f = (mirrorRect *** mirrorRect) . splitHorizontallyBy f . mirrorRect
------------------------------------------------------------------------
-- Utilities
-- | Return workspace visible on screen 'sc', or 0.
screenWorkspace :: ScreenId -> X WorkspaceId
screenWorkspace sc = withWorkspace $ return . fromMaybe 0 . W.lookupWorkspace sc
-- | 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 = withWorkspace $ \w -> whenJust (W.peek w) f
withFocused f = withWindowSet $ \w -> whenJust (W.peek w) f
-- | True if window is under management by us
isClient :: Window -> X Bool
isClient w = withWorkspace $ return . W.member w
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

45
README
View File

@@ -40,38 +40,63 @@ 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
(Included with GHC)
unix http://hackage.haskell.org/cgi-bin/hackage-scripts/package/unix-2.0
(Included with GHC)
X11 http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-1.2.2
(Included with GHC)
X11-extras: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras-0.2
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/dons
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:
exec /home/dons/bin/xmonad
$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:

View File

@@ -1,16 +1,53 @@
{-# LANGUAGE PatternGuards #-}
-----------------------------------------------------------------------------
-- |
-- Module : StackSet
-- Copyright : (c) Don Stewart 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : dons@cse.unsw.edu.au
-- Maintainer : dons@galois.com
-- Stability : experimental
-- Portability : portable, Haskell 98
--
-----------------------------------------------------------------------------
--
-- ** Introduction
module StackSet (
-- * Introduction
-- $intro
StackSet(..), Workspace(..), Screen(..), StackOrNot, Stack(..), RationalRect(..),
-- * Construction
-- $construction
new, view, greedyView,
-- * Xinerama operations
-- $xinerama
lookupWorkspace,
screens, workspaces, allWindows,
-- * Operations on the current stack
-- $stackOperations
peek, index, integrate, integrate', differentiate,
focusUp, focusDown, focusMaster, focusWindow,
tagMember, renameTag, ensureTags, member, findIndex, 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
-- * Composite operations
-- $composite
shift, shiftWin,
-- for testing
abort
) where
import Prelude hiding (filter)
import Data.Maybe (listToMaybe,fromJust)
import qualified Data.List as L (deleteBy,find,splitAt,filter,nub)
import Data.List ( (\\) )
import qualified Data.Map as M (Map,insert,delete,empty)
-- $intro
--
-- The 'StackSet' data type encodes a window manager abstraction. The
-- window manager is a set of virtual workspaces. On each workspace is a
@@ -19,17 +56,17 @@
-- workspace is the one which will take user input. It can be visualised
-- as follows:
--
-- Workspace { 0*} { 1 } { 2 } { 3 } { 4 }
--
-- Windows [1 [] [3* [6*] []
-- ,2*] ,4
-- ,5]
-- > Workspace { 0*} { 1 } { 2 } { 3 } { 4 }
-- >
-- > Windows [1 [] [3* [6*] []
-- > ,2*] ,4
-- > ,5]
--
-- 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':
--
@@ -40,9 +77,9 @@
-- The Zipper lets us replace an item deep in a complex data
-- structure, e.g., a tree or a term, without an mutation. The
-- resulting data structure will share as much of its components with
-- the old structure as possible.
-- the old structure as possible.
--
-- Oleg Kiselyov, 27 Apr 2005, haskell@, "Zipper as a delimited continuation"
-- 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
@@ -57,8 +94,8 @@
-- Another good reference is:
--
-- The Zipper, Haskell wikibook
--
-- ** Xinerama support:
--
-- Xinerama support:
--
-- Xinerama in X11 lets us view multiple virtual workspaces
-- simultaneously. While only one will ever be in focus (i.e. will
@@ -67,89 +104,101 @@
-- (viewed) on which physical screens. We use a simple Map Workspace
-- Screen for this.
--
-- ** Master and Focus
-- Master and 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
-- 'delete'.
--
module StackSet (
StackSet(..), Workspace(..), Screen(..), Stack(..),
new, view, lookupWorkspace, peek, index, focusUp, focusDown,
focusWindow, member, findIndex, insertUp, delete, shift,
swapMaster, swapUp, swapDown, modify -- needed by users
) where
import Data.Maybe (listToMaybe)
import qualified Data.List as L (delete,find,genericSplitAt)
-- |
-- 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.
-- 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.
--
-- A cursor into a non-empty list of workspaces.
-- We puncture the workspace list, producing a hole in the structure
-- used to track the currently focused workspace. The two other lists
-- that are produced are used to track those workspaces visible as
-- Xinerama screens, and those workspaces not visible anywhere.
--
data StackSet i a sid =
StackSet { size :: !i -- number of workspaces
, current :: !(Screen i a sid) -- currently focused workspace
, visible :: [Screen i a sid] -- non-focused workspaces, visible in xinerama
, hidden :: [Workspace i a] -- workspaces not visible anywhere
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
} deriving (Show, Read, Eq)
-- Visible workspaces, and their Xinerama screens.
data Screen i a sid = Screen { workspace :: !(Workspace i a), screen :: !sid }
-- | Visible workspaces, and their Xinerama screens.
data Screen i l a sid sd = Screen { workspace :: !(Workspace i l a)
, screen :: !sid
, screenDetail :: !sd }
deriving (Show, Read, Eq)
--
-- |
-- A workspace is just a tag - its index - and a stack
--
data Workspace i a = Workspace { tag :: !i, stack :: Stack a }
data Workspace i l a = Workspace { tag :: !i, layout :: l, stack :: StackOrNot a }
deriving (Show, Read, Eq)
--
-- | A structure for window geometries
data RationalRect = RationalRect Rational Rational Rational Rational
deriving (Show, Read, Eq)
-- |
-- A stack is a cursor onto a (possibly empty) 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
-- flattening the cursor. The structure can be envisaged as:
--
-- +-- master: < '7' >
-- up | [ '2' ]
-- +--------- [ '3' ]
-- focus: < '4' >
-- dn +----------- [ '8' ]
-- > +-- master: < '7' >
-- > up | [ '2' ]
-- > +--------- [ '3' ]
-- > focus: < '4' >
-- > dn +----------- [ '8' ]
--
-- A 'Stack' can be viewed as a list with a hole punched in it to make
-- the focused position. Under the zipper/calculus view of such
-- the focused position. Under the zipper\/calculus view of such
-- structures, it is the differentiation of a [a], and integrating it
-- back has a natural implementation used in 'index'.
--
data Stack a = Empty
| Node { focus :: !a -- focused thing in this set
, up :: [a] -- clowns to the left
, down :: [a] } -- jokers to the right
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)
@@ -158,163 +207,251 @@ abort :: String -> a
abort x = error $ "xmonad: StackSet: " ++ x
-- ---------------------------------------------------------------------
-- Construction
-- $construction
-- | /O(n)/. Create a new stackset, of empty stacks, of size 'n', with
-- 'm' physical screens. 'm' should be less than or equal to 'n'.
-- The workspace with index '0' will be current.
-- | /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.
--
-- Xinerama: Virtual workspaces are assigned to physical screens, starting at 0.
--
new :: (Integral i, Integral s) => i -> s -> StackSet i a s
new n m | n > 0 && m > 0 = StackSet n cur visi unseen
| otherwise = abort "non-positive arguments to StackSet.new"
where (seen,unseen) = L.genericSplitAt m $ Workspace 0 Empty : [ Workspace i Empty | i <- [1 ..n-1]]
(cur:visi) = [ Screen i s | (i,s) <- zip seen [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 ]
-- 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'.
-- |
-- /O(w)/. Set focus to the workspace with index \'i\'.
-- 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
-- current.
-- is raised to the current screen. If it is already visible, focus is
-- just moved.
--
view :: (Eq i, Eq a, Eq s, Integral i) => i -> StackSet i a s -> StackSet i a s
view :: (Eq s, Eq i) => i -> StackSet i l a s sd -> StackSet i l a s sd
view i s
| i < 0 && i >= size s || i == tag (workspace (current s)) = s -- out of bounds or current
| not (i `tagMember` s)
|| i == tag (workspace (current s)) = s -- out of bounds or current
| Just x <- L.find ((i==).tag.workspace) (visible s)
-- if it is visible, it is just raised
= s { current = x, visible = current s : L.delete x (visible s) }
= s { current = x, visible = current s : L.deleteBy (equating screen) x (visible s) }
| Just x <- L.find ((i==).tag) (hidden s)
| Just x <- L.find ((i==).tag) (hidden s) -- must be hidden then
-- if it was hidden, it is raised on the xine screen currently used
= s { current = Screen x (screen (current s))
, hidden = workspace (current s) : L.delete x (hidden s) }
= s { current = (current s) { workspace = x }
, hidden = workspace (current s) : L.deleteBy (equating tag) x (hidden s) }
| otherwise = abort "Inconsistent StackSet: workspace not found"
| otherwise = s -- can't happen: all workspaces are either invalid, current, visible, or hidden
where equating f = \x y -> f x == f y
-- 'Catch'ing this might be hard. Relies on monotonically increasing
-- workspace tags defined in 'new'
--
-- and now tags are not monotonic, what happens here?
-- |
-- Set focus to the given workspace. If that workspace does not exist
-- in the stackset, the original workspace is returned. If that workspace is
-- 'hidden', then display that workspace on the current screen, and move the
-- current workspace to 'hidden'. If that workspace is 'visible' on another
-- screen, the workspaces of the current screen and the other screen are
-- swapped.
greedyView :: (Eq s, Eq i) => i -> StackSet i l a s sd -> StackSet i l a s sd
greedyView w ws
| any wTag (hidden ws) = view w ws
| (Just s) <- L.find (wTag . workspace) (visible ws)
= ws { current = (current ws) { workspace = workspace s }
, visible = s { workspace = workspace (current ws) }
: L.filter (not . wTag . workspace) (visible ws) }
| otherwise = ws
where wTag = (w == ) . tag
-- ---------------------------------------------------------------------
-- Xinerama operations
-- $xinerama
-- | Find the tag of the workspace visible on Xinerama screen 'sc'.
-- Nothing if screen is out of bounds.
lookupWorkspace :: Eq s => s -> StackSet i a s -> Maybe i
lookupWorkspace sc w = listToMaybe [ tag i | Screen i s <- current w : visible w, s == sc ]
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 ]
-- ---------------------------------------------------------------------
-- Operations on the current stack
-- $stackOperations
--
-- |
-- The 'with' function takes a default value, a function, and a
-- StackSet. If the current stack is Empty, 'with' returns the
-- StackSet. If the current stack is Nothing, 'with' returns the
-- default value. Otherwise, it applies the function to the stack,
-- returning the result. It is like 'maybe' for the focused workspace.
--
with :: b -> (Stack a -> b) -> StackSet i a s -> b
with dflt f s = case stack (workspace (current s)) of Empty -> dflt; v -> f v
-- TODO: ndm: a 'catch' proof here that 'f' only gets Node
-- constructors, hence all 'f's are safe below?
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 Empty, to modify the current stack.
--
modify :: Stack a -> (Stack a -> Stack a) -> StackSet i a s -> StackSet i a s
modify :: StackOrNot a -> (Stack a -> StackOrNot 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 }}}
-- |
-- Apply a function to modify the current stack if it isn't empty, and we don't
-- want to empty it.
--
-- /O(1)/. Extract the focused element of the current stack.
modify' :: (Stack a -> Stack a) -> StackSet i l a s sd -> StackSet i l a s sd
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.
--
peek :: StackSet i a s -> Maybe a
peek :: StackSet i l a s sd -> Maybe a
peek = with Nothing (return . focus)
-- |
-- /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]
integrate' = maybe [] integrate
-- |
-- /O(n)/. Texture a list.
--
differentiate :: [a] -> StackOrNot 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'.
--
filter :: (a -> Bool) -> Stack a -> StackOrNot 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
f':ls' -> Just $ Stack f' ls' [] -- else up
[] -> Nothing
-- |
-- /O(s)/. Extract the stack on the current workspace, as a list.
-- The order of the stack is determined by the master window -- it will be
-- the head of the list. The implementation is given by the natural
-- integration of a one-hole list cursor, back to a list.
--
index :: Eq a => StackSet i a s -> [a]
index = with [] $ \(Node t l r) -> reverse l ++ t : r
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/.
-- |
-- /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'
-- 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 a s -> StackSet i a s
focusUp = modify Empty $ \c -> case c of
Node _ [] [] -> c
Node t (l:ls) rs -> Node l ls (t:rs)
Node t [] rs -> Node x (xs ++ [t]) [] where (x:xs) = reverse rs
--
focusUp, focusDown, swapUp, swapDown :: StackSet i l a s sd -> StackSet i l a s sd
focusUp = modify' focusUp'
focusDown = modify' (reverseStack . focusUp' . reverseStack)
focusDown = modify Empty $ \c -> case c of
Node _ [] [] -> c
Node t ls (r:rs) -> Node r (t:ls) rs
Node t ls [] -> Node x [] (xs ++ [t]) where (x:xs) = reverse ls
swapUp = modify' swapUp'
swapDown = modify' (reverseStack . swapUp' . reverseStack)
swapUp = modify Empty $ \c -> case c of
Node _ [] [] -> c
Node t (l:ls) rs -> Node t ls (l:rs)
Node t [] rs -> Node t (reverse rs) []
focusUp', swapUp' :: 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)
swapDown = modify Empty $ \c -> case c of
Node _ [] [] -> c
Node t ls (r:rs) -> Node t (r:ls) rs
Node t ls [] -> Node t [] (reverse ls)
swapUp' (Stack t (l:ls) rs) = Stack t ls (l:rs)
swapUp' (Stack t [] rs) = Stack t (reverse rs) []
-- | reverse a stack: up becomes down and down becomes up.
reverseStack :: Stack a -> Stack a
reverseStack (Stack t ls rs) = Stack t rs ls
--
-- | /O(1) on current window, O(n) in general/. Focus the window 'w',
-- | /O(1) on current window, O(n) in general/. Focus the window 'w',
-- and set its workspace as current.
--
focusWindow :: (Integral i, Eq s, Eq a) => a -> StackSet i a s -> StackSet i a s
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
return $ until ((Just w ==) . peek) focusUp (view n s)
--
-- Finding if a window is in the stackset is a little tedious. We could
-- keep a cache :: Map a i, but with more bookkeeping.
--
-- | 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.
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
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?
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.
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.
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
et (i:is) rn s | i `tagMember` s = et is rn s
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.
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.
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.
member :: Eq a => a -> StackSet i a s -> Bool
member :: Eq a => a -> StackSet i l a s sd -> Bool
member a s = maybe False (const True) (findIndex 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 a s -> Maybe i
findIndex :: Eq a => a -> StackSet i l a s sd -> Maybe i
findIndex a s = listToMaybe
[ tag w | w <- workspace (current s) : map workspace (visible s) ++ hidden s, has a (stack w) ]
where has _ Empty = False
has x (Node t l r) = x `elem` (t : l ++ r)
[ 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)
-- ---------------------------------------------------------------------
-- Modifying the stackset
-- $modifyStackset
--
-- |
-- /O(n)/. (Complexity due to duplicate check). Insert a new element into
-- the stack, above the currently focused element.
--
@@ -328,71 +465,101 @@ findIndex a s = listToMaybe
-- Semantics in Huet's paper is that insert doesn't move the cursor.
-- However, we choose to insert above, and move the focus.
--
insertUp :: Eq a => a -> StackSet i a s -> StackSet i a s
insertUp :: Eq a => a -> StackSet i l a s sd -> StackSet i l a s sd
insertUp a s = if member a s then s else insert
where insert = modify (Node a [] []) (\(Node t l r) -> Node a l (t:r)) s
where insert = modify (Just $ Stack a [] []) (\(Stack t l r) -> Just $ Stack a l (t:r)) s
-- insertDown :: a -> StackSet i a s -> StackSet i a s
-- insertDown a = modify (Node a [] []) $ \(Node t l r) -> Node a (t:l) r
-- insertDown :: a -> StackSet i l a s sd -> StackSet i l a s sd
-- insertDown a = modify (Stack a [] []) $ \(Stack t l r) -> Stack a (t:l) r
-- Old semantics, from Huet.
-- > w { down = a : down w }
--
-- |
-- /O(1) on current window, O(n) in general/. Delete window 'w' if it exists.
-- There are 4 cases to consider:
--
-- * delete on an Empty workspace leaves it Empty
-- * 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 Empty
-- * 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 :: (Integral i, Eq a, Eq s) => a -> StackSet i a s -> StackSet i a s
delete w s | Just w == peek s = remove s -- common case.
| otherwise = maybe s (removeWindow.tag.workspace.current $ s) (findIndex w s)
where
-- find and remove window script
removeWindow o n = foldr ($) s [view o,remove,view n]
delete :: (Ord a, Eq s) => a -> StackSet i l a s sd -> StackSet i l a s sd
delete w = sink w . delete' w
-- actual removal logic, and focus/master logic:
remove = modify Empty $ \c ->
if focus c == w
then case c of
Node _ ls (r:rs) -> Node r ls rs -- try down first
Node _ (l:ls) [] -> Node l ls [] -- else up
Node _ [] [] -> Empty
else c { up = w `L.delete` up c, down = w `L.delete` down c }
-- | 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
delete' w s = s { current = removeFromScreen (current s)
, visible = map removeFromScreen (visible s)
, hidden = map removeFromWorkspace (hidden s) }
where removeFromWorkspace ws = ws { stack = stack ws >>= filter (/=w) }
removeFromScreen scr = scr { workspace = removeFromWorkspace (workspace scr) }
------------------------------------------------------------------------
-- Setting the master window
-- /O(s)/. Set the master window to the focused window.
-- | Given a window, and its preferred rectangle, set it as floating
-- 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) }
-- | Clear the floating status of a window
sink :: Ord a => a -> StackSet i l a s sd -> StackSet i l a s sd
sink w s = s { floating = M.delete w (floating s) }
------------------------------------------------------------------------
-- $settingMW
-- | /O(s)/. Set the master window to the focused window.
-- The old master window is swapped in the tiling order with the focused window.
-- Focus stays with the item moved.
swapMaster :: StackSet i a s -> StackSet i a s
swapMaster = modify Empty $ \c -> case c of
Node _ [] _ -> c -- already master.
Node t ls rs -> Node t [] (ys ++ x : rs) where (x:ys) = reverse ls
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
-- natural! keep focus, move current to the top, move top to current.
-- natural! keep focus, move current to the top, move top to current.
-- | /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
-- ---------------------------------------------------------------------
-- Composite operations
--
-- ---------------------------------------------------------------------
-- $composite
-- /O(w)/. shift. Move the focused element of the current stack to stack
-- | /O(w)/. shift. Move the focused element of the current stack to stack
-- 'n', leaving it as the focused element on that stack. The item is
-- inserted above the currently focused element on that workspace. --
-- The actual focused workspace doesn't change. If there is -- no
-- inserted above the currently focused element on that workspace.
-- The actual focused workspace doesn't change. If there is no
-- element on the current stack, the original stackSet is returned.
--
shift :: (Eq a, Eq s, Integral i) => i -> StackSet i a s -> StackSet i a s
shift n s = if and [n >= 0,n < size s,n /= tag (workspace (current s))]
then maybe s go (peek s) else s
where go w = foldr ($) s [view (tag (workspace (current s))),insertUp w,view n,delete w]
-- ^^ poor man's state monad :-)
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))
-- | /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
-- element on that stack. The item is inserted above the currently
-- 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

19
TODO
View File

@@ -1,8 +1,15 @@
- possibles:
- use more constrained type in StackSet to avoid pattern match warnings
- audit for events handled in dwm.
- Write down invariants for the window life cycle, especially:
- When are borders set? Prove that the current handling is sufficient.
- related:
- xcb bindings
- randr
= 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

205
XMonad.hs
View File

@@ -1,4 +1,5 @@
{-# OPTIONS -fglasgow-exts #-}
{-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.hs
@@ -9,57 +10,64 @@
-- 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, WorkspaceId(..), ScreenId(..), XState(..), XConf(..), Layout(..),
Typeable, Message, SomeMessage(..), fromMessage,
runX, io, withDisplay, withWorkspace, isRoot, spawn, restart, trace, whenJust, whenX
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 (StackSet)
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
, xineScreens :: ![Rectangle] -- ^ dimensions of each screen
, dimensions :: !(Position,Position) -- ^ dimensions of the screen,
, statusGaps :: ![(Int,Int,Int,Int)] -- ^ width of status bar on each screen
, layouts :: !(M.Map WorkspaceId (Layout, [Layout])) }
-- ^ mapping of workspaces to descriptions of their layouts
{ 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
, wmdelete :: !Atom -- ^ window deletion atom
, wmprotocols :: !Atom -- ^ wm protocols atom
, normalBorder :: !Color -- ^ border color of unfocused windows
, focusedBorder :: !Color } -- ^ border color of the focused window
, normalBorder :: !Pixel -- ^ border color of unfocused windows
, focusedBorder :: !Pixel } -- ^ border color of the focused window
type WindowSet = StackSet WorkspaceId Window ScreenId
type WindowSet = StackSet WorkspaceId (Layout Window) Window ScreenId ScreenDetail
type WindowSpace = Workspace WorkspaceId (Layout Window) Window
-- | Virtual workspace indicies
newtype WorkspaceId = W Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
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
@@ -74,47 +82,144 @@ newtype X a = X (ReaderT XConf (StateT XState IO) a)
-- | 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 ()
runX c st (X a) = runStateT (runReaderT a c) st >> return ()
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
withDisplay f = asks display >>= f
-- | Run a monadic action with the current workspace
withWorkspace :: (WindowSet -> X a) -> X a
withWorkspace f = gets windowset >>= 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"
------------------------------------------------------------------------
-- Layout handling
-- | 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', a pure function to layout a Window set 'modifyLayout',
-- 'modifyLayout' can be considered a branch of an exception handler.
--
data Layout = Layout { doLayout :: Rectangle -> [Window] -> X [(Window, Rectangle)]
, modifyLayout :: SomeMessage -> Maybe Layout }
-- '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
-- Based on ideas in /An Extensible Dynamically-Typed Hierarchy of Exceptions/,
-- Simon Marlow, 2006. Use extensible messages to the modifyLayout handler.
--
-- User-extensible messages must be a member of this class:
-- | 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.
--
@@ -122,12 +227,17 @@ fromMessage :: Message m => SomeMessage -> Maybe m
fromMessage (SomeMessage m) = cast m
-- ---------------------------------------------------------------------
-- General utilities
-- | Lift an IO action into the X monad
-- | 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
@@ -147,10 +257,10 @@ spawn x = io $ do
-- 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 . show . windowset) else return []
io $ catch (executeFile prog True args Nothing)
(hPutStrLn stderr . show) -- print executable not found exception
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 ()
@@ -160,13 +270,6 @@ whenJust mg f = maybe (return ()) f mg
whenX :: X Bool -> X () -> X ()
whenX a f = a >>= \b -> when b f
-- | Grab the X server (lock it) from the X monad
-- withServerX :: X () -> X ()
-- withServerX f = withDisplay $ \dpy -> do
-- io $ grabServer dpy
-- f
-- io $ ungrabServer dpy
-- | A 'trace' for the X monad. Logs a string to stderr. The result may
-- be found in your .xsession-errors file
trace :: String -> X ()

View File

@@ -40,7 +40,7 @@ ___KEYBINDINGS___
.SH EXAMPLES
To use \fBxmonad\fR as your window manager add:
.RS
exec xmonad
xmonad
.RE
to your \fI~/.xinitrc\fR file
.SH CUSTOMIZATION

8
tests/Main.hs Normal file
View File

@@ -0,0 +1,8 @@
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

View File

@@ -1,6 +1,8 @@
{-# OPTIONS -fglasgow-exts #-}
module Properties where
import StackSet
import StackSet hiding (filter)
import qualified StackSet as S (filter)
import Operations (tile)
import Debug.Trace
@@ -10,8 +12,10 @@ import Data.Ratio
import Data.Maybe
import System.Environment
import Control.Exception (assert)
import qualified Control.Exception as C
import Control.Monad
import Test.QuickCheck hiding (promote)
import System.IO.Unsafe
import System.IO
import System.Random hiding (next)
import Text.Printf
@@ -27,17 +31,20 @@ import qualified Data.Map as M
-- 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
-- * most ops on StackSet should either be trivially reversible, or
-- idempotent, or both.
--
-- The all important Arbitrary instance for StackSet.
--
instance (Integral i, Integral s, Eq a, Arbitrary a) => Arbitrary (StackSet i a s) where
instance (Integral i, Integral s, Eq a, Arbitrary a, Arbitrary l, Arbitrary sd)
=> Arbitrary (StackSet i l a s sd) where
arbitrary = do
sz <- choose (1,10) -- number of workspaces
n <- choose (0,sz-1) -- pick one to be in focus
sc <- choose (1,sz) -- a number of physical screens
sc <- choose (1,sz) -- a number of physical screens
lay <- arbitrary -- pick any layout
sds <- replicateM sc arbitrary
ls <- vector sz -- a vector of sz workspaces
-- pick a random item in each stack to focus
@@ -45,7 +52,7 @@ instance (Integral i, Integral s, Eq a, Arbitrary a) => Arbitrary (StackSet i a
else liftM Just (choose ((-1),length s-1))
| s <- ls ]
return $ fromList (fromIntegral n, fromIntegral sc,fs,ls)
return $ fromList (fromIntegral n, sds,fs,ls,lay)
coarbitrary = error "no coarbitrary for StackSet"
@@ -59,19 +66,14 @@ instance (Integral i, Integral s, Eq a, Arbitrary a) => Arbitrary (StackSet i a
-- 'fs' random focused window on each workspace
-- 'xs' list of list of windows
--
fromList :: (Integral i, Integral s, Eq a) => (i, s, [Maybe Int], [[a]]) -> StackSet i a s
fromList (_,_,_,[]) = error "Cannot build a StackSet from an empty list"
fromList :: (Integral i, Integral s, Eq a) => (i, [sd], [Maybe Int], [[a]], l) -> StackSet i l a s sd
fromList (_,_,_,[],_) = error "Cannot build a StackSet from an empty list"
fromList (n,m,fs,xs) | n < 0 || n >= genericLength xs
= error $ "Cursor index is out of range: " ++ show (n, length xs)
| m < 1 || m > genericLength xs
= error $ "Can't have more screens than workspaces: " ++ show (m, length xs)
fromList (o,m,fs,xs) =
fromList (o,m,fs,xs,l) =
let s = view o $
foldr (\(i,ys) s ->
foldr insertUp (view i s) ys)
(new (genericLength xs) m) (zip [0..] xs)
(new l [0..genericLength xs-1] m) (zip [0..] xs)
in foldr (\f t -> case f of
Nothing -> t
Just i -> foldr (const focusUp) t [0..i] ) s fs
@@ -81,7 +83,7 @@ fromList (o,m,fs,xs) =
--
-- Just generate StackSets with Char elements.
--
type T = StackSet Int Char Int
type T = StackSet (NonNegative Int) Int Char Int Int
-- Useful operation, the non-local workspaces
hidden_spaces x = map workspace (visible x) ++ hidden x
@@ -89,7 +91,7 @@ hidden_spaces x = map workspace (visible x) ++ hidden x
-- 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.
-- 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.
@@ -103,7 +105,6 @@ hidden_spaces x = map workspace (visible x) ++ hidden x
invariant (s :: T) = and
-- no duplicates
[ noDuplicates
, accurateSize
-- all this xinerama stuff says we don't have the right structure
-- , validScreens
@@ -114,10 +115,8 @@ invariant (s :: T) = and
where
ws = concat [ focus t : up t ++ down t
| w <- workspace (current s) : map workspace (visible s) ++ hidden s
, let t = stack w, t /= Empty ] :: [Char]
, t <- maybeToList (stack w)] :: [Char]
noDuplicates = nub ws == ws
calculatedSize = length (visible s) + length (hidden s) + 1 -- +1 is for current
accurateSize = calculatedSize == size s
-- validScreens = monotonic . sort . M. . (W.current s : W.visible : W$ s
@@ -134,21 +133,27 @@ monotonic (x:y:zs) | x == y-1 = monotonic (y:zs)
prop_invariant = invariant
-- and check other ops preserve invariants
prop_empty_I (n :: Positive Int) = forAll (choose (1,fromIntegral n)) $ \m ->
invariant $ new (fromIntegral n) m
prop_empty_I (n :: Positive Int) l = forAll (choose (1,fromIntegral n)) $ \m ->
forAll (vector m) $ \ms ->
invariant $ new l [0..fromIntegral n-1] ms
prop_view_I (n :: NonNegative Int) (x :: T) =
fromIntegral n < size x ==> invariant $ view (fromIntegral n) x
n `tagMember` x ==> invariant $ view (fromIntegral n) x
prop_greedyView_I (n :: NonNegative Int) (x :: T) =
n `tagMember` x ==> invariant $ view (fromIntegral n) x
prop_focusUp_I (n :: NonNegative Int) (x :: T) =
invariant $ foldr (const focusUp) x [1..n]
prop_focusMaster_I (n :: NonNegative Int) (x :: T) =
invariant $ foldr (const focusMaster) x [1..n]
prop_focusDown_I (n :: NonNegative Int) (x :: T) =
invariant $ foldr (const focusDown) x [1..n]
prop_focus_I (n :: NonNegative Int) (x :: T) =
case peek x of
Nothing -> True
Just _ -> let w = focus . stack . workspace . current $ foldr (const focusUp) x [1..n]
Just _ -> let w = focus . fromJust . stack . workspace . current $ foldr (const focusUp) x [1..n]
in invariant $ focusWindow w x
prop_insertUp_I n (x :: T) = invariant $ insertUp n x
@@ -166,41 +171,43 @@ prop_swap_right_I (n :: NonNegative Int) (x :: T) =
invariant $ foldr (const swapDown) x [1..n]
prop_shift_I (n :: NonNegative Int) (x :: T) =
fromIntegral n < size x ==> invariant $ shift (fromIntegral n) x
n `tagMember` x ==> invariant $ shift (fromIntegral n) x
prop_shift_win_I (n :: NonNegative Int) (w :: Char) (x :: T) =
n `tagMember` x && w `member` x ==> invariant $ shiftWin (fromIntegral n) w x
-- ---------------------------------------------------------------------
-- 'new'
-- empty StackSets have no windows in them
prop_empty (n :: Positive Int)
(m :: Positive Int) =
all (== Empty) [ stack w | w <- workspace (current x)
prop_empty (EmptyStackSet x) =
all (== Nothing) [ stack w | w <- workspace (current x)
: map workspace (visible x) ++ hidden x ]
where x = new (fromIntegral n) (fromIntegral m) :: T
-- empty StackSets always have focus on workspace 0
prop_empty_current (n :: Positive Int)
(m :: Positive Int) = tag (workspace $ current x) == 0
where x = new (fromIntegral n) (fromIntegral m) :: T
-- empty StackSets always have focus on first workspace
prop_empty_current (NonEmptyNubList ns) (NonEmptyNubList sds) l =
-- TODO, this is ugly
length sds <= length ns ==>
tag (workspace $ current x) == head ns
where x = new l ns sds :: T
-- no windows will be a member of an empty workspace
prop_member_empty i (n :: Positive Int) (m :: Positive Int)
= member i (new (fromIntegral n) (fromIntegral m) :: T) == False
prop_member_empty i (EmptyStackSet x)
= member i x == False
-- ---------------------------------------------------------------------
-- viewing workspaces
-- view sets the current workspace to 'n'
prop_view_current (x :: T) (n :: NonNegative Int) = i < size x ==>
prop_view_current (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
tag (workspace $ current (view i x)) == i
where
i = fromIntegral n
-- view *only* sets the current workspace, and touches Xinerama.
-- view *only* sets the current workspace, and touches Xinerama.
-- no workspace contents will be changed.
prop_view_local (x :: T) (n :: NonNegative Int) = i < size x ==>
prop_view_local (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
workspaces x == workspaces (view i x)
where
workspaces a = sortBy (\s t -> tag s `compare` tag t) $
@@ -209,22 +216,45 @@ prop_view_local (x :: T) (n :: NonNegative Int) = i < size x ==>
i = fromIntegral n
-- view should result in a visible xinerama screen
-- prop_view_xinerama (x :: T) (n :: NonNegative Int) = i < size x ==>
-- 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) r =
let i = fromIntegral $ r `mod` sz
sz = size x
in view i (view i x) == (view i x)
prop_view_idem (x :: T) (i :: NonNegative Int) = i `tagMember` x ==> view i (view i x) == (view i x)
-- view is reversible, though shuffles the order of hidden/visible
prop_view_reversible r (x :: T) = normal (view n (view i x)) == normal x
prop_view_reversible (i :: NonNegative Int) (x :: T) =
i `tagMember` x ==> normal (view n (view i x)) == normal x
where n = tag (workspace $ current x)
-- ---------------------------------------------------------------------
-- greedyViewing workspaces
-- greedyView sets the current workspace to 'n'
prop_greedyView_current (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
tag (workspace $ current (greedyView i x)) == i
where
i = fromIntegral n
-- greedyView *only* sets the current workspace, and touches Xinerama.
-- no workspace contents will be changed.
prop_greedyView_local (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
workspaces x == workspaces (greedyView i x)
where
workspaces a = sortBy (\s t -> tag s `compare` tag t) $
workspace (current a)
: map workspace (visible a) ++ hidden a
i = fromIntegral n
-- greedyView is idempotent
prop_greedyView_idem (x :: T) (i :: NonNegative Int) = i `tagMember` x ==> greedyView i (greedyView i x) == (greedyView i x)
-- greedyView is reversible, though shuffles the order of hidden/visible
prop_greedyView_reversible (i :: NonNegative Int) (x :: T) =
i `tagMember` x ==> normal (greedyView n (greedyView i x)) == normal x
where n = tag (workspace $ current x)
sz = size x
i = fromIntegral $ r `mod` sz
-- normalise workspace list
normal s = s { hidden = sortBy g (hidden s), visible = sortBy f (visible s) }
@@ -257,25 +287,25 @@ prop_member_peek (x :: T) =
-- the list returned by index should be the same length as the actual
-- windows kept in the zipper
prop_index_length (x :: T) =
case it of
Empty -> length (index x) == 0
Node {} -> length (index x) == length list
where
it = stack . workspace . current $ x
list = focus it : up it ++ down it
case stack . workspace . current $ x of
Nothing -> length (index x) == 0
Just it -> length (index x) == length (focus it : up it ++ down it)
-- ---------------------------------------------------------------------
-- rotating focus
--
-- master/focus
--
--
-- The tiling order, and master window, of a stack is unaffected by focus changes.
--
prop_focus_left_master (n :: NonNegative Int) (x::T) =
index (foldr (const focusUp) x [1..n]) == index x
prop_focus_right_master (n :: NonNegative Int) (x::T) =
index (foldr (const focusDown) x [1..n]) == index x
prop_focus_master_master (n :: NonNegative Int) (x::T) =
index (foldr (const focusMaster) x [1..n]) == index x
prop_focusWindow_master (n :: NonNegative Int) (x :: T) =
case peek x of
Nothing -> True
@@ -287,13 +317,16 @@ prop_focusWindow_master (n :: NonNegative Int) (x :: T) =
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 (n :: NonNegative Int) (x :: T) =
case peek x of
Nothing -> True
Just _ -> let s = index x
i = fromIntegral n `mod` length s
in (focus . stack . workspace . current) (focusWindow (s !! i) x) == (s !! i)
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
@@ -305,7 +338,10 @@ prop_focus_all_r (x :: T) = (foldr (const focusDown) x [1..n]) == x
-- f x' = foldr (\_ y -> rotate GT y) x' [1..n]
-- focus is local to the current workspace
prop_focus_local (x :: T) = hidden_spaces (focusDown x) == hidden_spaces x
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 (n :: NonNegative Int) (x::T ) =
case peek x of
@@ -324,17 +360,17 @@ prop_focusWindow_local (n :: NonNegative Int) (x::T ) =
prop_findIndex (x :: T) =
and [ tag w == fromJust (findIndex i x)
| w <- workspace (current x) : map workspace (visible x) ++ hidden x
, let t = stack w
, t /= Empty
, i <- focus (stack w) : up (stack w) ++ down (stack w)
, t <- maybeToList (stack w)
, i <- focus t : up t ++ down t
]
prop_allWindowsMember w (x :: T) = (w `elem` allWindows x) ==> member w x
-- ---------------------------------------------------------------------
-- 'insert'
-- inserting a item into an empty stackset means that item is now a member
prop_insert_empty i (n :: Positive Int) (m :: Positive Int) = member i (insertUp i x)
where x = new (fromIntegral n) (fromIntegral m) :: T
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)
@@ -347,10 +383,8 @@ prop_insert_local (x :: T) i = not (member i x) ==> hidden_spaces x == hidden_sp
-- Inserting a (unique) list of items into an empty stackset should
-- result in the last inserted element having focus.
prop_insert_peek (n :: Positive Int) (m :: Positive Int) (NonEmptyNubList is) =
prop_insert_peek (EmptyStackSet x) (NonEmptyNubList is) =
peek (foldr insertUp x is) == Just (head is)
where
x = new (fromIntegral n) (fromIntegral m) :: T
-- insert >> delete is the identity, when i `notElem` .
-- Except for the 'master', which is reset on insert and delete.
@@ -361,11 +395,10 @@ prop_insert_delete n x = not (member n x) ==> delete n (insertUp n y) == (y :: T
-- otherwise, we don't have a rule for where master goes.
-- inserting n elements increases current stack size by n
prop_size_insert is (n :: Positive Int) (m :: Positive Int) =
prop_size_insert is (EmptyStackSet x) =
size (foldr insertUp x ws ) == (length ws)
where
ws = nub is
x = new (fromIntegral n) (fromIntegral m) :: T
size = length . index
@@ -390,7 +423,7 @@ prop_delete_insert (x :: T) =
y = swapMaster x
-- delete should be local
prop_delete_local (x :: T) =
prop_delete_local (x :: T) =
case peek x of
Nothing -> True
Just i -> hidden_spaces x == hidden_spaces (delete i x)
@@ -398,6 +431,36 @@ prop_delete_local (x :: T) =
-- delete should not affect focus unless the focused element is what is being deleted
prop_delete_focus n (x :: T) = member n x && Just n /= peek x ==> peek (delete n 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 (x :: T) =
length (index x) > 1
==>
peek (delete n y) == peek (focusUp y)
where
n = last (index x)
y = focusWindow n x -- focus last window in stack
-- focus movement in the presence of delete:
-- when not in the last item in the stack, focus moves down
prop_delete_focus_not_end (x :: T) =
length (index x) > 1 &&
n /= last (index x)
==>
peek (delete n x) == peek (focusDown x)
where
Just n = peek x
-- ---------------------------------------------------------------------
-- filter
-- 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))
-- ---------------------------------------------------------------------
-- swapUp, swapDown, swapMaster: reordiring windows
@@ -442,15 +505,120 @@ prop_swap_master_idempotent (x :: T) = swapMaster (swapMaster x) == swapMaster x
-- shift is fully reversible on current window, when focus and master
-- are the same. otherwise, master may move.
prop_shift_reversible (r :: Int) (x :: T) =
let i = fromIntegral $ r `mod` sz
sz = size y
n = tag (workspace $ current y)
in case peek y of
Nothing -> True
Just _ -> normal ((view n . shift n . view i . shift i) y) == normal y
prop_shift_reversible i (x :: T) =
i `tagMember` x ==> case peek y of
Nothing -> True
Just _ -> normal ((view n . shift n . view i . shift i) y) == normal y
where
y = swapMaster x
n = tag (workspace $ current y)
-- ---------------------------------------------------------------------
-- shiftWin
-- shiftWin on current window is the same as shift
prop_shift_win_focus i (x :: T) =
i `tagMember` x ==> case peek x of
Nothing -> True
Just w -> shiftWin i w x == shift i x
-- shiftWin on a non-existant window is identity
prop_shift_win_indentity i w (x :: T) =
i `tagMember` x && not (w `member` x) ==> shiftWin i w x == x
-- shiftWin leaves the current screen as it is, if neither i is the tag
-- of the current workspace nor w on the current workspace
prop_shift_win_fix_current i w (x :: T) =
i `tagMember` x && w `member` x && i /= n && findIndex w x /= Just n
==> (current $ x) == (current $ shiftWin i w x)
where
n = tag (workspace $ current x)
------------------------------------------------------------------------
-- properties for the floating layer:
prop_float_reversible n (x :: T) =
n `member` x ==> sink n (float n geom x) == x
where
geom = RationalRect 100 100 100 100
-- check rectanges were set
{-
prop_float_sets_geometry n (x :: T) =
n `member` x ==> let y = float n geom x in M.lookup y (floating x) == Just geom
where
geom = RationalRect 100 100 100 100
-}
------------------------------------------------------------------------
prop_screens (x :: T) = n `elem` screens x
where
n = current x
prop_differentiate xs =
if null xs then differentiate xs == Nothing
else (differentiate xs) == Just (Stack (head xs) [] (tail xs))
where _ = xs :: [Int]
-- 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 (x :: T) =
visible x /= [] ==>
fromJust (lookupWorkspace scr x) `elem` tags
where
tags = [ tag (workspace y) | y <- visible x ]
scr = last [ screen y | y <- visible x ]
-- ---------------------------------------------------------------------
-- testing for failure
-- and help out hpc
prop_abort x = unsafePerformIO $ C.catch (abort "fail")
(\e -> return $ show e == "xmonad: StackSet: fail" )
where
_ = x :: Int
-- new should fail with an abort
prop_new_abort x = unsafePerformIO $ C.catch f
(\e -> return $ show e == "xmonad: StackSet: non-positive argument to StackSet.new" )
where
f = new undefined{-layout-} [] [] `seq` return False
_ = x :: Int
-- prop_view_should_fail = view {- with some bogus data -}
-- screens makes sense
prop_screens_works (x :: T) = screens x == current x : visible x
------------------------------------------------------------------------
-- renaming tags
-- | Rename a given tag if present in the StackSet.
-- 408 renameTag :: Eq i => i -> i -> StackSet i l a s sd -> StackSet i l a s sd
prop_rename1 (x::T) o n = o `tagMember` x && not (n `tagMember` x) ==>
let y = renameTag o n x
in n `tagMember` y
prop_ensure (x :: T) l xs = let y = ensureTags l xs x
in and [ n `tagMember` y | n <- xs ]
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)
------------------------------------------------------------------------
-- some properties for layouts:
@@ -459,7 +627,7 @@ prop_shift_reversible (r :: Int) (x :: T) =
{-
prop_tile_fullscreen rect = tile pct rect 1 1 == [rect]
-- multiple windows
-- multiple windows
prop_tile_non_overlap rect windows nmaster = noOverlaps (tile pct rect nmaster windows)
where _ = rect :: Rectangle
@@ -507,6 +675,12 @@ main = do
-- ,("view / xinerama" , mytest prop_view_xinerama)
,("view is local" , mytest prop_view_local)
,("greedyView : invariant" , mytest prop_greedyView_I)
,("greedyView sets current" , mytest prop_greedyView_current)
,("greedyView idempotent" , mytest prop_greedyView_idem)
,("greedyView reversible" , mytest prop_greedyView_reversible)
,("greedyView is local" , mytest prop_greedyView_local)
--
-- ,("valid workspace xinerama", mytest prop_lookupWorkspace)
,("peek/member " , mytest prop_member_peek)
@@ -514,20 +688,27 @@ main = do
,("index/length" , mytest prop_index_length)
,("focus left : invariant", mytest prop_focusUp_I)
,("focus master : invariant", mytest prop_focusMaster_I)
,("focus right: invariant", mytest prop_focusDown_I)
,("focusWindow: invariant", mytest prop_focus_I)
,("focus left/master" , mytest prop_focus_left_master)
,("focus right/master" , mytest prop_focus_right_master)
,("focus master/master" , mytest prop_focus_master_master)
,("focusWindow master" , mytest prop_focusWindow_master)
,("focus left/right" , mytest prop_focus_left)
,("focus right/left" , mytest prop_focus_right)
,("focus all left " , mytest prop_focus_all_l)
,("focus all right " , mytest prop_focus_all_r)
,("focus is local" , mytest prop_focus_local)
,("focus down is local" , mytest prop_focus_down_local)
,("focus up is local" , mytest prop_focus_up_local)
,("focus master is local" , mytest prop_focus_master_local)
,("focus master idemp" , mytest prop_focusMaster_idem)
,("focusWindow is local", mytest prop_focusWindow_local)
,("focusWindow works" , mytest prop_focusWindow_works)
,("findIndex" , mytest prop_findIndex)
,("allWindows/member" , mytest prop_allWindowsMember)
,("insert: invariant" , mytest prop_insertUp_I)
,("insert/new" , mytest prop_insert_empty)
@@ -544,6 +725,10 @@ main = do
,("delete is reversible", mytest prop_delete_insert)
,("delete is local" , mytest prop_delete_local)
,("delete/focus" , mytest prop_delete_focus)
,("delete last/focus up", mytest prop_delete_focus_end)
,("delete ~last/focus down", mytest prop_delete_focus_not_end)
,("filter preserves order", mytest prop_filter_order)
,("swapMaster: invariant", mytest prop_swap_master_I)
,("swapUp: invariant" , mytest prop_swap_left_I)
@@ -560,6 +745,30 @@ main = do
,("shift: invariant" , mytest prop_shift_I)
,("shift is reversible" , mytest prop_shift_reversible)
,("shiftWin: invariant" , mytest prop_shift_win_I)
,("shiftWin is shift on focus" , mytest prop_shift_win_focus)
,("shiftWin fix current" , mytest prop_shift_win_fix_current)
,("floating is reversible" , mytest prop_float_reversible)
,("screens includes current", mytest prop_screens)
,("differentiate works", mytest prop_differentiate)
,("lookupTagOnScreen", mytest prop_lookup_current)
,("lookupTagOnVisbleScreen", mytest prop_lookup_visible)
,("screens works", mytest prop_screens_works)
,("renaming works", mytest prop_rename1)
,("ensure works", mytest prop_ensure)
,("mapWorkspace id", mytest prop_mapWorkspaceId)
,("mapWorkspace inverse", mytest prop_mapWorkspaceInverse)
,("mapLayout id", mytest prop_mapLayoutId)
,("mapLayout inverse", mytest prop_mapLayoutInverse)
-- testing for failure:
,("abort fails", mytest prop_abort)
,("new fails with abort", mytest prop_new_abort)
,("shiftWin identity", mytest prop_shift_win_indentity)
-- renaming
{-
,("tile 1 window fullsize", mytest prop_tile_fullscreen)
@@ -704,7 +913,6 @@ instance (Eq a, Arbitrary a) => Arbitrary (NonEmptyNubList a) where
arbitrary = NonEmptyNubList `fmap` ((liftM nub arbitrary) `suchThat` (not . null))
coarbitrary = undefined
type Positive a = NonZero (NonNegative a)
newtype NonZero a = NonZero a
@@ -725,6 +933,16 @@ instance (Num a, Ord a, Arbitrary a) => Arbitrary (NonNegative a) where
]
coarbitrary = undefined
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
-- | Generates a value that satisfies a predicate.
suchThat :: Gen a -> (a -> Bool) -> Gen a
gen `suchThat` p =

View File

@@ -8,9 +8,7 @@ main = do foo <- getContents
putStrLn $ show loc
-- uncomment the following to check for mistakes in isntcomment
-- putStr $ unlines $ actual_loc
when (loc > 550) $ fail "Too many lines of code!"
isntcomment "" = False
isntcomment ('-':'-':_) = False
isntcomment ('{':'-':_) = False -- pragmas
isntcomment _ = True

View File

@@ -4,14 +4,14 @@
--
-- Format for the docstrings in Config.hs takes the following form:
--
-- -- mod-x @@ Frob the whatsit
-- -- 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
-- [ ((modMask .|. shiftMask, xK_Return), spawn "xterm") -- %! Launch an xterm
--
-- Here, mod-shift-return will be used as the keybinding name.
--
@@ -32,7 +32,7 @@ binding [ _, bindingLine, "", desc ] = (guessKeys bindingLine, desc)
binding [ _, _, keyCombo, desc ] = (keyCombo, desc)
allBindings :: String -> [(String, String)]
allBindings xs = map (binding . map trim) (xs =~ "(.*)--(.*)@@(.*)")
allBindings xs = map (binding . map trim) (xs =~ "(.*)--(.*)%!(.*)")
-- FIXME: What escaping should we be doing on these strings?
troff :: (String, String) -> String

View File

@@ -1,30 +1,30 @@
name: xmonad
version: 0.2
version: 0.4
homepage: http://xmonad.org
synopsis: A lightweight X11 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 maximise 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.
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
license: BSD3
license-file: LICENSE
author: Spencer Janssen
maintainer: sjanssen@cse.unl.edu
build-depends: base>=1.0, X11>=1.2.1, X11-extras>=0.2, mtl>=1.0, unix>=1.0
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
executable: xmonad
main-is: Main.hs
other-modules: Config Operations StackSet XMonad
ghc-options: -funbox-strict-fields -O -fasm -Wall -optl-Wl,-s
ghc-options: -funbox-strict-fields -O2 -fasm -Wall -optl-Wl,-s
ghc-prof-options: -prof -auto-all
extensions: GeneralizedNewtypeDeriving
-- Also requires deriving Typeable, PatternGuards