467 Commits
v0.3 ... v0.8.1

Author SHA1 Message Date
Spencer Janssen
5edfb1d262 Bump version to 0.8.1 2009-01-16 22:36:21 +00:00
Spencer Janssen
0fecae0abc Remove doubleFork, handle SIGCHLD
This is a rather big change.  Rather than make spawned processes become
children of init, we handle them in xmonad.  As a side effect of this change,
we never need to use waitForProcess in any contrib module -- in fact, doing so
will raise an exception.  The main benefit to handling SIGCHLD is that xmonad
can now be started with 'exec', and will correctly clean up after inherited
child processes.
2009-01-16 20:47:42 +00:00
gwern0
26f4f734f9 Main.hs: escape / in Haddocks
This lets haddocks for Main.hs, at least, to build with 2.3.0.
2008-12-07 02:09:15 +00:00
Daniel Schoepe
5e7df396b9 More flexible userCode function 2009-01-10 22:18:52 +00:00
Spencer Janssen
314ba78335 Call logHook as the very last action in windows 2008-12-09 23:37:00 +00:00
Spencer Janssen
7aa78ecc75 Accept inferior crossing events. This patch enables fmouse-focus-follows-screen 2008-12-05 04:51:30 +00:00
Spencer Janssen
ba8e26458e Tile all windows at once 2008-11-18 07:44:47 +00:00
Spencer Janssen
c627e8cc4d Factor rational rect scaling into a separate function 2008-11-18 07:28:49 +00:00
Spencer Janssen
04f894275d Change screen focus by clicking on the root window.
This is a modification of a patch from Joachim Breitner.
2008-11-06 22:40:31 +00:00
Spencer Janssen
edb752136f Fix #192. 2008-10-21 22:00:59 +00:00
Adam Vogt
2b463a632f select base < 4 for building on ghc 6.10 2008-10-13 21:45:09 +00:00
Joachim Breitner
ca122dd2cb add killWindow function
This is required to kill anything that is not focused, without
having to focus it first.
2008-10-05 00:18:04 +00:00
Devin Mullins
77657b65f9 add'l documentation 2008-09-27 23:46:39 +00:00
Spencer Janssen
28c57a837a Regression: ungrab buttons on *non* root windows 2008-10-07 21:43:51 +00:00
Spencer Janssen
afda20b56d Partial fix for #40
Improvements:
 - clicking on the root will change focus to that screen
 - moving the mouse from a window on a screen to an empty screen changes focus
   to that screen
The only remaining issue is that moving the mouse between two empty screens
does not change focus.  In order to solve this, we'd have to select motion events
on the root window, which is potentially expensive.
2008-10-07 21:20:53 +00:00
Spencer Janssen
0cc7b12fd0 Track mouse position via events received 2008-10-07 20:39:53 +00:00
Spencer Janssen
15a78ae715 Fix haddock 2008-10-07 09:46:41 +00:00
Spencer Janssen
18444799e0 Move screen locating code into pointScreen 2008-10-07 09:42:07 +00:00
Spencer Janssen
cc60fa73ad Make pointWithin a top-level binding 2008-10-07 09:02:29 +00:00
gwern0
8881e2ac78 sp README, CONFIG, STYLE, TODO 2008-09-13 02:44:57 +00:00
Spencer Janssen
533031e3d6 Use the same X11 dependency as xmonad-contrib 2008-09-21 06:15:08 +00:00
Spencer Janssen
76d4af15e4 Export focusUp' and focusDown' -- work entirely on stacks 2008-09-11 21:48:03 +00:00
Devin Mullins
74c6dd2721 add W.shiftMaster, fix float/tile-reordering bug 2008-09-11 05:39:09 +00:00
Spencer Janssen
b605fd9fce Spelling. Any bets on how long this has been there? 2008-09-05 19:52:11 +00:00
Spencer Janssen
85202ebd47 Bump version to 0.8 2008-09-05 19:42:25 +00:00
Spencer Janssen
328c660ce7 Remove obsolete comments about darcs X11 2008-09-05 19:49:15 +00:00
Spencer Janssen
b185a439b1 Recommend latest packages rather than specific versions 2008-09-05 19:48:37 +00:00
Spencer Janssen
0016e06984 Also remove -optl from the executable section 2008-08-20 21:00:23 +00:00
Spencer Janssen
339b2d0097 -optl-Wl,-s is not needed with recent Cabal versions 2008-08-20 20:41:02 +00:00
Malebria
5f4d63ba71 Haddock links 2008-06-01 21:25:15 +00:00
Malebria
942572c830 Haddock syntax for enumeration 2008-06-01 20:49:51 +00:00
Spencer Janssen
46ac2ca24b I prefer the spencerjanssen@gmail.com address now 2008-07-14 20:26:50 +00:00
Trevor Elliott
3830d7a571 Raise windows in the floating layer when moving or resizing 2008-05-21 21:50:57 +00:00
Devin Mullins
5b3eaf663a add currentTag convenience function 2008-05-11 22:42:58 +00:00
Spencer Janssen
c93b7c7c3b Make Mirror a newtype 2008-05-08 10:46:40 +00:00
Spencer Janssen
42dee4768e Comments 2008-05-07 01:31:22 +00:00
Spencer Janssen
e847b350ed Break long line 2008-05-07 01:26:08 +00:00
Spencer Janssen
cccbfa21e4 Style 2008-05-07 01:25:19 +00:00
Spencer Janssen
870b3ad282 Simplify 2008-05-07 01:13:09 +00:00
Spencer Janssen
ab30d76578 Overhaul Choose, fixes issue 183 2008-05-06 22:08:09 +00:00
Klaus Weidner
d8d636e573 Remember if focus changes were caused by mouse actions or by key commands
If the user used the mouse to change window focus (moving into or clicking on a
window), this should be handled differently than focus changes due to keyboard
commands. Specifically, it's inappropriate to discard window enter/leave events
while the mouse is moving. This fixes the bug where a fast mouse motion across
multiple windows resulted in the wrong window keeping focus.

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

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

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

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

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

82
CONFIG Normal file
View File

@@ -0,0 +1,82 @@
== Configuring xmonad ==
xmonad is configured by creating and editing the file:
~/.xmonad/xmonad.hs
xmonad then uses settings from this file as arguments to the window manager,
on startup. For a complete example of possible settings, see the file:
man/xmonad.hs
Further examples are on the website, wiki and extension documentation.
http://haskell.org/haskellwiki/Xmonad
== A simple example ==
Here is a basic example, which overrides the default border width,
default terminal, and some colours. This text goes in the file
$HOME/.xmonad/xmonad.hs :
import XMonad
main = xmonad $ defaultConfig
{ borderWidth = 2
, terminal = "urxvt"
, normalBorderColor = "#cccccc"
, focusedBorderColor = "#cd8b00" }
You can find the defaults in the file:
XMonad/Config.hs
== Checking your xmonad.hs is correct ==
Place this text in ~/.xmonad/xmonad.hs, and then check that it is
syntactically and type correct by loading it in the Haskell
interpreter:
$ ghci ~/.xmonad/xmonad.hs
GHCi, version 6.8.1: http://www.haskell.org/ghc/ :? for help
Loading package base ... linking ... done.
Ok, modules loaded: Main.
Prelude Main> :t main
main :: IO ()
Ok, looks good.
== Loading your configuration ==
To have xmonad start using your settings, type 'mod-q'. xmonad will
then load this new file, and run it. If it is unable to, the defaults
are used.
To load successfully, both 'xmonad' and 'ghc' must be in your $PATH
environment variable. If GHC isn't in your path, for some reason, you
can compile the xmonad.hs file yourself:
$ cd ~/.xmonad
$ ghc --make xmonad.hs
$ ls
xmonad xmonad.hi xmonad.hs xmonad.o
When you hit mod-q, this newly compiled xmonad will be used.
== Where are the defaults? ==
The default configuration values are defined in the source file:
XMonad/Config.hs
the XConfig data structure itself is defined in:
XMonad/Core.hs
== Extensions ==
Since the xmonad.hs file is just another Haskell module, you may import
and use any Haskell code or libraries you wish. For example, you can use
things from the xmonad-contrib library, or other code you write
yourself.

186
Config.hs
View File

@@ -1,186 +0,0 @@
-----------------------------------------------------------------------------
-- |
-- Module : Config.hs
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : dons@cse.unsw.edu.au
-- Stability : stable
-- Portability : portable
--
--
-- This module specifies configurable defaults for xmonad. If you change
-- values here, be sure to recompile and restart (mod-q) xmonad,
-- for the changes to take effect.
--
------------------------------------------------------------------------
module Config where
--
-- Useful imports
--
import XMonad
import Operations
import qualified StackSet as W
import Data.Ratio
import Data.Bits ((.|.))
import qualified Data.Map as M
import System.Exit
import Graphics.X11.Xlib
--
-- The number of workspaces (virtual screens, or window groups)
--
workspaces :: [WorkspaceId]
workspaces = [0..8]
-- |
-- 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
-- |
-- Default offset of drawable screen boundaries from each physical screen.
-- Anything non-zero here will leave a gap of that many pixels on the
-- given edge, on the that screen. A useful gap at top of screen for a
-- menu bar (e.g. 15)
--
-- An example, to set a top gap on monitor 1, and a gap on the bottom of
-- monitor 2, you'd use a list of geometries like so:
--
-- > defaultGaps = [(18,0,0,0),(0,18,0,0)] -- 2 gaps on 2 monitors
--
-- Fields are: top, bottom, left, right.
--
defaultGaps :: [(Int,Int,Int,Int)]
defaultGaps = [(0,0,0,0)] -- 15 for default dzen
-- |
-- numlock handling:
--
-- The mask for the numlock key. 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)
--
numlockMask :: KeyMask
numlockMask = mod2Mask
-- |
-- 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 tiling algorithms
--
defaultLayouts :: [Layout Window]
defaultLayouts = [ tiled , mirror tiled , full ]
where
-- default tiling algorithm partitions the screen into two panes
tiled = tall nmaster delta ratio
-- The default number of windows in the master pane
nmaster = 1
-- Default proportion of screen occupied by master pane
ratio = 1%2
-- Percent of screen to increment by when resizing panes
delta = 3%100
-- |
-- Perform an arbitrary action on each state change.
-- Examples include:
-- * do nothing
-- * log the state to stdout
--
logHook :: X ()
logHook = return ()
-- |
-- The key bindings list.
--
-- The unusual comment format is used to generate the documentation
-- automatically.
--
keys :: M.Map (KeyMask, KeySym) (X ())
keys = M.fromList $
-- launching and killing programs
[ ((modMask .|. shiftMask, xK_Return), spawn "xterm") -- @@ Launch an xterm
, ((modMask, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"") -- @@ Launch dmenu
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun") -- @@ Launch gmrun
, ((modMask .|. shiftMask, xK_c ), kill) -- @@ Close the focused window
, ((modMask, xK_space ), switchLayout) -- @@ Rotate through the available layout algorithms
, ((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
-- 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
-- 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_t ), withFocused sink) -- @@ Push window back into tiling
-- increase or decrease number of windows in the master area
, ((modMask , xK_comma ), sendMessage (IncMasterN 1)) -- @@ Increment the number of windows in the master area
, ((modMask , xK_period), sendMessage (IncMasterN (-1))) -- @@ Deincrement the number of windows in the master area
-- toggle the status bar gap
, ((modMask , xK_b ), modifyGap (\i n -> let x = (defaultGaps ++ repeat (0,0,0,0)) !! i in if n == x then (0,0,0,0) else x)) -- @@ Toggle the status bar gap
-- quit, or restart
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- @@ Quit xmonad
, ((modMask , xK_q ), 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 workspaces [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
++
[((m .|. modMask, key), screenWorkspace sc >>= flip whenJust f)
| (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
, (f, m) <- [(windows . W.view, 0), (shift, shiftMask)]]
-- |
-- 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 >> swapMaster))
-- mod-button3 @@ Set the window to floating mode and resize by dragging
, ((modMask, button3), (\w -> focus w >> mouseResizeWindow w)) ]

View File

@@ -1,8 +0,0 @@
module Config where
import Graphics.X11.Xlib.Types (Dimension)
import Graphics.X11.Xlib (KeyMask)
import XMonad
borderWidth :: Dimension
logHook :: X ()
numlockMask :: KeyMask
workspaces :: [WorkspaceId]

24
LICENSE
View File

@@ -1,27 +1,31 @@
Copyright (c) Spencer Janssen
Copyright (c) 2007,2008 Spencer Janssen
Copyright (c) 2007,2008 Don Stewart
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the author nor the names of his contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
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 FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

289
Main.hs
View File

@@ -1,6 +1,6 @@
-----------------------------------------------------------------------------
----------------------------------------------------------------------------
-- |
-- Module : Main.hs
-- Module : Main
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
@@ -12,243 +12,74 @@
--
-----------------------------------------------------------------------------
module Main where
import Data.Bits
import qualified Data.Map as M
import qualified Data.Set as S
import Control.Monad.Reader
import Control.Monad.State
import Data.Maybe (fromMaybe)
import System.Environment (getArgs)
import Graphics.X11.Xlib hiding (refreshKeyboardMapping)
import Graphics.X11.Xlib.Extras
import Graphics.X11.Xinerama (getScreenInfo)
module Main (main) where
import XMonad
import Config
import StackSet (new, floating, member, findIndex, workspace, tag, current, visible)
import qualified StackSet as W
import Operations
import System.IO
import System.Info
import System.Environment
import System.Posix.Process (executeFile)
-- |
-- The main entry point
--
import Paths_xmonad (version)
import Data.Version (showVersion)
#ifdef TESTING
import qualified Properties
#endif
-- | The entry point into xmonad. Attempts to compile any custom main
-- for xmonad, and if it doesn't find one, just launches the default.
main :: IO ()
main = do
dpy <- openDisplay ""
let dflt = defaultScreen dpy
rootw <- rootWindow dpy dflt
xinesc <- getScreenInfo dpy
nbc <- initColor dpy normalBorderColor
fbc <- initColor dpy focusedBorderColor
hSetBuffering stdout NoBuffering
installSignalHandlers -- important to ignore SIGCHLD to avoid zombies
args <- getArgs
let launch = catchIO buildLaunch >> xmonad defaultConfig
case args of
[] -> launch
["--resume", _] -> launch
["--help"] -> usage
["--recompile"] -> recompile True >> return ()
["--version"] -> putStrLn ("xmonad " ++ showVersion version)
#ifdef TESTING
("--run-tests":_) -> Properties.main
#endif
_ -> fail "unrecognized flags"
let winset | ("--resume" : s : _) <- args
, [(x, "")] <- reads s = x
| otherwise = new workspaces $ zipWith SD xinesc gaps
gaps = take (length xinesc) $ defaultGaps ++ repeat (0,0,0,0)
usage :: IO ()
usage = do
self <- getProgName
putStr . unlines $
concat ["Usage: ", self, " [OPTION]"] :
"Options:" :
" --help Print this message" :
" --version Print the version number" :
" --recompile Recompile your ~/.xmonad/xmonad.hs" :
#ifdef TESTING
" --run-tests Run the test suite" :
#endif
[]
safeLayouts = case defaultLayouts of [] -> (full, []); (x:xs) -> (x,xs)
cf = XConf
{ display = dpy
, theRoot = rootw
, normalBorder = nbc
, focusedBorder = fbc }
st = XState
{ windowset = winset
, layouts = M.fromList [(w, safeLayouts) | w <- workspaces]
, mapped = S.empty
, waitingUnmap = M.empty
, dragging = Nothing }
xSetErrorHandler -- in C, I'm too lazy to write the binding: dons
-- setup initial X environment
sync dpy False
selectInput dpy rootw $ substructureRedirectMask .|. substructureNotifyMask
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
grabKeys dpy rootw
grabButtons dpy rootw
sync dpy False
ws <- scan dpy rootw -- on the resume case, will pick up new windows
allocaXEvent $ \e ->
runX cf st $ do
-- walk workspace, resetting X states/mask for windows
-- TODO, general iterators for these lists.
sequence_ [ setInitialProperties w >> reveal w
| wk <- map W.workspace (W.current winset : W.visible winset)
, w <- W.integrate' (W.stack wk) ]
sequence_ [ setInitialProperties w >> hide w
| wk <- W.hidden winset
, w <- W.integrate' (W.stack wk) ]
mapM_ manage ws -- find new windows
refresh
-- main loop, for all you HOF/recursion fans out there.
forever $ handle =<< io (nextEvent dpy e >> getEvent e)
where forever a = a >> forever a
-- ---------------------------------------------------------------------
-- IO stuff. Doesn't require any X state
-- Most of these things run only on startup (bar grabkeys)
-- | scan for any new windows to manage. If they're already managed,
-- this should be idempotent.
scan :: Display -> Window -> IO [Window]
scan dpy rootw = do
(_, _, ws) <- queryTree dpy rootw
filterM ok ws
-- TODO: scan for windows that are either 'IsViewable' or where WM_STATE ==
-- Iconic
where ok w = do wa <- getWindowAttributes dpy w
a <- internAtom dpy "WM_STATE" False
p <- getWindowProperty32 dpy a w
let ic = case p of
Just (3:_) -> True -- 3 for iconified
_ -> False
return $ not (wa_override_redirect wa)
&& (wa_map_state wa == waIsViewable || ic)
-- | Grab the keys back
grabKeys :: Display -> Window -> IO ()
grabKeys dpy rootw = do
ungrabKey dpy anyKey anyModifier rootw
forM_ (M.keys keys) $ \(mask,sym) -> do
kc <- keysymToKeycode dpy sym
-- "If the specified KeySym is not defined for any KeyCode,
-- XKeysymToKeycode() returns zero."
when (kc /= '\0') $ mapM_ (grab kc . (mask .|.)) extraModifiers
where grab kc m = grabKey dpy kc m rootw True grabModeAsync grabModeAsync
grabButtons :: Display -> Window -> IO ()
grabButtons dpy rootw = do
ungrabButton dpy anyButton anyModifier rootw
mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) extraModifiers) (M.keys mouseBindings)
where grab button mask = grabButton dpy button mask rootw False buttonPressMask
grabModeAsync grabModeSync none none
-- ---------------------------------------------------------------------
-- | Event handler. Map X events onto calls into Operations.hs, which
-- modify our internal model of the window manager state.
-- | Build "~\/.xmonad\/xmonad.hs" with ghc, then execute it. If there are no
-- errors, this function does not return. An exception is raised in any of
-- these cases:
--
-- Events dwm handles that we don't:
-- * ghc missing
--
-- [ButtonPress] = buttonpress,
-- [Expose] = expose,
-- [PropertyNotify] = propertynotify,
-- * "~\/.xmonad\/xmonad.hs" missing
--
handle :: Event -> X ()
-- run window manager command
handle (KeyEvent {ev_event_type = t, ev_state = m, ev_keycode = code})
| t == keyPress = withDisplay $ \dpy -> do
s <- io $ keycodeToKeysym dpy code 0
whenJust (M.lookup (cleanMask m,s) keys) id
-- manage a new window
handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
wa <- io $ getWindowAttributes dpy w -- ignore override windows
-- need to ignore mapping requests by managed windows not on the current workspace
managed <- isClient w
when (not (wa_override_redirect wa) && not managed) $ do manage w
-- window destroyed, unmanage it
-- window gone, unmanage it
handle (DestroyWindowEvent {ev_window = w}) = whenX (isClient w) $ unmanage w
-- We track expected unmap events in waitingUnmap. We ignore this event unless
-- it is synthetic or we are not expecting an unmap notification from a window.
handle (UnmapEvent {ev_window = w, ev_send_event = synthetic}) = whenX (isClient w) $ do
e <- gets (fromMaybe 0 . M.lookup w . waitingUnmap)
if (synthetic || e == 0)
then unmanage w
else modify (\s -> s { waitingUnmap = M.adjust pred w (waitingUnmap s) })
-- set keyboard mapping
handle e@(MappingNotifyEvent {ev_window = w}) = do
io $ refreshKeyboardMapping e
when (ev_request e == mappingKeyboard) $ withDisplay $ io . flip grabKeys w
-- handle button release, which may finish dragging.
handle e@(ButtonEvent {ev_event_type = t})
| t == buttonRelease = do
drag <- gets dragging
case drag of
-- we're done dragging and have released the mouse:
Just (_,f) -> modify (\s -> s { dragging = Nothing }) >> f
Nothing -> broadcastMessage e
-- handle motionNotify event, which may mean we are dragging.
handle e@(MotionEvent {ev_event_type = _t, ev_x = x, ev_y = y}) = do
drag <- gets dragging
case drag of
Just (d,_) -> d (fromIntegral x) (fromIntegral y) -- we're dragging
Nothing -> broadcastMessage e
-- click on an unfocused window, makes it focused on this workspace
handle e@(ButtonEvent {ev_window = w,ev_event_type = t,ev_button = b })
| t == buttonPress = do
-- If it's the root window, then it's something we
-- grabbed in grabButtons. Otherwise, it's click-to-focus.
isr <- isRoot w
if isr then whenJust (M.lookup (cleanMask (ev_state e), b) mouseBindings) ($ ev_subwindow e)
else focus w
sendMessage e -- Always send button events.
-- entered a normal window, makes this focused.
handle e@(CrossingEvent {ev_window = w, ev_event_type = t})
| t == enterNotify && ev_mode e == notifyNormal
&& ev_detail e /= notifyInferior = focus w
-- left a window, check if we need to focus root
handle e@(CrossingEvent {ev_event_type = t})
| t == leaveNotify
= do rootw <- asks theRoot
when (ev_window e == rootw && not (ev_same_screen e)) $ setFocusX rootw
-- configure a window
handle e@(ConfigureRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
ws <- gets windowset
wa <- io $ getWindowAttributes dpy w
-- TODO temporary workaround for some bugs in float. Don't call 'float' on
-- windows that aren't visible, because it changes the focused screen
let vis = any ((== findIndex w ws) . Just . tag . workspace) (current ws : visible ws)
if (M.member w (floating ws) && vis)
|| 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
handle (ConfigureEvent {ev_window = w}) = whenX (isRoot w) rescreen
handle e = broadcastMessage e -- trace (eventName e) -- ignoring
-- * xmonad.hs fails to compile
--
-- ** wrong ghc in path (fails to compile)
--
-- ** type error, syntax error, ..
--
-- * Missing XMonad\/XMonadContrib modules due to ghc upgrade
--
buildLaunch :: IO ()
buildLaunch = do
recompile False
dir <- getXMonadDir
args <- getArgs
executeFile (dir ++ "/xmonad-"++arch++"-"++os) False args Nothing
return ()

View File

@@ -1,581 +0,0 @@
{-# OPTIONS -fno-warn-orphans -fglasgow-exts #-}
-- \^^ deriving Typeable
-- --------------------------------------------------------------------------
-- |
-- Module : Operations.hs
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : dons@cse.unsw.edu.au
-- Stability : unstable
-- Portability : not portable, Typeable deriving, mtl, posix
--
-- Operations.
--
-----------------------------------------------------------------------------
module Operations where
import XMonad
import qualified StackSet as W
import {-# SOURCE #-} Config (borderWidth,logHook,numlockMask)
import Data.Maybe
import Data.List (nub, (\\), find)
import Data.Bits ((.|.), (.&.), complement)
import Data.Ratio
import qualified Data.Map as M
import qualified Data.Set as S
import Control.Monad.State
import Control.Monad.Reader
import Control.Arrow ((***), first, second)
import System.IO
import Graphics.X11.Xlib
import Graphics.X11.Xinerama (getScreenInfo)
import Graphics.X11.Xlib.Extras
import qualified Data.Traversable as T
-- ---------------------------------------------------------------------
-- |
-- Window manager operations
-- manage. Add a new window to be managed in the current workspace.
-- Bring it into focus.
--
-- Whether the window is already managed, or not, it is mapped, has its
-- border set, and its event mask set.
--
manage :: Window -> X ()
manage w = whenX (fmap not $ isClient w) $ withDisplay $ \d -> do
setInitialProperties w >> reveal w
-- FIXME: This is pretty awkward. We can't can't let "refresh" happen
-- before the call to float, because that will resize the window and
-- lose the default sizing.
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)
if isFixedSize || isTransient
then do modify $ \s -> s { windowset = W.insertUp w (windowset s) }
float w -- \^^ now go the refresh.
else windows $ W.insertUp w
-- | unmanage. A window no longer exists, remove it from the window
-- list, on whatever workspace it is.
--
-- FIXME: clearFloating should be taken care of in W.delete, but if we do it
-- there, floating status is lost when moving windows between workspaces,
-- because W.shift calls W.delete.
--
-- should also unmap?
--
unmanage :: Window -> X ()
unmanage w = do
windows (W.sink w . W.delete w)
setWMState w 0 {-withdrawn-}
modify (\s -> s {mapped = S.delete w (mapped s), waitingUnmap = M.delete w (waitingUnmap s)})
-- | 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 = windows (W.shift n)
-- | view. Change the current workspace to workspace at offset n (0 indexed).
view :: WorkspaceId -> X ()
view = windows . W.greedyView
-- | Modify the size of the status gap at the top of the current screen
-- Taking a function giving the current screen, and current geometry.
modifyGap :: (Int -> (Int,Int,Int,Int) -> (Int,Int,Int,Int)) -> X ()
modifyGap f = do
windows $ \ws@(W.StackSet { W.current = c@(W.Screen { W.screenDetail = sd }) }) ->
let n = fromIntegral . W.screen $ c
g = f n . statusGap $ sd
in ws { W.current = c { W.screenDetail = sd { statusGap = g } } }
-- | Kill the currently focused client. If we do kill it, we'll get a
-- delete notify back from X.
--
-- There are two ways to delete a window. Either just kill it, or if it
-- supports the delete protocol, send a delete event (e.g. firefox)
--
kill :: X ()
kill = withDisplay $ \d -> withFocused $ \w -> do
wmdelt <- atom_WM_DELETE_WINDOW ; wmprot <- atom_WM_PROTOCOLS
protocols <- io $ getWMProtocols d w
io $ if wmdelt `elem` protocols
then allocaXEvent $ \ev -> do
setEventType ev clientMessage
setClientMessageEvent ev w wmprot 32 wmdelt 0
sendEvent d w False noEventMask ev
else killClient d w >> return ()
-- ---------------------------------------------------------------------
-- Managing windows
data UnDoLayout = UnDoLayout deriving ( Typeable, Eq )
instance Message UnDoLayout
-- | windows. Modify the current window list with a pure function, and refresh
windows :: (WindowSet -> WindowSet) -> X ()
windows f = do
-- Notify visible layouts to remove decorations etc
-- We cannot use sendMessage because this must not call refresh ever,
-- and must be called on all visible workspaces.
broadcastMessage UnDoLayout
XState { windowset = old, layouts = fls } <- get
let oldvisible = concatMap (W.integrate' . W.stack . W.workspace) $ W.current old : W.visible old
ws = f old
modify (\s -> s { windowset = ws })
d <- asks display
-- for each workspace, layout the currently visible workspaces
let allscreens = W.current ws : W.visible 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
Just l = fmap fst $ M.lookup n fls
flt = filter (flip M.member (W.floating ws)) (W.index this)
tiled = (W.stack . W.workspace . W.current $ this)
>>= W.filter (not . flip M.member (W.floating ws))
>>= W.filter (not . (`elem` vis))
(SD (Rectangle sx sy sw sh)
(gt,gb,gl,gr)) = W.screenDetail w
viewrect = Rectangle (sx + fromIntegral gl) (sy + fromIntegral gt)
(sw - fromIntegral (gl + gr)) (sh - fromIntegral (gt + gb))
-- just the tiled windows:
-- now tile the windows on this workspace, modified by the gap
(rs, ml') <- runLayout l viewrect tiled `catchX` runLayout full viewrect tiled
mapM_ (uncurry tileWindow) rs
whenJust ml' $ \l' -> modify $ \ss ->
ss { layouts = M.adjust (first (const l')) n (layouts ss) }
-- 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
setTopFocus
logHook
-- io performGC -- really helps, but seems to trigger GC bugs?
-- hide every window that was potentially visible before, but is not
-- given a position by a layout now.
mapM_ hide (nub oldvisible \\ visible)
clearEvents enterWindowMask
-- | setWMState. set the WM_STATE property
setWMState :: Window -> Int -> X ()
setWMState w v = withDisplay $ \dpy -> do
a <- atom_WM_STATE
io $ changeProperty32 dpy w a a propModeReplace [fromIntegral v, fromIntegral none]
-- | hide. Hide a window by unmapping it, and setting Iconified.
hide :: Window -> X ()
hide w = whenX (gets (S.member w . mapped)) $ withDisplay $ \d -> do
io $ do selectInput d w (clientMask .&. complement structureNotifyMask)
unmapWindow d w
selectInput d w clientMask
setWMState w 3 --iconic
-- this part is key: we increment the waitingUnmap counter to distinguish
-- between client and xmonad initiated unmaps.
modify (\s -> s { waitingUnmap = M.insertWith (+) w 1 (waitingUnmap s)
, mapped = S.delete w (mapped s) })
-- | reveal. Show a window by mapping it and setting Normal
-- this is harmless if the window was already visible
reveal :: Window -> X ()
reveal w = withDisplay $ \d -> do
setWMState w 1 --normal
io $ mapWindow d w
modify (\s -> s { mapped = S.insert w (mapped s) })
-- | The client events that xmonad is interested in
clientMask :: EventMask
clientMask = structureNotifyMask .|. enterWindowMask .|. propertyChangeMask
-- | Set some properties when we initially gain control of a window
setInitialProperties :: Window -> X ()
setInitialProperties w = withDisplay $ \d -> io $ do
selectInput d w $ clientMask
setWindowBorderWidth d w borderWidth
-- | refresh. Render the currently visible workspaces, as determined by
-- the StackSet. Also, set focus to the focused window.
--
-- This is our 'view' operation (MVC), in that it pretty prints our model
-- with X calls.
--
refresh :: X ()
refresh = windows id
-- | clearEvents. Remove all events of a given type from the event queue.
clearEvents :: EventMask -> X ()
clearEvents mask = withDisplay $ \d -> io $ do
sync d False
allocaXEvent $ \p -> fix $ \again -> do
more <- checkMaskEvent d mask p
when more again -- beautiful
-- | tileWindow. Moves and resizes w such that it fits inside the given
-- rectangle, including its border.
tileWindow :: Window -> Rectangle -> X ()
tileWindow w r = withDisplay $ \d -> do
bw <- (fromIntegral . wa_border_width) `liftM` io (getWindowAttributes d w)
-- give all windows at least 1x1 pixels
let least x | x <= bw*2 = 1
| otherwise = x - bw*2
io $ moveResizeWindow d w (rect_x r) (rect_y r)
(least $ rect_width r) (least $ rect_height r)
reveal w
-- ---------------------------------------------------------------------
-- | rescreen. The screen configuration may have changed (due to
-- xrandr), update the state and refresh the screen, and reset the gap.
rescreen :: X ()
rescreen = do
xinesc <- withDisplay (io . getScreenInfo)
windows $ \ws@(W.StackSet { W.current = v, W.visible = vs, W.hidden = hs }) ->
let (xs, ys) = splitAt (length xinesc) $ map W.workspace (v:vs) ++ hs
(a:as) = zipWith3 W.Screen xs [0..] $ zipWith SD xinesc gs
sgs = map (statusGap . W.screenDetail) (v:vs)
gs = take (length xinesc) (sgs ++ repeat (0,0,0,0))
in ws { W.current = a
, W.visible = as
, W.hidden = ys }
-- ---------------------------------------------------------------------
-- | setButtonGrab. Tell whether or not to intercept clicks on a given window
setButtonGrab :: Bool -> Window -> X ()
setButtonGrab grab w = withDisplay $ \d -> io $
if grab
then forM_ [button1, button2, button3] $ \b ->
grabButton d b anyModifier w False buttonPressMask
grabModeAsync grabModeSync none none
else ungrabButton d anyButton anyModifier w
-- ---------------------------------------------------------------------
-- Setting keyboard focus
-- | Set the focus to the window on top of the stack, or root
setTopFocus :: X ()
setTopFocus = withWindowSet $ maybe (setFocusX =<< asks theRoot) setFocusX . W.peek
-- | Set focus explicitly to window 'w' if it is managed by us, or root.
-- This happens if X notices we've moved the mouse (and perhaps moved
-- the mouse to a new screen).
focus :: Window -> X ()
focus w = withWindowSet $ \s -> do
if W.member w s then when (W.peek s /= Just w) $ windows (W.focusWindow w)
else whenX (isRoot w) $ setFocusX w
-- | Call X to set the keyboard focus details.
setFocusX :: Window -> X ()
setFocusX w = withWindowSet $ \ws -> do
XConf { display = dpy , normalBorder = nbc, focusedBorder = fbc } <- ask
-- clear mouse button grab and border on other windows
forM_ (W.current ws : W.visible ws) $ \wk -> do
forM_ (W.index (W.view (W.tag (W.workspace wk)) ws)) $ \otherw -> do
setButtonGrab True otherw
io $ setWindowBorder dpy otherw 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
io $ setWindowBorder dpy w fbc
-- ---------------------------------------------------------------------
-- Managing layout
-- | 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.
--
-- Note that the new layout's deconstructor will be called, so it should be
-- idempotent.
switchLayout :: X ()
switchLayout = do
broadcastMessage UnDoLayout -- calling refresh now would defeat the point of deconstruction
n <- gets (W.tag . W.workspace . W.current . windowset)
modify $ \s -> s { layouts = M.adjust switch n (layouts s) }
refresh
where switch (x, xs) = let xs' = xs ++ [x] in (head xs', tail xs')
-- | Throw a message to the current Layout possibly modifying how we
-- layout the windows, then refresh.
--
sendMessage :: Message a => a -> X ()
sendMessage a = do n <- (W.tag . W.workspace . W.current) `fmap` gets windowset
Just (l,ls) <- M.lookup n `fmap` gets layouts
ml' <- modifyLayout l (SomeMessage a) `catchX` return (Just l)
whenJust ml' $ \l' -> do modify $ \s -> s { layouts = M.insert n (l',ls) (layouts s) }
refresh
-- | 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 = do
ol <- gets layouts
nl <- T.forM ol $ \ (l,ls) -> maybe (l,ls) (flip (,) ls) `fmap`
(modifyLayout l (SomeMessage a) `catchX` return (Just l))
modify $ \s -> s { layouts = nl }
instance Message Event
--
-- Builtin layout algorithms:
--
-- fullscreen mode
-- tall mode
--
-- The latter algorithms support the following operations:
--
-- Shrink
-- Expand
--
data Resize = Shrink | Expand deriving Typeable
data IncMasterN = IncMasterN Int deriving Typeable
instance Message Resize
instance Message IncMasterN
-- simple fullscreen mode, just render all windows fullscreen.
-- a plea for tuple sections: map . (,sc)
full :: Layout a
full = Layout { doLayout = \sc (W.Stack f _ _) -> return ([(f, sc)],Nothing)
, modifyLayout = const (return Nothing) } -- no changes
--
-- The tiling mode of xmonad, and its operations.
--
tall :: Int -> Rational -> Rational -> Layout a
tall nmaster delta frac =
Layout { doLayout = \r -> return . (\x->(x,Nothing)) .
ap zip (tile frac r nmaster . length) . W.integrate
, modifyLayout = \m -> return $ 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
-- | Mirror a rectangle
mirrorRect :: Rectangle -> Rectangle
mirrorRect (Rectangle rx ry rw rh) = (Rectangle ry rx rh rw)
-- | Mirror a layout, compute its 90 degree rotated form.
mirror :: Layout a -> Layout a
mirror (Layout { doLayout = dl, modifyLayout = ml }) =
Layout { doLayout = \sc w -> do (wrs, ml') <- dl (mirrorRect sc) w
return (map (second mirrorRect) wrs, mirror `fmap` ml')
, modifyLayout = fmap (fmap mirror) . ml }
-- | tile. Compute the positions for windows using the default 2 pane tiling algorithm.
--
-- The screen is divided (currently) into two panes. all clients are
-- then partioned between these two panes. one pane, the `master', by
-- convention has the least number of windows in it (by default, 1).
-- the variable `nmaster' controls how many windows are rendered in the
-- master pane.
--
-- `delta' specifies the ratio of the screen to resize by.
--
-- 'frac' specifies what proportion of the screen to devote to the
-- master area.
--
tile :: Rational -> Rectangle -> Int -> Int -> [Rectangle]
tile f r nmaster n = if n <= nmaster || nmaster == 0
then splitVertically n r
else splitVertically nmaster r1 ++ splitVertically (n-nmaster) r2 -- two columns
where (r1,r2) = splitHorizontallyBy f r
--
-- Divide the screen vertically into n subrectangles
--
splitVertically, splitHorizontally :: Int -> Rectangle -> [Rectangle]
splitVertically n r | n < 2 = [r]
splitVertically n (Rectangle sx sy sw sh) = Rectangle sx sy sw smallh :
splitVertically (n-1) (Rectangle sx (sy+fromIntegral smallh) sw (sh-smallh))
where smallh = sh `div` fromIntegral n --hmm, this is a fold or map.
splitHorizontally n = map mirrorRect . splitVertically n . mirrorRect
-- Divide the screen into two rectangles, using a rational to specify the ratio
splitHorizontallyBy, splitVerticallyBy :: RealFrac r => r -> Rectangle -> (Rectangle, Rectangle)
splitHorizontallyBy f (Rectangle sx sy sw sh) =
( Rectangle sx sy leftw sh
, Rectangle (sx + fromIntegral leftw) sy (sw-fromIntegral leftw) sh)
where leftw = floor $ fromIntegral sw * f
splitVerticallyBy f = (mirrorRect *** mirrorRect) . splitHorizontallyBy f . mirrorRect
------------------------------------------------------------------------
-- Utilities
-- | Return workspace visible on screen 'sc', or Nothing.
screenWorkspace :: ScreenId -> X (Maybe WorkspaceId)
screenWorkspace sc = withWindowSet $ return . W.lookupWorkspace sc
-- | Apply an X operation to the currently focused window, if there is one.
withFocused :: (Window -> X ()) -> X ()
withFocused f = withWindowSet $ \w -> whenJust (W.peek w) f
-- | True if window is under management by us
isClient :: Window -> X Bool
isClient w = withWindowSet $ return . W.member w
-- | Combinations of extra modifier masks we need to grab keys\/buttons for.
-- (numlock and capslock)
extraModifiers :: [KeyMask]
extraModifiers = [0, numlockMask, lockMask, numlockMask .|. lockMask ]
-- | Strip numlock\/capslock from a mask
cleanMask :: KeyMask -> KeyMask
cleanMask = (complement (numlockMask .|. lockMask) .&.)
-- | Get the Pixel value for a named color
initColor :: Display -> String -> IO Pixel
initColor dpy c = (color_pixel . fst) `liftM` allocNamedColor dpy colormap c
where colormap = defaultColormap dpy (defaultScreen dpy)
------------------------------------------------------------------------
-- | Floating layer support
-- | Make a floating window tiled
sink :: Window -> X ()
sink = windows . W.sink
-- | Make a tiled window floating, using its suggested rectangle
--
-- TODO: float changes the set of visible workspaces when we call it for an
-- invisible window -- this should not happen. See 'temporary workaround' in
-- the handler for ConfigureRequestEvent also.
float :: Window -> X ()
float w = withDisplay $ \d -> do
ws <- gets windowset
wa <- io $ getWindowAttributes d w
let sc = fromMaybe (W.current ws) $ find (pointWithin (fi $ wa_x wa) (fi $ wa_y wa) . screenRect . W.screenDetail) $ W.current ws : W.visible ws
sr = screenRect . W.screenDetail $ sc
sw = W.tag . W.workspace $ sc
bw = fi . wa_border_width $ wa
windows $ maybe id W.focusWindow (W.peek ws) . W.shift sw . W.focusWindow w . W.float w
(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)))
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)
-- ---------------------------------------------------------------------
-- 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
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)
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 ((fromIntegral (max 1 (ex - fromIntegral (wa_x wa)))),
(fromIntegral (max 1 (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 :: 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

157
README
View File

@@ -1,71 +1,99 @@
xmonad : a lightweight X11 window manager.
xmonad : a tiling window manager
http://xmonad.org
------------------------------------------------------------------------
xmonad is a tiling window manager for X. Windows are arranged
automatically to tile the screen without gaps or overlap, maximising
screen use. Window manager features are accessible from the
keyboard: a mouse is optional. xmonad is written, configured and
extensible in Haskell. Custom layout algorithms, key bindings and
other extensions may be written by the user in config files. Layouts
are applied dynamically, and different layouts may be used on each
workspace. Xinerama is fully supported, allowing windows to be tiled
on several physical screens.
About:
Quick start:
Xmonad is a tiling window manager for X. Windows are managed using
automatic tiling algorithms, which can be dynamically configured.
Windows are arranged so as to tile the screen without gaps, maximising
screen use. All features of the window manager are accessible
from the keyboard: a mouse is strictly optional. Xmonad is written
and extensible in Haskell, and custom layout algorithms may be
implemented by the user in config files. A guiding principle of the
user interface is <i>predictability</i>: users should know in
advance precisely the window arrangement that will result from any
action, leading to an intuitive user interface.
Obtain the dependent libraries, then build with:
Xmonad provides three tiling algorithms by default: tall, wide and
fullscreen. In tall or wide mode, all windows are visible and tiled
to fill the plane without gaps. In fullscreen mode only the focused
window is visible, filling the screen. Alternative tiling
algorithms are provided as extensions. Sets of windows are grouped
together on virtual workspaces and each workspace retains its own
layout. Multiple physical monitors are supported via Xinerama,
allowing simultaneous display of several workspaces.
runhaskell Setup.lhs configure --user --prefix=$HOME
runhaskell Setup.lhs build
runhaskell Setup.lhs install --user
Adhering to a minimalist philosophy of doing one job, and doing it
well, the entire code base remains tiny, and is written to be simple
to understand and modify. By using Haskell as a configuration
language arbitrarily complex extensions may be implemented by the
user using a powerful `scripting' language, without needing to
modify the window manager directly. For example, users may write
their own tiling algorithms.
------------------------------------------------------------------------
For the full story, read on.
Building:
Get the dependencies
Building is quite straightforward, and requires a basic Haskell toolchain.
On many systems xmonad is available as a binary package in your
package system (e.g. on Debian or Gentoo). If at all possible, use this
in preference to a source build, as the dependency resolution will be
simpler.
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'
We'll now walk through the complete list of toolchain dependencies.
mtl http://hackage.haskell.org/cgi-bin/hackage-scripts/package/mtl-1.0
unix http://hackage.haskell.org/cgi-bin/hackage-scripts/package/unix-2.0
X11 http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-1.2.2
X11-extras: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras-0.3
* GHC: the Glasgow Haskell Compiler
And then build with Cabal:
You first need a Haskell compiler. Your distribution's package
system will have binaries of GHC (the Glasgow Haskell Compiler), the
compiler we use, so install that first. If your operating system's
package system doesn't provide a binary version of GHC, you can find
them here:
runhaskell Setup.lhs configure --prefix=$HOME
runhaskell Setup.lhs build
runhaskell Setup.lhs install --user
http://haskell.org/ghc
------------------------------------------------------------------------
For example, in Debian you would install GHC with:
Notes for using the darcs version
apt-get install ghc6
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.
It shouldn't be necessary to compile GHC from source -- every common
system has a pre-build binary version.
darcs get http://darcs.haskell.org/~sjanssen/X11-extras
* X11 libraries:
Not using X11-extras from darcs, is the most common reason for the
darcs version of xmonad to fail to build.
Since you're building an X application, 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
Typically you need: libXinerama libXext libX11
* Cabal
xmonad requires a recent version of Cabal, >= 1.2.0. If you're using
GHC 6.8, then it comes bundled with the right version. If you're
using GHC 6.6.x, you'll need to build and install Cabal from hackage
first:
http://hackage.haskell.org/cgi-bin/hackage-scripts/package/Cabal
You can check which version you have with the command:
$ ghc-pkg list Cabal
Cabal-1.2.2.0
* Haskell libraries: mtl, unix, X11
Finally, you need the Haskell libraries xmonad depends on. Since
you've a working GHC installation now, most of these will be
provided. To check whether you've got a package run 'ghc-pkg list
some_package_name'. You will need the following packages:
mtl http://hackage.haskell.org/cgi-bin/hackage-scripts/package/mtl
unix http://hackage.haskell.org/cgi-bin/hackage-scripts/package/unix
X11 http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11
* Build xmonad:
Once you've got all the dependencies in place (which should be
straightforward), build xmonad:
runhaskell Setup.lhs configure --user --prefix=$HOME
runhaskell Setup.lhs build
runhaskell Setup.lhs install --user
And you're done!
------------------------------------------------------------------------
@@ -79,33 +107,40 @@ Running xmonad:
------------------------------------------------------------------------
Configuring:
See the CONFIG document
------------------------------------------------------------------------
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:
There are many extensions to xmonad available in the XMonadContrib
(xmc) library. Examples include an ion3-like tabbed layout, a
prompt/program launcher, and various other useful modules.
XMonadContrib is available at:
0.3 release: http://www.xmonad.org/XMonadContrib-0.3.tar.gz
latest release: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmonad-contrib
darcs version: darcs get http://darcs.haskell.org/~sjanssen/XMonadContrib
darcs version: darcs get http://code.haskell.org/XMonadContrib
------------------------------------------------------------------------
Other useful programs:
For a program dispatch menu:
A nicer xterm replacement, that supports resizing better:
dmenu http://www.suckless.org/download/
or
gmrun (in your package system)
urxvt http://software.schmorp.de/pkg/rxvt-unicode.html
For custom status bars:
dzen http://gotmor.googlepages.com/dzen
dzen http://gotmor.googlepages.com/dzen
xmobar http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmobar
A nicer xterm replacment, that supports resizing better:
For a program dispatch menu:
urxvt http://software.schmorp.de/pkg/rxvt-unicode.html
dmenu http://www.suckless.org/download/
gmrun (in your package system)
Authors:

21
STYLE Normal file
View File

@@ -0,0 +1,21 @@
== Coding guidelines for contributing to
== xmonad and the xmonad contributed extensions
* Comment every top level function (particularly exported functions), and
provide a type signature; use Haddock syntax in the comments.
* Follow the coding style of the other modules.
* Code should be compilable with -Wall -Werror. There should be no warnings.
* Partial functions should be avoided: the window manager should not
crash, so do not call `error` or `undefined`
* Tabs are illegal. Use 4 spaces for indenting.
* Any pure function added to the core should have QuickCheck properties
precisely defining its behavior.
* New modules should identify the author, and be submitted under
the same license as xmonad (BSD3 license or freer).

30
TODO
View File

@@ -1,17 +1,21 @@
0.3 release:
* stable contrib repo tarball
* haddocks for core and contribs on xmonad.org
* tag xmonad
* tag X11-extras
* tag X11
* more QC tests
- Write down invariants for the window life cycle, especially:
- When are borders set? Prove that the current handling is sufficient.
- current floating layer handling is nonoptimal. FocusUp should raise,
for example
- possibles:
- use more constrained type in StackSet to avoid pattern match warnings
- audit for events handled in dwm.
- Issues still with stacking order.
- related:
- xcb bindings
- randr
= Release management =
* configuration documentation
* generate haddocks for core and XMC, upload to xmonad.org
* generate manpage, generate html manpage
* double check README build instructions
* test core with 6.6 and 6.8
* bump xmonad.cabal version and X11 version
* upload X11 and xmonad to Hackage
* check examples/text in user-facing Config.hs
* check tour.html and intro.html are up to date, and mention all core bindings
* confirm template config is type correct

239
XMonad.hs
View File

@@ -1,212 +1,47 @@
{-# OPTIONS -fglasgow-exts #-}
-----------------------------------------------------------------------------
--------------------------------------------------------------------
-- |
-- Module : XMonad.hs
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
-- Module : XMonad
-- Copyright : (c) Don Stewart
-- License : BSD3
--
-- Maintainer : sjanssen@cse.unl.edu
-- Stability : unstable
-- Portability : not portable, uses cunning newtype deriving
-- Maintainer: Don Stewart <dons@galois.com>
-- Stability : provisional
-- Portability:
--
-- The X monad, a state monad transformer over IO, for the window
-- manager state, and support routines.
--------------------------------------------------------------------
--
-----------------------------------------------------------------------------
-- Useful exports for configuration files.
module XMonad (
X, WindowSet, WorkspaceId(..), ScreenId(..), ScreenDetail(..), XState(..), XConf(..), Layout(..),
Typeable, Message, SomeMessage(..), fromMessage, runLayout,
runX, catchX, io, catchIO, withDisplay, withWindowSet, isRoot, spawn, restart, trace, whenJust, whenX,
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW
) where
import StackSet
module XMonad.Main,
module XMonad.Core,
module XMonad.Config,
module XMonad.Layout,
module XMonad.ManageHook,
module XMonad.Operations,
module Graphics.X11,
module Graphics.X11.Xlib.Extras,
(.|.),
MonadState(..), gets, modify,
MonadReader(..), asks,
MonadIO(..)
) where
-- core modules
import XMonad.Main
import XMonad.Core
import XMonad.Config
import XMonad.Layout
import XMonad.ManageHook
import XMonad.Operations
-- import XMonad.StackSet -- conflicts with 'workspaces' defined in XMonad.hs
-- modules needed to get basic configuration working
import Data.Bits
import Graphics.X11 hiding (refreshKeyboardMapping)
import Graphics.X11.Xlib.Extras
import Control.Monad.State
import Control.Monad.Reader
import System.IO
import System.Posix.Process (executeFile, forkProcess, getProcessStatus, createSession)
import System.Exit
import System.Environment
import Graphics.X11.Xlib
-- for Read instance
import Graphics.X11.Xlib.Extras ()
import Data.Typeable
import qualified Data.Map as M
import qualified Data.Set as S
-- | XState, the window manager state.
-- Just the display, width, height and a window list
data XState = XState
{ windowset :: !WindowSet -- ^ workspace list
, mapped :: !(S.Set Window) -- ^ the Set of mapped windows
, waitingUnmap :: !(M.Map Window Int) -- ^ the number of expected UnmapEvents
, layouts :: !(M.Map WorkspaceId (Layout Window, [Layout Window]))
-- ^ mapping of workspaces to descriptions of their layouts
, dragging :: !(Maybe (Position -> Position -> X (), X ())) }
data XConf = XConf
{ display :: Display -- ^ the X11 display
, theRoot :: !Window -- ^ the root window
, normalBorder :: !Pixel -- ^ border color of unfocused windows
, focusedBorder :: !Pixel } -- ^ border color of the focused window
type WindowSet = StackSet WorkspaceId Window ScreenId ScreenDetail
-- | Virtual workspace indicies
newtype WorkspaceId = W Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
-- | Physical screen indicies
newtype ScreenId = S Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
data ScreenDetail = SD { screenRect :: !Rectangle
, statusGap :: !(Int,Int,Int,Int) -- ^ width of status bar on the screen
} deriving (Eq,Show, Read)
------------------------------------------------------------------------
-- | The X monad, a StateT transformer over IO encapsulating the window
-- manager state
--
-- Dynamic components may be retrieved with 'get', static components
-- with 'ask'. With newtype deriving we get readers and state monads
-- instantiated on XConf and XState automatically.
--
newtype X a = X (ReaderT XConf (StateT XState IO) a)
deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf)
-- | Run the X monad, given a chunk of X monad code, and an initial state
-- Return the result, and final state
runX :: XConf -> XState -> X a -> IO ()
runX c st (X a) = runStateT (runReaderT a c) st >> return ()
-- | 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 (X job) (X errcase) = do
st <- get
c <- ask
(a,s') <- io ((runStateT (runReaderT job c) st) `catch`
\e -> (do hPutStrLn stderr (show e); runStateT (runReaderT errcase c) st))
put s'
return a
-- ---------------------------------------------------------------------
-- Convenient wrappers to state
-- | Run a monad action with the current display settings
withDisplay :: (Display -> X a) -> X a
withDisplay f = asks display >>= f
-- | Run a monadic action with the current stack set
withWindowSet :: (WindowSet -> X a) -> X a
withWindowSet f = gets windowset >>= f
-- | 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
-- The different layout modes
-- 'doLayout': given a Rectangle and a Stack, layout the stack elements
-- inside the given Rectangle. If an element is not given a Rectangle
-- by 'doLayout', then it is not shown on screen. Windows are restacked
-- according to the order they are returned by 'doLayout'.
--
-- 'modifyLayout' performs message handling for that layout. If
-- 'modifyLayout' returns Nothing, then the layout did not respond to
-- that message and the screen is not refreshed. Otherwise, 'modifyLayout'
-- returns an updated 'Layout' and the screen is refreshed.
--
data Layout a = Layout { doLayout :: Rectangle -> Stack a -> X ([(a, Rectangle)], Maybe (Layout a))
, modifyLayout :: SomeMessage -> X (Maybe (Layout a)) }
runLayout :: Layout a -> Rectangle -> StackOrNot a -> X ([(a, Rectangle)], Maybe (Layout 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 modifyLayout handler.
--
-- User-extensible messages must be a member of this class.
--
class Typeable a => Message a
-- |
-- A wrapped value of some type in the Message class.
--
data SomeMessage = forall a. Message a => SomeMessage a
-- |
-- And now, unwrap a given, unknown Message type, performing a (dynamic)
-- type check on the result.
--
fromMessage :: Message m => SomeMessage -> Maybe m
fromMessage (SomeMessage m) = cast m
-- ---------------------------------------------------------------------
-- | General utilities
--
-- Lift an IO action into the X monad
io :: IO a -> X a
io = liftIO
-- | Lift an IO action into the X monad. If the action results in an IO
-- exception, log the exception to stderr and continue normal execution.
catchIO :: IO () -> X ()
catchIO f = liftIO (f `catch` \e -> do hPutStrLn stderr (show e); hFlush stderr)
-- | spawn. Launch an external application
spawn :: String -> X ()
spawn x = io $ do
pid <- forkProcess $ do
forkProcess (createSession >> executeFile "/bin/sh" False ["-c", x] Nothing)
exitWith ExitSuccess
getProcessStatus True False pid
return ()
-- | Restart xmonad via exec().
--
-- If the first parameter is 'Just name', restart will attempt to execute the
-- program corresponding to 'name'. Otherwise, xmonad will attempt to execute
-- the name of the current program.
--
-- When the second parameter is 'True', xmonad will attempt to resume with the
-- current window state.
restart :: Maybe String -> Bool -> X ()
restart mprog resume = do
prog <- maybe (io getProgName) return mprog
args <- if resume then gets (("--resume":) . return . show . windowset) else return []
catchIO (executeFile prog True args Nothing)
-- | Run a side effecting action with the current workspace. Like 'when' but
whenJust :: Maybe a -> (a -> X ()) -> X ()
whenJust mg f = maybe (return ()) f mg
-- | Conditionally run an action, using a X event to decide
whenX :: X Bool -> X () -> X ()
whenX a f = a >>= \b -> when b f
-- 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 ()
trace msg = io $! do hPutStrLn stderr msg; hFlush stderr

253
XMonad/Config.hs Normal file
View File

@@ -0,0 +1,253 @@
{-# OPTIONS -fno-warn-missing-signatures #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Config
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : dons@galois.com
-- Stability : stable
-- Portability : portable
--
-- This module specifies the default configuration values for xmonad.
--
-- DO NOT MODIFY THIS FILE! It won't work. You may configure xmonad
-- by providing your own @~\/.xmonad\/xmonad.hs@ that overrides
-- specific fields in 'defaultConfig'. For a starting point, you can
-- copy the @xmonad.hs@ found in the @man@ directory, or look at
-- examples on the xmonad wiki.
--
------------------------------------------------------------------------
module XMonad.Config (defaultConfig) where
--
-- Useful imports
--
import XMonad.Core as XMonad hiding
(workspaces,manageHook,numlockMask,keys,logHook,startupHook,borderWidth,mouseBindings
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse)
import qualified XMonad.Core as XMonad
(workspaces,manageHook,numlockMask,keys,logHook,startupHook,borderWidth,mouseBindings
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse)
import XMonad.Layout
import XMonad.Operations
import XMonad.ManageHook
import qualified XMonad.StackSet as W
import Data.Bits ((.|.))
import qualified Data.Map as M
import System.Exit
import Graphics.X11.Xlib
-- | The default number of workspaces (virtual screens) and their names.
-- By default we use numeric strings, but any string may be used as a
-- workspace name. The number of workspaces is determined by the length
-- of this list.
--
-- A tagging example:
--
-- > workspaces = ["web", "irc", "code" ] ++ map show [4..9]
--
workspaces :: [WorkspaceId]
workspaces = map show [1 .. 9 :: Int]
-- | modMask lets you specify which modkey you want to use. The default
-- is mod1Mask ("left alt"). You may also consider using mod3Mask
-- ("right alt"), which does not conflict with emacs keybindings. The
-- "windows key" is usually mod4Mask.
--
defaultModMask :: KeyMask
defaultModMask = mod1Mask
-- | The mask for the numlock key. Numlock status is "masked" from the
-- current modifier status, so the keybindings will work with numlock on or
-- off. You may need to change this on some systems.
--
-- You can find the numlock modifier by running "xmodmap" and looking for a
-- modifier with Num_Lock bound to it:
--
-- > $ xmodmap | grep Num
-- > mod2 Num_Lock (0x4d)
--
-- Set numlockMask = 0 if you don't have a numlock key, or want to treat
-- numlock status separately.
--
numlockMask :: KeyMask
numlockMask = mod2Mask
-- | Width of the window border in pixels.
--
borderWidth :: Dimension
borderWidth = 1
-- | Border colors for unfocused and focused windows, respectively.
--
normalBorderColor, focusedBorderColor :: String
normalBorderColor = "gray" -- "#dddddd"
focusedBorderColor = "red" -- "#ff0000" don't use hex, not <24 bit safe
------------------------------------------------------------------------
-- Window rules
-- | Execute arbitrary actions and WindowSet manipulations when managing
-- a new window. You can use this to, for example, always float a
-- particular program, or have a client always appear on a particular
-- workspace.
--
-- To find the property name associated with a program, use
-- xprop | grep WM_CLASS
-- and click on the client you're interested in.
--
manageHook :: ManageHook
manageHook = composeAll
[ className =? "MPlayer" --> doFloat
, className =? "Gimp" --> doFloat ]
------------------------------------------------------------------------
-- Logging
-- | Perform an arbitrary action on each internal state change or X event.
-- Examples include:
--
-- * do nothing
--
-- * log the state to stdout
--
-- See the 'DynamicLog' extension for examples.
--
logHook :: X ()
logHook = return ()
-- | Perform an arbitrary action at xmonad startup.
startupHook :: X ()
startupHook = return ()
------------------------------------------------------------------------
-- Extensible layouts
--
-- You can specify and transform your layouts by modifying these values.
-- If you change layout bindings be sure to use 'mod-shift-space' after
-- restarting (with 'mod-q') to reset your layout state to the new
-- defaults, as xmonad preserves your old layout settings by default.
--
-- | The available layouts. Note that each layout is separated by |||, which
-- denotes layout choice.
layout = tiled ||| Mirror tiled ||| Full
where
-- default tiling algorithm partitions the screen into two panes
tiled = Tall nmaster delta ratio
-- The default number of windows in the master pane
nmaster = 1
-- Default proportion of screen occupied by master pane
ratio = 1/2
-- Percent of screen to increment by when resizing panes
delta = 3/100
------------------------------------------------------------------------
-- Key bindings:
-- | The preferred terminal program, which is used in a binding below and by
-- certain contrib modules.
terminal :: String
terminal = "xterm"
-- | Whether focus follows the mouse pointer.
focusFollowsMouse :: Bool
focusFollowsMouse = True
-- | The xmonad key bindings. Add, modify or remove key bindings here.
--
-- (The comment formatting character is used when generating the manpage)
--
keys :: XConfig Layout -> M.Map (KeyMask, KeySym) (X ())
keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
-- launching and killing programs
[ ((modMask .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf) -- %! Launch terminal
, ((modMask, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"") -- %! Launch dmenu
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun") -- %! Launch gmrun
, ((modMask .|. shiftMask, xK_c ), kill) -- %! Close the focused window
, ((modMask, xK_space ), sendMessage NextLayout) -- %! Rotate through the available layout algorithms
, ((modMask .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf) -- %! Reset the layouts on the current workspace to default
, ((modMask, xK_n ), refresh) -- %! Resize viewed windows to the correct size
-- move focus up or down the window stack
, ((modMask, xK_Tab ), windows W.focusDown) -- %! Move focus to the next window
, ((modMask .|. shiftMask, xK_Tab ), windows W.focusUp ) -- %! Move focus to the previous window
, ((modMask, xK_j ), windows W.focusDown) -- %! Move focus to the next window
, ((modMask, xK_k ), windows W.focusUp ) -- %! Move focus to the previous window
, ((modMask, xK_m ), windows W.focusMaster ) -- %! Move focus to the master window
-- modifying the window order
, ((modMask, xK_Return), windows W.swapMaster) -- %! Swap the focused window and the master window
, ((modMask .|. shiftMask, xK_j ), windows W.swapDown ) -- %! Swap the focused window with the next window
, ((modMask .|. shiftMask, xK_k ), windows W.swapUp ) -- %! Swap the focused window with the previous window
-- resizing the master/slave ratio
, ((modMask, xK_h ), sendMessage Shrink) -- %! Shrink the master area
, ((modMask, xK_l ), sendMessage Expand) -- %! Expand the master area
-- floating layer support
, ((modMask, xK_t ), withFocused $ windows . W.sink) -- %! Push window back into tiling
-- increase or decrease number of windows in the master area
, ((modMask , xK_comma ), sendMessage (IncMasterN 1)) -- %! Increment the number of windows in the master area
, ((modMask , xK_period), sendMessage (IncMasterN (-1))) -- %! Deincrement the number of windows in the master area
-- toggle the status bar gap
--, ((modMask , xK_b ), modifyGap (\i n -> let x = (XMonad.defaultGaps conf ++ 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 "xmonad" True) -- %! Restart xmonad
]
++
-- mod-[1..9] %! Switch to workspace N
-- mod-shift-[1..9] %! Move client to workspace N
[((m .|. modMask, k), windows $ f i)
| (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
++
-- mod-{w,e,r} %! Switch to physical/Xinerama screens 1, 2, or 3
-- mod-shift-{w,e,r} %! Move client to screen 1, 2, or 3
[((m .|. modMask, key), screenWorkspace sc >>= flip whenJust (windows . f))
| (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
-- | Mouse bindings: default actions bound to mouse events
--
mouseBindings :: XConfig Layout -> M.Map (KeyMask, Button) (Window -> X ())
mouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList $
-- mod-button1 %! Set the window to floating mode and move by dragging
[ ((modMask, button1), (\w -> focus w >> mouseMoveWindow w
>> windows W.shiftMaster))
-- mod-button2 %! Raise the window to the top of the stack
, ((modMask, button2), (\w -> focus w >> windows W.shiftMaster))
-- mod-button3 %! Set the window to floating mode and resize by dragging
, ((modMask, button3), (\w -> focus w >> mouseResizeWindow w
>> windows W.shiftMaster))
-- you may also bind events to the mouse scroll wheel (button4 and button5)
]
-- | And, finally, the default set of configuration values itself
defaultConfig = XConfig
{ XMonad.borderWidth = borderWidth
, XMonad.workspaces = workspaces
, XMonad.layoutHook = layout
, XMonad.terminal = terminal
, XMonad.normalBorderColor = normalBorderColor
, XMonad.focusedBorderColor = focusedBorderColor
, XMonad.numlockMask = numlockMask
, XMonad.modMask = defaultModMask
, XMonad.keys = keys
, XMonad.logHook = logHook
, XMonad.startupHook = startupHook
, XMonad.mouseBindings = mouseBindings
, XMonad.manageHook = manageHook
, XMonad.focusFollowsMouse = focusFollowsMouse }

449
XMonad/Core.hs Normal file
View File

@@ -0,0 +1,449 @@
{-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving,
MultiParamTypeClasses, TypeSynonymInstances, CPP #-}
-- required for deriving Typeable
{-# OPTIONS_GHC -fglasgow-exts #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Core
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : spencerjanssen@gmail.com
-- Stability : unstable
-- Portability : not portable, uses cunning newtype deriving
--
-- The 'X' monad, a state monad transformer over 'IO', for the window
-- manager state, and support routines.
--
-----------------------------------------------------------------------------
module XMonad.Core (
X, WindowSet, WindowSpace, WorkspaceId,
ScreenId(..), ScreenDetail(..), XState(..),
XConf(..), XConfig(..), LayoutClass(..),
Layout(..), readsLayout, Typeable, Message,
SomeMessage(..), fromMessage, LayoutMessages(..),
runX, catchX, userCode, userCodeDef, io, catchIO, installSignalHandlers,
withDisplay, withWindowSet, isRoot, runOnWorkspaces,
getAtom, spawn, spawnPID, getXMonadDir, recompile, trace, whenJust, whenX,
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, ManageHook, Query(..), runQuery
) where
import XMonad.StackSet hiding (modify)
import Prelude hiding ( catch )
import Control.Exception (catch, try, bracket, throw, Exception(ExitException))
import Control.Applicative
import Control.Monad.State
import Control.Monad.Reader
import System.IO
import System.Info
import System.Posix.Process (executeFile, forkProcess, getAnyProcessStatus)
import System.Posix.Signals
import System.Posix.Types (ProcessID)
import System.Process
import System.Directory
import System.Exit
import Graphics.X11.Xlib
import Graphics.X11.Xlib.Extras (Event)
import Data.Typeable
import Data.Maybe (isJust)
import Data.Monoid
import Data.Maybe (fromMaybe)
import qualified Data.Map as M
import qualified Data.Set as S
-- | XState, the (mutable) window manager state.
data XState = XState
{ windowset :: !WindowSet -- ^ workspace list
, mapped :: !(S.Set Window) -- ^ the Set of mapped windows
, waitingUnmap :: !(M.Map Window Int) -- ^ the number of expected UnmapEvents
, dragging :: !(Maybe (Position -> Position -> X (), X ())) }
-- | XConf, the (read-only) window manager configuration.
data XConf = XConf
{ display :: Display -- ^ the X11 display
, config :: !(XConfig Layout) -- ^ initial user configuration
, theRoot :: !Window -- ^ the root window
, normalBorder :: !Pixel -- ^ border color of unfocused windows
, focusedBorder :: !Pixel -- ^ border color of the focused window
, keyActions :: !(M.Map (KeyMask, KeySym) (X ()))
-- ^ a mapping of key presses to actions
, buttonActions :: !(M.Map (KeyMask, Button) (Window -> X ()))
-- ^ a mapping of button presses to actions
, mouseFocused :: !Bool -- ^ was refocus caused by mouse action?
, mousePosition :: !(Maybe (Position, Position))
-- ^ position of the mouse according to
-- the event currently being processed
}
-- todo, better name
data XConfig l = XConfig
{ normalBorderColor :: !String -- ^ Non focused windows border color. Default: \"#dddddd\"
, focusedBorderColor :: !String -- ^ Focused windows border color. Default: \"#ff0000\"
, terminal :: !String -- ^ The preferred terminal application. Default: \"xterm\"
, layoutHook :: !(l Window) -- ^ The available layouts
, manageHook :: !ManageHook -- ^ The action to run when a new window is opened
, workspaces :: ![String] -- ^ The list of workspaces' names
, numlockMask :: !KeyMask -- ^ The numlock modifier
, modMask :: !KeyMask -- ^ the mod modifier
, keys :: !(XConfig Layout -> M.Map (ButtonMask,KeySym) (X ()))
-- ^ The key binding: a map from key presses and actions
, mouseBindings :: !(XConfig Layout -> M.Map (ButtonMask, Button) (Window -> X ()))
-- ^ The mouse bindings
, borderWidth :: !Dimension -- ^ The border width
, logHook :: !(X ()) -- ^ The action to perform when the windows set is changed
, startupHook :: !(X ()) -- ^ The action to perform on startup
, focusFollowsMouse :: !Bool -- ^ Whether window entry events can change focus
}
type WindowSet = StackSet WorkspaceId (Layout Window) Window ScreenId ScreenDetail
type WindowSpace = Workspace WorkspaceId (Layout Window) Window
-- | Virtual workspace indices
type WorkspaceId = String
-- | Physical screen indices
newtype ScreenId = S Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
-- | The 'Rectangle' with screen dimensions
data ScreenDetail = SD { screenRect :: !Rectangle } deriving (Eq,Show, Read)
------------------------------------------------------------------------
-- | The X monad, 'ReaderT' and 'StateT' transformers over 'IO'
-- encapsulating the window manager configuration and state,
-- respectively.
--
-- Dynamic components may be retrieved with 'get', static components
-- with 'ask'. With newtype deriving we get readers and state monads
-- instantiated on 'XConf' and 'XState' automatically.
--
newtype X a = X (ReaderT XConf (StateT XState IO) a)
#ifndef __HADDOCK__
deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf)
#endif
instance Applicative X where
pure = return
(<*>) = ap
instance (Monoid a) => Monoid (X a) where
mempty = return mempty
mappend = liftM2 mappend
type ManageHook = Query (Endo WindowSet)
newtype Query a = Query (ReaderT Window X a)
#ifndef __HADDOCK__
deriving (Functor, Monad, MonadReader Window, MonadIO)
#endif
runQuery :: Query a -> Window -> X a
runQuery (Query m) w = runReaderT m w
instance Monoid a => Monoid (Query a) where
mempty = return mempty
mappend = liftM2 mappend
-- | Run the 'X' monad, given a chunk of 'X' monad code, and an initial state
-- Return the result, and final state
runX :: XConf -> XState -> X a -> IO (a, XState)
runX c st (X a) = runStateT (runReaderT a c) st
-- | Run in the 'X' monad, and in case of exception, and catch it and log it
-- to stderr, and run the error case.
catchX :: X a -> X a -> X a
catchX job errcase = do
st <- get
c <- ask
(a, s') <- io $ runX c st job `catch` \e -> case e of
ExitException {} -> throw e
_ -> do hPrint stderr e; runX c st errcase
put s'
return a
-- | Execute the argument, catching all exceptions. Either this function or
-- 'catchX' should be used at all callsites of user customized code.
userCode :: X a -> X (Maybe a)
userCode a = catchX (Just `liftM` a) (return Nothing)
-- | Same as userCode but with a default argument to return instead of using
-- Maybe, provided for convenience.
userCodeDef :: a -> X a -> X a
userCodeDef def a = fromMaybe def `liftM` userCode a
-- ---------------------------------------------------------------------
-- Convenient wrappers to state
-- | Run a monad action with the current display settings
withDisplay :: (Display -> X a) -> X a
withDisplay f = asks display >>= f
-- | Run a monadic action with the current stack set
withWindowSet :: (WindowSet -> X a) -> X a
withWindowSet f = gets windowset >>= f
-- | True if the given window is the root window
isRoot :: Window -> X Bool
isRoot w = (w==) <$> asks theRoot
-- | Wrapper for the common case of atom internment
getAtom :: String -> X Atom
getAtom str = withDisplay $ \dpy -> io $ internAtom dpy str False
-- | Common non-predefined atoms
atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, atom_WM_STATE :: X Atom
atom_WM_PROTOCOLS = getAtom "WM_PROTOCOLS"
atom_WM_DELETE_WINDOW = getAtom "WM_DELETE_WINDOW"
atom_WM_STATE = getAtom "WM_STATE"
------------------------------------------------------------------------
-- LayoutClass handling. See particular instances in Operations.hs
-- | An existential type that can hold any object that is in 'Read'
-- and 'LayoutClass'.
data Layout a = forall l. (LayoutClass l a, Read (l a)) => Layout (l a)
-- | Using the 'Layout' as a witness, parse existentially wrapped windows
-- from a 'String'.
readsLayout :: Layout a -> String -> [(Layout a, String)]
readsLayout (Layout l) s = [(Layout (asTypeOf x l), rs) | (x, rs) <- reads s]
-- | Every layout must be an instance of 'LayoutClass', which defines
-- the basic layout operations along with a sensible default for each.
--
-- Minimal complete definition:
--
-- * 'runLayout' || (('doLayout' || 'pureLayout') && 'emptyLayout'), and
--
-- * 'handleMessage' || 'pureMessage'
--
-- You should also strongly consider implementing 'description',
-- although it is not required.
--
-- Note that any code which /uses/ 'LayoutClass' methods should only
-- ever call 'runLayout', 'handleMessage', and 'description'! In
-- other words, the only calls to 'doLayout', 'pureMessage', and other
-- such methods should be from the default implementations of
-- 'runLayout', 'handleMessage', and so on. This ensures that the
-- proper methods will be used, regardless of the particular methods
-- that any 'LayoutClass' instance chooses to define.
class Show (layout a) => LayoutClass layout a where
-- | By default, 'runLayout' calls 'doLayout' if there are any
-- windows to be laid out, and 'emptyLayout' otherwise. Most
-- instances of 'LayoutClass' probably do not need to implement
-- 'runLayout'; it is only useful for layouts which wish to make
-- use of more of the 'Workspace' information (for example,
-- "XMonad.Layout.PerWorkspace").
runLayout :: Workspace WorkspaceId (layout a) a
-> Rectangle
-> X ([(a, Rectangle)], Maybe (layout a))
runLayout (Workspace _ l ms) r = maybe (emptyLayout l r) (doLayout l r) ms
-- | Given a 'Rectangle' in which to place the windows, and a 'Stack'
-- of windows, return a list of windows and their corresponding
-- Rectangles. If an element is not given a Rectangle by
-- 'doLayout', then it is not shown on screen. The order of
-- windows in this list should be the desired stacking order.
--
-- Also possibly return a modified layout (by returning @Just
-- newLayout@), if this layout needs to be modified (e.g. if it
-- keeps track of some sort of state). Return @Nothing@ if the
-- layout does not need to be modified.
--
-- Layouts which do not need access to the 'X' monad ('IO', window
-- manager state, or configuration) and do not keep track of their
-- own state should implement 'pureLayout' instead of 'doLayout'.
doLayout :: layout a -> Rectangle -> Stack a
-> X ([(a, Rectangle)], Maybe (layout a))
doLayout l r s = return (pureLayout l r s, Nothing)
-- | This is a pure version of 'doLayout', for cases where we
-- don't need access to the 'X' monad to determine how to lay out
-- the windows, and we don't need to modify the layout itself.
pureLayout :: layout a -> Rectangle -> Stack a -> [(a, Rectangle)]
pureLayout _ r s = [(focus s, r)]
-- | 'emptyLayout' is called when there are no windows.
emptyLayout :: layout a -> Rectangle -> X ([(a, Rectangle)], Maybe (layout a))
emptyLayout _ _ = return ([], Nothing)
-- | 'handleMessage' performs message handling. If
-- 'handleMessage' returns @Nothing@, then the layout did not
-- respond to the message and the screen is not refreshed.
-- Otherwise, 'handleMessage' returns an updated layout and the
-- screen is refreshed.
--
-- Layouts which do not need access to the 'X' monad to decide how
-- to handle messages should implement 'pureMessage' instead of
-- 'handleMessage' (this restricts the risk of error, and makes
-- testing much easier).
handleMessage :: layout a -> SomeMessage -> X (Maybe (layout a))
handleMessage l = return . pureMessage l
-- | Respond to a message by (possibly) changing our layout, but
-- taking no other action. If the layout changes, the screen will
-- be refreshed.
pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
pureMessage _ _ = Nothing
-- | This should be a human-readable string that is used when
-- selecting layouts by name. The default implementation is
-- 'show', which is in some cases a poor default.
description :: layout a -> String
description = show
instance LayoutClass Layout Window where
runLayout (Workspace i (Layout l) ms) r = fmap (fmap Layout) `fmap` runLayout (Workspace i l ms) r
doLayout (Layout l) r s = fmap (fmap Layout) `fmap` doLayout l r s
emptyLayout (Layout l) r = fmap (fmap Layout) `fmap` emptyLayout l r
handleMessage (Layout l) = fmap (fmap Layout) . handleMessage l
description (Layout l) = description l
instance Show (Layout a) where show (Layout l) = show l
-- | Based on ideas in /An Extensible Dynamically-Typed Hierarchy of
-- Exceptions/, Simon Marlow, 2006. Use extensible messages to the
-- 'handleMessage' handler.
--
-- User-extensible messages must be a member of this class.
--
class Typeable a => Message a
-- |
-- A wrapped value of some type in the 'Message' class.
--
data SomeMessage = forall a. Message a => SomeMessage a
-- |
-- And now, unwrap a given, unknown 'Message' type, performing a (dynamic)
-- type check on the result.
--
fromMessage :: Message m => SomeMessage -> Maybe m
fromMessage (SomeMessage m) = cast m
-- X Events are valid Messages.
instance Message Event
-- | 'LayoutMessages' are core messages that all layouts (especially stateful
-- layouts) should consider handling.
data LayoutMessages = Hide -- ^ sent when a layout becomes non-visible
| ReleaseResources -- ^ sent when xmonad is exiting or restarting
deriving (Typeable, Eq)
instance Message LayoutMessages
-- ---------------------------------------------------------------------
-- | General utilities
--
-- Lift an 'IO' action into the 'X' monad
io :: MonadIO m => IO a -> m a
io = liftIO
-- | Lift an 'IO' action into the 'X' monad. If the action results in an 'IO'
-- exception, log the exception to stderr and continue normal execution.
catchIO :: MonadIO m => IO () -> m ()
catchIO f = io (f `catch` \e -> hPrint stderr e >> hFlush stderr)
-- | spawn. Launch an external application. Specifically, it double-forks and
-- runs the 'String' you pass as a command to /bin/sh.
spawn :: MonadIO m => String -> m ()
spawn x = spawnPID x >> return ()
spawnPID :: MonadIO m => String -> m ProcessID
spawnPID x = io $ forkProcess $ executeFile "/bin/sh" False ["-c", x] Nothing
-- | This is basically a map function, running a function in the 'X' monad on
-- each workspace with the output of that function being the modified workspace.
runOnWorkspaces :: (WindowSpace -> X WindowSpace) -> X ()
runOnWorkspaces job = do
ws <- gets windowset
h <- mapM job $ hidden ws
c:v <- mapM (\s -> (\w -> s { workspace = w}) <$> job (workspace s))
$ current ws : visible ws
modify $ \s -> s { windowset = ws { current = c, visible = v, hidden = h } }
-- | Return the path to @~\/.xmonad@.
getXMonadDir :: MonadIO m => m String
getXMonadDir = io $ getAppUserDataDirectory "xmonad"
-- | 'recompile force', recompile @~\/.xmonad\/xmonad.hs@ when any of the
-- following apply:
--
-- * force is 'True'
--
-- * the xmonad executable does not exist
--
-- * the xmonad executable is older than xmonad.hs
--
-- The -i flag is used to restrict recompilation to the xmonad.hs file only.
--
-- Compilation errors (if any) are logged to ~\/.xmonad\/xmonad.errors. If
-- GHC indicates failure with a non-zero exit code, an xmessage displaying
-- that file is spawned.
--
-- 'False' is returned if there are compilation errors.
--
recompile :: MonadIO m => Bool -> m Bool
recompile force = io $ do
dir <- getXMonadDir
let binn = "xmonad-"++arch++"-"++os
bin = dir ++ "/" ++ binn
base = dir ++ "/" ++ "xmonad"
err = base ++ ".errors"
src = base ++ ".hs"
srcT <- getModTime src
binT <- getModTime bin
if (force || srcT > binT)
then do
-- temporarily disable SIGCHLD ignoring:
installHandler sigCHLD Default Nothing
status <- bracket (openFile err WriteMode) hClose $ \h -> do
waitForProcess =<< runProcess "ghc" ["--make", "xmonad.hs", "-i", "-no-recomp", "-v0", "-o",binn] (Just dir)
Nothing Nothing Nothing (Just h)
-- re-enable SIGCHLD:
installSignalHandlers
-- now, if it fails, run xmessage to let the user know:
when (status /= ExitSuccess) $ do
ghcErr <- readFile err
let msg = unlines $
["Error detected while loading xmonad configuration file: " ++ src]
++ lines ghcErr ++ ["","Please check the file for errors."]
-- nb, the ordering of printing, then forking, is crucial due to
-- lazy evaluation
hPutStrLn stderr msg
forkProcess $ executeFile "xmessage" True ["-default", "okay", msg] Nothing
return ()
return (status == ExitSuccess)
else return True
where getModTime f = catch (Just <$> getModificationTime f) (const $ return Nothing)
-- | Conditionally run an action, using a @Maybe a@ to decide.
whenJust :: Monad m => Maybe a -> (a -> m ()) -> m ()
whenJust mg f = maybe (return ()) f mg
-- | Conditionally run an action, using a 'X' event to decide
whenX :: X Bool -> X () -> X ()
whenX a f = a >>= \b -> when b f
-- | A 'trace' for the 'X' monad. Logs a string to stderr. The result may
-- be found in your .xsession-errors file
trace :: MonadIO m => String -> m ()
trace = io . hPutStrLn stderr
-- | Ignore SIGPIPE to avoid termination when a pipe is full, and SIGCHLD to
-- avoid zombie processes, and clean up any extant zombie processes.
installSignalHandlers :: MonadIO m => m ()
installSignalHandlers = io $ do
installHandler openEndedPipe Ignore Nothing
installHandler sigCHLD Ignore Nothing
try $ fix $ \more -> do
x <- getAnyProcessStatus False False
when (isJust x) more
return ()

210
XMonad/Layout.hs Normal file
View File

@@ -0,0 +1,210 @@
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
-- --------------------------------------------------------------------------
-- |
-- Module : XMonad.Layout
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : spencerjanssen@gmail.com
-- Stability : unstable
-- Portability : not portable, Typeable deriving, mtl, posix
--
-- The collection of core layouts.
--
-----------------------------------------------------------------------------
module XMonad.Layout (
Full(..), Tall(..), Mirror(..),
Resize(..), IncMasterN(..), Choose, (|||), ChangeLayout(..),
mirrorRect, splitVertically,
splitHorizontally, splitHorizontallyBy, splitVerticallyBy,
tile
) where
import XMonad.Core
import Graphics.X11 (Rectangle(..))
import qualified XMonad.StackSet as W
import Control.Arrow ((***), second)
import Control.Monad
import Data.Maybe (fromMaybe)
------------------------------------------------------------------------
-- | Change the size of the master pane.
data Resize = Shrink | Expand deriving Typeable
-- | Increase the number of clients in the master pane.
data IncMasterN = IncMasterN !Int deriving Typeable
instance Message Resize
instance Message IncMasterN
-- | Simple fullscreen mode. Renders the focused window fullscreen.
data Full a = Full deriving (Show, Read)
instance LayoutClass Full a
-- | The builtin tiling mode of xmonad. Supports 'Shrink', 'Expand' and
-- 'IncMasterN'.
data Tall a = Tall !Int -- ^ The default number of windows in the master pane (default: 1)
!Rational -- ^ Default proportion of screen occupied by master pane (default: 1/2)
!Rational -- ^ Percent of screen to increment by when resizing panes (default: 3/100)
deriving (Show, Read)
-- TODO should be capped [0..1] ..
-- a nice pure layout, lots of properties for the layout, and its messages, in Properties.hs
instance LayoutClass Tall a where
pureLayout (Tall nmaster _ frac) r s = zip ws rs
where ws = W.integrate s
rs = tile frac r nmaster (length ws)
pureMessage (Tall nmaster delta frac) m =
msum [fmap resize (fromMessage m)
,fmap incmastern (fromMessage m)]
where resize Shrink = Tall nmaster delta (max 0 $ frac-delta)
resize Expand = Tall nmaster delta (min 1 $ frac+delta)
incmastern (IncMasterN d) = Tall (max 0 (nmaster+d)) delta frac
description _ = "Tall"
-- | Compute the positions for windows using the default two-pane tiling
-- algorithm.
--
-- The screen is divided into two panes. All clients are
-- then partioned between these two panes. One pane, the master, by
-- convention has the least number of windows in it.
tile
:: Rational -- ^ @frac@, what proportion of the screen to devote to the master area
-> Rectangle -- ^ @r@, the rectangle representing the screen
-> Int -- ^ @nmaster@, the number of windows in the master pane
-> Int -- ^ @n@, the total number of windows to tile
-> [Rectangle]
tile f r nmaster n = if n <= nmaster || nmaster == 0
then splitVertically n r
else splitVertically nmaster r1 ++ splitVertically (n-nmaster) r2 -- two columns
where (r1,r2) = splitHorizontallyBy f r
--
-- Divide the screen vertically into n subrectangles
--
splitVertically, splitHorizontally :: Int -> Rectangle -> [Rectangle]
splitVertically n r | n < 2 = [r]
splitVertically n (Rectangle sx sy sw sh) = Rectangle sx sy sw smallh :
splitVertically (n-1) (Rectangle sx (sy+fromIntegral smallh) sw (sh-smallh))
where smallh = sh `div` fromIntegral n --hmm, this is a fold or map.
-- Not used in the core, but exported
splitHorizontally n = map mirrorRect . splitVertically n . mirrorRect
-- Divide the screen into two rectangles, using a rational to specify the ratio
splitHorizontallyBy, splitVerticallyBy :: RealFrac r => r -> Rectangle -> (Rectangle, Rectangle)
splitHorizontallyBy f (Rectangle sx sy sw sh) =
( Rectangle sx sy leftw sh
, Rectangle (sx + fromIntegral leftw) sy (sw-fromIntegral leftw) sh)
where leftw = floor $ fromIntegral sw * f
-- Not used in the core, but exported
splitVerticallyBy f = (mirrorRect *** mirrorRect) . splitHorizontallyBy f . mirrorRect
------------------------------------------------------------------------
-- | Mirror a layout, compute its 90 degree rotated form.
newtype Mirror l a = Mirror (l a) deriving (Show, Read)
instance LayoutClass l a => LayoutClass (Mirror l) a where
runLayout (W.Workspace i (Mirror l) ms) r = (map (second mirrorRect) *** fmap Mirror)
`fmap` runLayout (W.Workspace i l ms) (mirrorRect r)
handleMessage (Mirror l) = fmap (fmap Mirror) . handleMessage l
description (Mirror l) = "Mirror "++ description l
-- | Mirror a rectangle.
mirrorRect :: Rectangle -> Rectangle
mirrorRect (Rectangle rx ry rw rh) = (Rectangle ry rx rh rw)
------------------------------------------------------------------------
-- LayoutClass selection manager
-- Layouts that transition between other layouts
-- | Messages to change the current layout.
data ChangeLayout = FirstLayout | NextLayout deriving (Eq, Show, Typeable)
instance Message ChangeLayout
-- | The layout choice combinator
(|||) :: (LayoutClass l a, LayoutClass r a) => l a -> r a -> Choose l r a
(|||) = Choose L
infixr 5 |||
-- | A layout that allows users to switch between various layout options.
data Choose l r a = Choose LR (l a) (r a) deriving (Read, Show)
-- | Are we on the left or right sub-layout?
data LR = L | R deriving (Read, Show, Eq)
data NextNoWrap = NextNoWrap deriving (Eq, Show, Typeable)
instance Message NextNoWrap
-- | A small wrapper around handleMessage, as it is tedious to write
-- SomeMessage repeatedly.
handle :: (LayoutClass l a, Message m) => l a -> m -> X (Maybe (l a))
handle l m = handleMessage l (SomeMessage m)
-- | A smart constructor that takes some potential modifications, returns a
-- new structure if any fields have changed, and performs any necessary cleanup
-- on newly non-visible layouts.
choose :: (LayoutClass l a, LayoutClass r a)
=> Choose l r a-> LR -> Maybe (l a) -> Maybe (r a) -> X (Maybe (Choose l r a))
choose (Choose d _ _) d' Nothing Nothing | d == d' = return Nothing
choose (Choose d l r) d' ml mr = f lr
where
(l', r') = (fromMaybe l ml, fromMaybe r mr)
lr = case (d, d') of
(L, R) -> (hide l' , return r')
(R, L) -> (return l', hide r' )
(_, _) -> (return l', return r')
f (x,y) = fmap Just $ liftM2 (Choose d') x y
hide x = fmap (fromMaybe x) $ handle x Hide
instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
runLayout (W.Workspace i (Choose L l r) ms) =
fmap (second . fmap $ flip (Choose L) $ r) . runLayout (W.Workspace i l ms)
runLayout (W.Workspace i (Choose R l r) ms) =
fmap (second . fmap $ Choose R l) . runLayout (W.Workspace i r ms)
description (Choose L l _) = description l
description (Choose R _ r) = description r
handleMessage lr m | Just NextLayout <- fromMessage m = do
mlr' <- handle lr NextNoWrap
maybe (handle lr FirstLayout) (return . Just) mlr'
handleMessage c@(Choose d l r) m | Just NextNoWrap <- fromMessage m =
case d of
L -> do
ml <- handle l NextNoWrap
case ml of
Just _ -> choose c L ml Nothing
Nothing -> choose c R Nothing =<< handle r FirstLayout
R -> choose c R Nothing =<< handle r NextNoWrap
handleMessage c@(Choose _ l _) m | Just FirstLayout <- fromMessage m = do
flip (choose c L) Nothing =<< handle l FirstLayout
handleMessage c@(Choose d l r) m | Just ReleaseResources <- fromMessage m =
join $ liftM2 (choose c d) (handle l ReleaseResources) (handle r ReleaseResources)
handleMessage c@(Choose d l r) m = do
ml' <- case d of
L -> handleMessage l m
R -> return Nothing
mr' <- case d of
L -> return Nothing
R -> handleMessage r m
choose c d ml' mr'

329
XMonad/Main.hsc Normal file
View File

@@ -0,0 +1,329 @@
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, ForeignFunctionInterface #-}
----------------------------------------------------------------------------
-- |
-- Module : XMonad.Main
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : spencerjanssen@gmail.com
-- Stability : unstable
-- Portability : not portable, uses mtl, X11, posix
--
-- xmonad, a minimalist, tiling window manager for X11
--
-----------------------------------------------------------------------------
module XMonad.Main (xmonad) where
import Data.Bits
import Data.List ((\\))
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 Foreign.C
import Foreign.Ptr
import System.Environment (getArgs)
import Graphics.X11.Xlib hiding (refreshKeyboardMapping)
import Graphics.X11.Xlib.Extras
import XMonad.Core
import qualified XMonad.Config as Default
import XMonad.StackSet (new, floating, member)
import qualified XMonad.StackSet as W
import XMonad.Operations
import System.IO
------------------------------------------------------------------------
-- Locale support
#include <locale.h>
foreign import ccall unsafe "locale.h setlocale"
c_setlocale :: CInt -> Ptr CChar -> IO (Ptr CChar)
------------------------------------------------------------------------
-- |
-- The main entry point
--
xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
xmonad initxmc = do
-- setup locale information from environment
withCString "" $ c_setlocale (#const LC_ALL)
-- ignore SIGPIPE and SIGCHLD
installSignalHandlers
-- First, wrap the layout in an existential, to keep things pretty:
let xmc = initxmc { layoutHook = Layout $ layoutHook initxmc }
dpy <- openDisplay ""
let dflt = defaultScreen dpy
rootw <- rootWindow dpy dflt
-- If another WM is running, a BadAccess error will be returned. The
-- default error handler will write the exception to stderr and exit with
-- an error.
selectInput dpy rootw $ substructureRedirectMask .|. substructureNotifyMask
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
.|. buttonPressMask
sync dpy False -- sync to ensure all outstanding errors are delivered
-- turn off the default handler in favor of one that ignores all errors
-- (ugly, I know)
xSetErrorHandler -- in C, I'm too lazy to write the binding: dons
xinesc <- getCleanedScreenInfo dpy
nbc <- do v <- initColor dpy $ normalBorderColor xmc
~(Just nbc_) <- initColor dpy $ normalBorderColor Default.defaultConfig
return (fromMaybe nbc_ v)
fbc <- do v <- initColor dpy $ focusedBorderColor xmc
~(Just fbc_) <- initColor dpy $ focusedBorderColor Default.defaultConfig
return (fromMaybe fbc_ v)
hSetBuffering stdout NoBuffering
args <- getArgs
let layout = layoutHook xmc
lreads = readsLayout layout
initialWinset = new layout (workspaces xmc) $ map SD xinesc
maybeRead reads' s = case reads' s of
[(x, "")] -> Just x
_ -> Nothing
winset = fromMaybe initialWinset $ do
("--resume" : s : _) <- return args
ws <- maybeRead reads s
return . W.ensureTags layout (workspaces xmc)
$ W.mapLayout (fromMaybe layout . maybeRead lreads) ws
cf = XConf
{ display = dpy
, config = xmc
, theRoot = rootw
, normalBorder = nbc
, focusedBorder = fbc
, keyActions = keys xmc xmc
, buttonActions = mouseBindings xmc xmc
, mouseFocused = False
, mousePosition = Nothing }
st = XState
{ windowset = initialWinset
, mapped = S.empty
, waitingUnmap = M.empty
, dragging = Nothing }
allocaXEvent $ \e ->
runX cf st $ do
grabKeys
grabButtons
io $ sync dpy False
ws <- io $ scan dpy rootw
-- bootstrap the windowset, Operations.windows will identify all
-- the windows in winset as new and set initial properties for
-- those windows. Remove all windows that are no longer top-level
-- children of the root, they may have disappeared since
-- restarting.
windows . const . foldr W.delete winset $ W.allWindows winset \\ ws
-- manage the as-yet-unmanaged windows
mapM_ manage (ws \\ W.allWindows winset)
userCode $ startupHook initxmc
-- main loop, for all you HOF/recursion fans out there.
forever_ $ prehandle =<< io (nextEvent dpy e >> getEvent e)
return ()
where
forever_ a = a >> forever_ a
-- if the event gives us the position of the pointer, set mousePosition
prehandle e = let mouse = do guard (ev_event_type e `elem` evs)
return (fromIntegral (ev_x_root e)
,fromIntegral (ev_y_root e))
in local (\c -> c { mousePosition = mouse }) (handle e)
evs = [ keyPress, keyRelease, enterNotify, leaveNotify
, buttonPress, buttonRelease]
-- ---------------------------------------------------------------------
-- | Event handler. Map X events onto calls into Operations.hs, which
-- modify our internal model of the window manager state.
--
-- Events dwm handles that we don't:
--
-- [ButtonPress] = buttonpress,
-- [Expose] = expose,
-- [PropertyNotify] = propertynotify,
--
handle :: Event -> X ()
-- run window manager command
handle (KeyEvent {ev_event_type = t, ev_state = m, ev_keycode = code})
| t == keyPress = withDisplay $ \dpy -> do
s <- io $ keycodeToKeysym dpy code 0
mClean <- cleanMask m
ks <- asks keyActions
userCodeDef () $ whenJust (M.lookup (mClean, s) ks) id
-- manage a new window
handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
wa <- io $ getWindowAttributes dpy w -- ignore override windows
-- need to ignore mapping requests by managed windows not on the current workspace
managed <- isClient w
when (not (wa_override_redirect wa) && not managed) $ do manage w
-- window destroyed, unmanage it
-- window gone, unmanage it
handle (DestroyWindowEvent {ev_window = w}) = whenX (isClient w) $ do
unmanage w
modify (\s -> s { mapped = S.delete w (mapped s)
, waitingUnmap = M.delete w (waitingUnmap s)})
-- We track expected unmap events in waitingUnmap. We ignore this event unless
-- it is synthetic or we are not expecting an unmap notification from a window.
handle (UnmapEvent {ev_window = w, ev_send_event = synthetic}) = whenX (isClient w) $ do
e <- gets (fromMaybe 0 . M.lookup w . waitingUnmap)
if (synthetic || e == 0)
then unmanage w
else modify (\s -> s { waitingUnmap = M.update mpred w (waitingUnmap s) })
where mpred 1 = Nothing
mpred n = Just $ pred n
-- set keyboard mapping
handle e@(MappingNotifyEvent {}) = do
io $ refreshKeyboardMapping e
when (ev_request e == mappingKeyboard) grabKeys
-- handle button release, which may finish dragging.
handle e@(ButtonEvent {ev_event_type = t})
| t == buttonRelease = do
drag <- gets dragging
case drag of
-- we're done dragging and have released the mouse:
Just (_,f) -> modify (\s -> s { dragging = Nothing }) >> f
Nothing -> broadcastMessage e
-- handle motionNotify event, which may mean we are dragging.
handle e@(MotionEvent {ev_event_type = _t, ev_x = x, ev_y = y}) = do
drag <- gets dragging
case drag of
Just (d,_) -> d (fromIntegral x) (fromIntegral y) -- we're dragging
Nothing -> broadcastMessage e
-- click on an unfocused window, makes it focused on this workspace
handle e@(ButtonEvent {ev_window = w,ev_event_type = t,ev_button = b })
| t == buttonPress = do
-- If it's the root window, then it's something we
-- grabbed in grabButtons. Otherwise, it's click-to-focus.
isr <- isRoot w
m <- cleanMask $ ev_state e
mact <- asks (M.lookup (m, b) . buttonActions)
case mact of
(Just act) | isr -> act $ ev_subwindow e
_ -> focus w
broadcastMessage e -- Always send button events.
-- entered a normal window: focus it if focusFollowsMouse is set to
-- True in the user's config.
handle e@(CrossingEvent {ev_window = w, ev_event_type = t})
| t == enterNotify && ev_mode e == notifyNormal
= whenX (asks $ focusFollowsMouse . config) (focus w)
-- left a window, check if we need to focus root
handle e@(CrossingEvent {ev_event_type = t})
| t == leaveNotify
= do rootw <- asks theRoot
when (ev_window e == rootw && not (ev_same_screen e)) $ setFocusX rootw
-- configure a window
handle e@(ConfigureRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
ws <- gets windowset
wa <- io $ getWindowAttributes dpy w
bw <- asks (borderWidth . config)
if M.member w (floating ws)
|| not (member w ws)
then do io $ configureWindow dpy w (ev_value_mask e) $ WindowChanges
{ wc_x = ev_x e
, wc_y = ev_y e
, wc_width = ev_width e
, wc_height = ev_height e
, wc_border_width = fromIntegral bw
, wc_sibling = ev_above e
, wc_stack_mode = ev_detail e }
when (member w ws) (float w)
else io $ allocaXEvent $ \ev -> do
setEventType ev configureNotify
setConfigureEvent ev w w
(wa_x wa) (wa_y wa) (wa_width wa)
(wa_height wa) (ev_border_width e) none (wa_override_redirect wa)
sendEvent dpy w False 0 ev
io $ sync dpy False
-- configuration changes in the root may mean display settings have changed
handle (ConfigureEvent {ev_window = w}) = whenX (isRoot w) rescreen
-- property notify
handle PropertyEvent { ev_event_type = t, ev_atom = a }
| t == propertyNotify && a == wM_NAME = userCodeDef () =<< asks (logHook . config)
handle e = broadcastMessage e -- trace (eventName e) -- ignoring
-- ---------------------------------------------------------------------
-- IO stuff. Doesn't require any X state
-- Most of these things run only on startup (bar grabkeys)
-- | scan for any new windows to manage. If they're already managed,
-- this should be idempotent.
scan :: Display -> Window -> IO [Window]
scan dpy rootw = do
(_, _, ws) <- queryTree dpy rootw
filterM ok ws
-- TODO: scan for windows that are either 'IsViewable' or where WM_STATE ==
-- Iconic
where ok w = do wa <- getWindowAttributes dpy w
a <- internAtom dpy "WM_STATE" False
p <- getWindowProperty32 dpy a w
let ic = case p of
Just (3:_) -> True -- 3 for iconified
_ -> False
return $ not (wa_override_redirect wa)
&& (wa_map_state wa == waIsViewable || ic)
-- | Grab the keys back
grabKeys :: X ()
grabKeys = do
XConf { display = dpy, theRoot = rootw } <- ask
let grab kc m = io $ grabKey dpy kc m rootw True grabModeAsync grabModeAsync
io $ ungrabKey dpy anyKey anyModifier rootw
ks <- asks keyActions
forM_ (M.keys ks) $ \(mask,sym) -> do
kc <- io $ keysymToKeycode dpy sym
-- "If the specified KeySym is not defined for any KeyCode,
-- XKeysymToKeycode() returns zero."
when (kc /= '\0') $ mapM_ (grab kc . (mask .|.)) =<< extraModifiers
-- | XXX comment me
grabButtons :: X ()
grabButtons = do
XConf { display = dpy, theRoot = rootw } <- ask
let grab button mask = io $ grabButton dpy button mask rootw False buttonPressMask
grabModeAsync grabModeSync none none
io $ ungrabButton dpy anyButton anyModifier rootw
ems <- extraModifiers
ba <- asks buttonActions
mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys $ ba)

114
XMonad/ManageHook.hs Normal file
View File

@@ -0,0 +1,114 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.ManageHook
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : spencerjanssen@gmail.com
-- Stability : unstable
-- Portability : not portable, uses cunning newtype deriving
--
-- An EDSL for ManageHooks
--
-----------------------------------------------------------------------------
-- XXX examples required
module XMonad.ManageHook where
import Prelude hiding (catch)
import XMonad.Core
import Graphics.X11.Xlib.Extras
import Graphics.X11.Xlib (Display, Window, internAtom, wM_NAME)
import Control.Exception (bracket, catch)
import Control.Monad.Reader
import Data.Maybe
import Data.Monoid
import qualified XMonad.StackSet as W
import XMonad.Operations (floatLocation, reveal)
-- | Lift an 'X' action to a 'Query'.
liftX :: X a -> Query a
liftX = Query . lift
-- | The identity hook that returns the WindowSet unchanged.
idHook :: ManageHook
idHook = doF id
-- | Compose two 'ManageHook's.
(<+>) :: ManageHook -> ManageHook -> ManageHook
(<+>) = mappend
-- | Compose the list of 'ManageHook's.
composeAll :: [ManageHook] -> ManageHook
composeAll = mconcat
-- | @p --> x@. If @p@ returns 'True', execute the 'ManageHook'.
(-->) :: Query Bool -> ManageHook -> ManageHook
p --> f = p >>= \b -> if b then f else mempty
-- | @q =? x@. if the result of @q@ equals @x@, return 'True'.
(=?) :: Eq a => Query a -> a -> Query Bool
q =? x = fmap (== x) q
infixr 3 <&&>, <||>
-- | '&&' lifted to a 'Monad'.
(<&&>) :: Monad m => m Bool -> m Bool -> m Bool
(<&&>) = liftM2 (&&)
-- | '||' lifted to a 'Monad'.
(<||>) :: Monad m => m Bool -> m Bool -> m Bool
(<||>) = liftM2 (||)
-- | Return the window title.
title :: Query String
title = ask >>= \w -> liftX $ do
d <- asks display
let
getProp =
(internAtom d "_NET_WM_NAME" False >>= getTextProperty d w)
`catch` \_ -> getTextProperty d w wM_NAME
extract = fmap head . wcTextPropertyToTextList d
io $ bracket getProp (xFree . tp_value) extract `catch` \_ -> return ""
-- | Return the application name.
appName :: Query String
appName = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resName $ io $ getClassHint d w)
-- | Backwards compatible alias for 'appName'.
resource :: Query String
resource = appName
-- | Return the resource class.
className :: Query String
className = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resClass $ io $ getClassHint d w)
-- | A query that can return an arbitrary X property of type 'String',
-- identified by name.
stringProperty :: String -> Query String
stringProperty p = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap (fromMaybe "") $ getStringProperty d w p)
getStringProperty :: Display -> Window -> String -> X (Maybe String)
getStringProperty d w p = do
a <- getAtom p
md <- io $ getWindowProperty8 d a w
return $ fmap (map (toEnum . fromIntegral)) md
-- | Modify the 'WindowSet' with a pure function.
doF :: (WindowSet -> WindowSet) -> ManageHook
doF = return . Endo
-- | Move the window to the floating layer.
doFloat :: ManageHook
doFloat = ask >>= \w -> doF . W.float w . snd =<< liftX (floatLocation w)
-- | Map the window and remove it from the 'WindowSet'.
doIgnore :: ManageHook
doIgnore = ask >>= \w -> liftX (reveal w) >> doF (W.delete w)
-- | Move the window to a given workspace
doShift :: WorkspaceId -> ManageHook
doShift = doF . W.shift

565
XMonad/Operations.hs Normal file
View File

@@ -0,0 +1,565 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
-- --------------------------------------------------------------------------
-- |
-- Module : XMonad.Operations
-- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : dons@cse.unsw.edu.au
-- Stability : unstable
-- Portability : not portable, Typeable deriving, mtl, posix
--
-- Operations.
--
-----------------------------------------------------------------------------
module XMonad.Operations where
import XMonad.Core
import XMonad.Layout (Full(..))
import qualified XMonad.StackSet as W
import Data.Maybe
import Data.Monoid (Endo(..))
import Data.List (nub, (\\), find)
import Data.Bits ((.|.), (.&.), complement)
import Data.Ratio
import qualified Data.Map as M
import qualified Data.Set as S
import Control.Applicative
import Control.Monad.Reader
import Control.Monad.State
import qualified Control.Exception as C
import System.IO
import System.Posix.Process (executeFile)
import Graphics.X11.Xlib
import Graphics.X11.Xinerama (getScreenInfo)
import Graphics.X11.Xlib.Extras
-- ---------------------------------------------------------------------
-- |
-- Window manager operations
-- manage. Add a new window to be managed in the current workspace.
-- Bring it into focus.
--
-- Whether the window is already managed, or not, it is mapped, has its
-- border set, and its event mask set.
--
manage :: Window -> X ()
manage w = whenX (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 <$> io (getTransientForHint d w)
rr <- snd `fmap` floatLocation w
-- ensure that float windows don't go over the edge of the screen
let adjust (W.RationalRect x y wid h) | x + wid > 1 || y + h > 1 || x < 0 || y < 0
= W.RationalRect (0.5 - wid/2) (0.5 - h/2) wid h
adjust r = r
f ws | isFixedSize || isTransient = W.float w (adjust rr) . W.insertUp w . W.view i $ ws
| otherwise = W.insertUp w ws
where i = W.tag $ W.workspace $ W.current ws
mh <- asks (manageHook . config)
g <- fmap appEndo $ userCodeDef (Endo id) (runQuery mh w)
windows (g . f)
-- | unmanage. A window no longer exists, remove it from the window
-- list, on whatever workspace it is.
--
unmanage :: Window -> X ()
unmanage = windows . W.delete
-- | Kill the specified window. If we do kill it, we'll get a
-- delete notify back from X.
--
-- There are two ways to delete a window. Either just kill it, or if it
-- supports the delete protocol, send a delete event (e.g. firefox)
--
killWindow :: Window -> X ()
killWindow w = withDisplay $ \d -> do
wmdelt <- atom_WM_DELETE_WINDOW ; wmprot <- atom_WM_PROTOCOLS
protocols <- io $ getWMProtocols d w
io $ if wmdelt `elem` protocols
then allocaXEvent $ \ev -> do
setEventType ev clientMessage
setClientMessageEvent ev w wmprot 32 wmdelt 0
sendEvent d w False noEventMask ev
else killClient d w >> return ()
-- | Kill the currently focused client.
kill :: X ()
kill = withFocused killWindow
-- ---------------------------------------------------------------------
-- Managing windows
-- | windows. Modify the current window list with a pure function, and refresh
windows :: (WindowSet -> WindowSet) -> X ()
windows f = do
XState { windowset = old } <- get
let oldvisible = concatMap (W.integrate' . W.stack . W.workspace) $ W.current old : W.visible old
newwindows = W.allWindows ws \\ W.allWindows old
ws = f old
XConf { display = d , normalBorder = nbc, focusedBorder = fbc } <- ask
mapM_ setInitialProperties newwindows
whenJust (W.peek old) $ \otherw -> io $ setWindowBorder d otherw nbc
modify (\s -> s { windowset = ws })
-- notify non visibility
let tags_oldvisible = map (W.tag . W.workspace) $ W.current old : W.visible old
gottenhidden = filter (flip elem tags_oldvisible . W.tag) $ W.hidden ws
mapM_ (sendMessageWithNoRefresh Hide) gottenhidden
-- for each workspace, layout the currently visible workspaces
let allscreens = W.screens ws
summed_visible = scanl (++) [] $ map (W.integrate' . W.stack . W.workspace) allscreens
rects <- fmap concat $ forM (zip allscreens summed_visible) $ \ (w, vis) -> do
let wsp = W.workspace w
this = W.view n ws
n = W.tag wsp
tiled = (W.stack . W.workspace . W.current $ this)
>>= W.filter (`M.notMember` W.floating ws)
>>= W.filter (`notElem` vis)
viewrect = screenRect $ W.screenDetail w
-- just the tiled windows:
-- now tile the windows on this workspace, modified by the gap
(rs, ml') <- runLayout wsp { W.stack = tiled } viewrect `catchX`
runLayout wsp { W.stack = tiled, W.layout = Layout Full } viewrect
updateLayout n ml'
let m = W.floating ws
flt = [(fw, scaleRationalRect viewrect r)
| fw <- filter (flip M.member m) (W.index this)
, Just r <- [M.lookup fw m]]
vs = flt ++ rs
io $ restackWindows d (map fst vs)
-- return the visible windows for this workspace:
return vs
let visible = map fst rects
mapM_ (uncurry tileWindow) rects
whenJust (W.peek ws) $ \w -> io $ setWindowBorder d w fbc
-- hide every window that was potentially visible before, but is not
-- given a position by a layout now.
mapM_ hide (nub (oldvisible ++ newwindows) \\ visible)
mapM_ reveal visible
setTopFocus
-- all windows that are no longer in the windowset are marked as
-- withdrawn, it is important to do this after the above, otherwise 'hide'
-- will overwrite withdrawnState with iconicState
mapM_ (flip setWMState withdrawnState) (W.allWindows old \\ W.allWindows ws)
isMouseFocused <- asks mouseFocused
unless isMouseFocused $ clearEvents enterWindowMask
asks (logHook . config) >>= userCodeDef ()
-- | Produce the actual rectangle from a screen and a ratio on that screen.
scaleRationalRect :: Rectangle -> W.RationalRect -> Rectangle
scaleRationalRect (Rectangle sx sy sw sh) (W.RationalRect rx ry rw rh)
= Rectangle (sx + scale sw rx) (sy + scale sh ry) (scale sw rw) (scale sh rh)
where scale s r = floor (toRational s * r)
-- | setWMState. set the WM_STATE property
setWMState :: Window -> Int -> X ()
setWMState w v = withDisplay $ \dpy -> do
a <- atom_WM_STATE
io $ changeProperty32 dpy w a a propModeReplace [fromIntegral v, fromIntegral none]
-- | hide. Hide a window by unmapping it, and setting Iconified.
hide :: Window -> X ()
hide w = whenX (gets (S.member w . mapped)) $ withDisplay $ \d -> do
io $ do selectInput d w (clientMask .&. complement structureNotifyMask)
unmapWindow d w
selectInput d w clientMask
setWMState w iconicState
-- this part is key: we increment the waitingUnmap counter to distinguish
-- between client and xmonad initiated unmaps.
modify (\s -> s { waitingUnmap = M.insertWith (+) w 1 (waitingUnmap s)
, mapped = S.delete w (mapped s) })
-- | 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 normalState
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 -> do
setWMState w iconicState
io $ selectInput d w $ clientMask
bw <- asks (borderWidth . config)
io $ setWindowBorderWidth d w bw
-- we must initially set the color of new windows, to maintain invariants
-- required by the border setting in 'windows'
io $ setWindowBorder d w nb
-- | refresh. Render the currently visible workspaces, as determined by
-- the 'StackSet'. Also, set focus to the focused window.
--
-- This is our 'view' operation (MVC), in that it pretty prints our model
-- with X calls.
--
refresh :: X ()
refresh = windows id
-- | clearEvents. Remove all events of a given type from the event queue.
clearEvents :: EventMask -> X ()
clearEvents mask = withDisplay $ \d -> io $ do
sync d False
allocaXEvent $ \p -> fix $ \again -> do
more <- checkMaskEvent d mask p
when more again -- beautiful
-- | tileWindow. Moves and resizes w such that it fits inside the given
-- rectangle, including its border.
tileWindow :: Window -> Rectangle -> X ()
tileWindow w r = withDisplay $ \d -> do
bw <- (fromIntegral . wa_border_width) <$> 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)
-- ---------------------------------------------------------------------
-- | Returns 'True' if the first rectangle is contained within, but not equal
-- to the second.
containedIn :: Rectangle -> Rectangle -> Bool
containedIn r1@(Rectangle x1 y1 w1 h1) r2@(Rectangle x2 y2 w2 h2)
= and [ r1 /= r2
, x1 >= x2
, y1 >= y2
, fromIntegral x1 + w1 <= fromIntegral x2 + w2
, fromIntegral y1 + h1 <= fromIntegral y2 + h2 ]
-- | Given a list of screens, remove all duplicated screens and screens that
-- are entirely contained within another.
nubScreens :: [Rectangle] -> [Rectangle]
nubScreens xs = nub . filter (\x -> not $ any (x `containedIn`) xs) $ xs
-- | Cleans the list of screens according to the rules documented for
-- nubScreens.
getCleanedScreenInfo :: MonadIO m => Display -> m [Rectangle]
getCleanedScreenInfo = io . fmap nubScreens . getScreenInfo
-- | rescreen. The screen configuration may have changed (due to
-- xrandr), update the state and refresh the screen, and reset the gap.
rescreen :: X ()
rescreen = do
xinesc <- withDisplay getCleanedScreenInfo
windows $ \ws@(W.StackSet { W.current = v, W.visible = vs, W.hidden = hs }) ->
let (xs, ys) = splitAt (length xinesc) $ map W.workspace (v:vs) ++ hs
(a:as) = zipWith3 W.Screen xs [0..] $ map SD xinesc
in ws { W.current = a
, W.visible = as
, W.hidden = ys }
-- ---------------------------------------------------------------------
-- | setButtonGrab. Tell whether or not to intercept clicks on a given window
setButtonGrab :: Bool -> Window -> X ()
setButtonGrab grab w = withDisplay $ \d -> io $
if grab
then forM_ [button1, button2, button3] $ \b ->
grabButton d b anyModifier w False buttonPressMask
grabModeAsync grabModeSync none none
else ungrabButton d anyButton anyModifier w
-- ---------------------------------------------------------------------
-- Setting keyboard focus
-- | Set the focus to the window on top of the stack, or root
setTopFocus :: X ()
setTopFocus = withWindowSet $ maybe (setFocusX =<< asks theRoot) setFocusX . W.peek
-- | Set focus explicitly to window 'w' if it is managed by us, or root.
-- This happens if X notices we've moved the mouse (and perhaps moved
-- the mouse to a new screen).
focus :: Window -> X ()
focus w = local (\c -> c { mouseFocused = True }) $ withWindowSet $ \s -> do
let stag = W.tag . W.workspace
curr = stag $ W.current s
mnew <- maybe (return Nothing) (fmap (fmap stag) . uncurry pointScreen)
=<< asks mousePosition
root <- asks theRoot
case () of
_ | W.member w s && W.peek s /= Just w -> windows (W.focusWindow w)
| Just new <- mnew, w == root && curr /= new
-> windows (W.view new)
| otherwise -> return ()
-- | Call X to set the keyboard focus details.
setFocusX :: Window -> X ()
setFocusX w = withWindowSet $ \ws -> do
dpy <- asks display
-- clear mouse button grab and border on other windows
forM_ (W.current ws : W.visible ws) $ \wk -> do
forM_ (W.index (W.view (W.tag (W.workspace wk)) ws)) $ \otherw -> do
setButtonGrab True otherw
-- If we ungrab buttons on the root window, we lose our mouse bindings.
whenX (not <$> isRoot w) $ setButtonGrab False w
io $ do setInputFocus dpy w revertToPointerRoot 0
-- raiseWindow dpy w
------------------------------------------------------------------------
-- Message handling
-- | Throw a message to the current 'LayoutClass' possibly modifying how we
-- layout the windows, then refresh.
sendMessage :: Message a => a -> X ()
sendMessage a = do
w <- W.workspace . W.current <$> 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 all layouts, without refreshing.
broadcastMessage :: Message a => a -> X ()
broadcastMessage a = withWindowSet $ \ws -> do
let c = W.workspace . W.current $ ws
v = map W.workspace . W.visible $ ws
h = W.hidden ws
mapM_ (sendMessageWithNoRefresh a) (c : v ++ h)
-- | Send a message to a layout, without refreshing.
sendMessageWithNoRefresh :: Message a => a -> W.Workspace WorkspaceId (Layout Window) Window -> X ()
sendMessageWithNoRefresh a w =
handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing >>=
updateLayout (W.tag w)
-- | Update the layout field of a workspace
updateLayout :: WorkspaceId -> Maybe (Layout Window) -> X ()
updateLayout i ml = whenJust ml $ \l ->
runOnWorkspaces $ \ww -> return $ if W.tag ww == i then ww { W.layout = l} else ww
-- | Set the layout of the currently viewed workspace
setLayout :: Layout Window -> X ()
setLayout l = do
ss@(W.StackSet { W.current = c@(W.Screen { W.workspace = ws })}) <- gets windowset
handleMessage (W.layout ws) (SomeMessage ReleaseResources)
windows $ const $ ss {W.current = c { W.workspace = ws { W.layout = l } } }
------------------------------------------------------------------------
-- Utilities
-- | Return workspace visible on screen 'sc', or 'Nothing'.
screenWorkspace :: ScreenId -> X (Maybe WorkspaceId)
screenWorkspace sc = withWindowSet $ return . W.lookupWorkspace sc
-- | Apply an 'X' operation to the currently focused window, if there is one.
withFocused :: (Window -> X ()) -> X ()
withFocused f = withWindowSet $ \w -> whenJust (W.peek w) f
-- | 'True' if window is under management by us
isClient :: Window -> X Bool
isClient w = withWindowSet $ return . W.member w
-- | Combinations of extra modifier masks we need to grab keys\/buttons for.
-- (numlock and capslock)
extraModifiers :: X [KeyMask]
extraModifiers = do
nlm <- asks (numlockMask . config)
return [0, nlm, lockMask, nlm .|. lockMask ]
-- | Strip numlock\/capslock from a mask
cleanMask :: KeyMask -> X KeyMask
cleanMask km = do
nlm <- asks (numlockMask . config)
return (complement (nlm .|. lockMask) .&. km)
-- | Get the 'Pixel' value for a named color
initColor :: Display -> String -> IO (Maybe Pixel)
initColor dpy c = C.handle (\_ -> return Nothing) $
(Just . color_pixel . fst) <$> allocNamedColor dpy colormap c
where colormap = defaultColormap dpy (defaultScreen dpy)
------------------------------------------------------------------------
-- | @restart name resume@. Attempt to restart xmonad by executing the program
-- @name@. If @resume@ is 'True', restart with the current window state.
-- When executing another window manager, @resume@ should be 'False'.
restart :: String -> Bool -> X ()
restart prog resume = do
broadcastMessage ReleaseResources
io . flush =<< asks display
args <- if resume then gets (("--resume":) . return . showWs . windowset) else return []
catchIO (executeFile prog True args Nothing)
where showWs = show . W.mapLayout show
------------------------------------------------------------------------
-- | 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
bw <- fi <$> asks (borderWidth . config)
sc <- fromMaybe (W.current ws) <$> pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
let sr = screenRect . W.screenDetail $ sc
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
-- | Given a point, determine the screen (if any) that contains it.
pointScreen :: Position -> Position
-> X (Maybe (W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail))
pointScreen x y = withWindowSet $ return . find p . W.screens
where p = pointWithin x y . screenRect . W.screenDetail
-- | @pointWithin x y r@ returns 'True' if the @(x, y)@ co-ordinate is within
-- @r@.
pointWithin :: Position -> Position -> Rectangle -> Bool
pointWithin x y r = x >= rect_x r &&
x < rect_x r + fromIntegral (rect_width r) &&
y >= rect_y r &&
y < rect_y r + fromIntegral (rect_height r)
-- | Make a tiled window floating, using its suggested rectangle
float :: Window -> X ()
float w = do
(sc, rr) <- floatLocation w
windows $ \ws -> W.float w rr . fromMaybe ws $ do
i <- W.findTag w ws
guard $ i `elem` map (W.tag . W.workspace) (W.screens ws)
f <- W.peek ws
sw <- W.lookupWorkspace sc ws
return (W.focusWindow f . W.shiftWin sw w $ ws)
-- ---------------------------------------------------------------------
-- Mouse handling
-- | Accumulate mouse motion events
mouseDrag :: (Position -> Position -> X ()) -> X () -> X ()
mouseDrag 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`
applySizeHintsContents sh (ex - fromIntegral (wa_x wa),
ey - fromIntegral (wa_y wa)))
(float w)
-- ---------------------------------------------------------------------
-- | Support for window size hints
type D = (Dimension, Dimension)
-- | Given a window, build an adjuster function that will reduce the given
-- dimensions according to the window's border width and size hints.
mkAdjust :: Window -> X (D -> D)
mkAdjust w = withDisplay $ \d -> liftIO $ do
sh <- getWMNormalHints d w
bw <- fmap (fromIntegral . wa_border_width) $ getWindowAttributes d w
return $ applySizeHints bw sh
-- | Reduce the dimensions if needed to comply to the given SizeHints, taking
-- window borders into account.
applySizeHints :: Integral a => Dimension -> SizeHints -> (a, a) -> D
applySizeHints bw sh =
tmap (+ 2 * bw) . applySizeHintsContents sh . tmap (subtract $ 2 * fromIntegral bw)
where
tmap f (x, y) = (f x, f y)
-- | Reduce the dimensions if needed to comply to the given SizeHints.
applySizeHintsContents :: Integral a => SizeHints -> (a, a) -> D
applySizeHintsContents sh (w, h) =
applySizeHints' sh (fromIntegral $ max 1 w, fromIntegral $ max 1 h)
-- | 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

View File

@@ -1,43 +1,60 @@
{-# LANGUAGE PatternGuards #-}
-----------------------------------------------------------------------------
-- |
-- Module : StackSet
-- Module : XMonad.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
--
module StackSet (
module XMonad.StackSet (
-- * Introduction
-- $intro
StackSet(..), Workspace(..), Screen(..), StackOrNot, Stack(..), RationalRect(..),
-- ** The Zipper
-- $zipper
-- ** Xinerama support
-- $xinerama
-- ** Master and Focus
-- $focus
StackSet(..), Workspace(..), Screen(..), Stack(..), RationalRect(..),
-- * Construction
-- $construction
new, view, greedyView,
-- * Xinerama operations
-- $xinerama
lookupWorkspace,
screens, workspaces, allWindows, currentTag,
-- * Operations on the current stack
-- $stackOperations
peek, index, integrate, integrate', differentiate,
focusUp, focusDown,
focusWindow, tagMember, member, findIndex,
focusUp, focusDown, focusUp', focusDown', focusMaster, focusWindow,
tagMember, renameTag, ensureTags, member, findTag, mapWorkspace, mapLayout,
-- * Modifying the stackset
-- $modifyStackset
insertUp, delete, filter,
insertUp, delete, delete', filter,
-- * Setting the master window
-- $settingMW
swapMaster, swapUp, swapDown, modify, modify', float, sink, -- needed by users
swapUp, swapDown, swapMaster, shiftMaster, modify, modify', float, sink, -- needed by users
-- * Composite operations
-- $composite
shift
shift, shiftWin,
-- for testing
abort
) where
import Prelude hiding (filter)
import Data.Maybe (listToMaybe)
import qualified Data.List as L (delete,deleteBy,find,splitAt,filter)
import Data.Maybe (listToMaybe,fromJust,isJust)
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
@@ -58,8 +75,8 @@ import qualified Data.Map as M (Map,insert,delete,empty)
-- Note that workspaces are indexed from 0, windows are numbered
-- uniquely. A '*' indicates the window on each workspace that has
-- focus, and which workspace is current.
--
-- Zipper
-- $zipper
--
-- We encode all the focus tracking directly in the data structure, with a 'zipper':
--
@@ -87,56 +104,24 @@ import qualified Data.Map as M (Map,insert,delete,empty)
-- Another good reference is:
--
-- The Zipper, Haskell wikibook
--
-- Xinerama support:
--
-- $xinerama
-- Xinerama in X11 lets us view multiple virtual workspaces
-- simultaneously. While only one will ever be in focus (i.e. will
-- receive keyboard events), other workspaces may be passively viewable.
-- We thus need to track which virtual workspaces are associated
-- (viewed) on which physical screens. We use a simple Map Workspace
-- Screen for this.
--
-- Master and Focus
-- receive keyboard events), other workspaces may be passively
-- viewable. We thus need to track which virtual workspaces are
-- associated (viewed) on which physical screens. To keep track of
-- this, 'StackSet' keeps separate lists of visible but non-focused
-- workspaces, and non-visible workspaces.
-- $focus
--
-- Each stack tracks a focused item, and for tiling purposes also tracks
-- a 'master' position. The connection between 'master' and 'focus'
-- needs to be well defined. Particular in relation to 'insert' and
-- needs to be well defined, particularly in relation to 'insert' and
-- 'delete'.
--
-- |
-- API changes from xmonad 0.1:
-- StackSet constructor arguments changed. StackSet workspace window screen
--
-- * new, -- was: empty
--
-- * view,
--
-- * index,
--
-- * peek, -- was: peek\/peekStack
--
-- * focusUp, focusDown, -- was: rotate
--
-- * swapUp, swapDown
--
-- * focus -- was: raiseFocus
--
-- * insertUp, -- was: insert\/push
--
-- * delete,
--
-- * swapMaster, -- was: promote\/swap
--
-- * member,
--
-- * shift,
--
-- * lookupWorkspace, -- was: workspace
--
-- * visibleWorkspaces -- gone.
--
------------------------------------------------------------------------
-- |
-- A cursor into a non-empty list of workspaces.
@@ -146,25 +131,26 @@ import qualified Data.Map as M (Map,insert,delete,empty)
-- that are produced are used to track those workspaces visible as
-- Xinerama screens, and those workspaces not visible anywhere.
data StackSet i a sid sd =
StackSet { current :: !(Screen i a sid sd) -- ^ currently focused workspace
, visible :: [Screen i a sid sd] -- ^ non-focused workspaces, visible in xinerama
, hidden :: [Workspace i a] -- ^ workspaces not visible anywhere
, floating :: M.Map a RationalRect -- ^ floating windows
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 sd = Screen { workspace :: !(Workspace i a)
, screen :: !sid
, screenDetail :: !sd }
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
-- A workspace is just a tag, a layout, and a stack.
--
data Workspace i a = Workspace { tag :: !i, stack :: StackOrNot a }
data Workspace i l a = Workspace { tag :: !i, layout :: l, stack :: Maybe (Stack a) }
deriving (Show, Read, Eq)
-- | A structure for window geometries
data RationalRect = RationalRect Rational Rational Rational Rational
deriving (Show, Read, Eq)
@@ -186,8 +172,6 @@ data RationalRect = RationalRect Rational Rational Rational Rational
-- structures, it is the differentiation of a [a], and integrating it
-- back has a natural implementation used in 'index'.
--
type StackOrNot a = Maybe (Stack a)
data Stack a = Stack { focus :: !a -- focused thing in this set
, up :: [a] -- clowns to the left
, down :: [a] } -- jokers to the right
@@ -201,48 +185,50 @@ abort x = error $ "xmonad: StackSet: " ++ x
-- ---------------------------------------------------------------------
-- $construction
-- | /O(n)/. Create a new stackset, of empty stacks, with given tags, with
-- 'm' physical screens. 'm' should be less than or equal to the number of
-- workspace tags. The first workspace in the list will be current.
-- | /O(n)/. Create a new stackset, of empty stacks, with given tags,
-- with physical screens whose descriptions are given by 'm'. The
-- number of physical screens (@length 'm'@) should be less than or
-- equal to the number of workspace tags. The first workspace in the
-- list will be current.
--
-- Xinerama: Virtual workspaces are assigned to physical screens, starting at 0.
--
new :: (Integral s) => [i] -> [sd] -> StackSet i a s sd
new wids m | not (null wids) && length m <= length wids = StackSet cur visi unseen M.empty
where (seen,unseen) = L.splitAt (length m) $ map (flip Workspace Nothing) wids
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"
new _ _ _ = abort "non-positive argument to StackSet.new"
-- |
-- /O(w)/. Set focus to the workspace with index \'i\'.
-- If the index is out of range, return the original StackSet.
-- If the index is out of range, return the original 'StackSet'.
--
-- Xinerama: If the workspace is not visible on any Xinerama screen, it
-- becomes the current screen. If it is in the visible list, it becomes
-- current.
view :: (Eq a, Eq s, Eq i) => i -> StackSet i a s sd -> StackSet i a s sd
view :: (Eq s, Eq i) => i -> StackSet i l a s sd -> StackSet i l a s sd
view i s
| not (i `tagMember` s)
|| i == tag (workspace (current s)) = s -- out of bounds or current
| i == currentTag s = s -- current
| Just x <- L.find ((i==).tag.workspace) (visible s)
-- if it is visible, it is just raised
= s { current = x, visible = current s : L.deleteBy screenEq 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 = (current s) { workspace = x }
, hidden = workspace (current s) : L.delete x (hidden s) }
, hidden = workspace (current s) : L.deleteBy (equating tag) x (hidden s) }
| otherwise = s
where screenEq x y = screen x == screen y
| otherwise = s -- not a member of the stackset
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
@@ -252,23 +238,22 @@ view i s
-- screen, the workspaces of the current screen and the other screen are
-- swapped.
greedyView :: (Eq a, Eq s, Eq i) => i -> StackSet i a s sd -> StackSet i a s sd
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
| 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
-- | 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 sd -> Maybe i
-- 'Nothing' if screen is out of bounds.
lookupWorkspace :: Eq s => s -> StackSet i l a s sd -> Maybe i
lookupWorkspace sc w = listToMaybe [ tag i | Screen i s _ <- current w : visible w, s == sc ]
-- ---------------------------------------------------------------------
@@ -280,13 +265,13 @@ lookupWorkspace sc w = listToMaybe [ tag i | Screen i s _ <- current w : visible
-- 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 sd -> b
with :: b -> (Stack a -> b) -> StackSet i l a s sd -> b
with dflt f = maybe dflt f . stack . workspace . current
-- |
-- Apply a function, and a default value for Nothing, to modify the current stack.
-- Apply a function, and a default value for 'Nothing', to modify the current stack.
--
modify :: StackOrNot a -> (Stack a -> StackOrNot a) -> StackSet i a s sd -> StackSet i a s sd
modify :: Maybe (Stack a) -> (Stack a -> Maybe (Stack a)) -> StackSet i l a s sd -> StackSet i l a s sd
modify d f s = s { current = (current s)
{ workspace = (workspace (current s)) { stack = with d f s }}}
@@ -294,39 +279,40 @@ modify d f s = s { current = (current s)
-- Apply a function to modify the current stack if it isn't empty, and we don't
-- want to empty it.
--
modify' :: (Stack a -> Stack a) -> StackSet i a s sd -> StackSet i a s sd
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.
-- Return 'Just' that element, or 'Nothing' for an empty stack.
--
peek :: StackSet i a s sd -> Maybe a
peek :: StackSet i l a s sd -> Maybe a
peek = with Nothing (return . focus)
-- |
-- /O(n)/. Flatten a Stack into a list.
-- /O(n)/. Flatten a 'Stack' into a list.
--
integrate :: Stack a -> [a]
integrate (Stack x l r) = reverse l ++ x : r
-- |
-- /O(n)/ Flatten a possibly empty stack into a list.
integrate' :: StackOrNot a -> [a]
integrate' :: Maybe (Stack a) -> [a]
integrate' = maybe [] integrate
-- |
-- /O(n)/. Texture a list.
--
differentiate :: [a] -> StackOrNot a
differentiate [] = Nothing
-- /O(n)/. Turn a list into a possibly empty stack (i.e., a zipper):
-- the first element of the list is current, and the rest of the list
-- is down.
differentiate :: [a] -> Maybe (Stack a)
differentiate [] = Nothing
differentiate (x:xs) = Just $ Stack x [] xs
-- |
-- /O(n)/. 'filter p s' returns the elements of 's' such that 'p' evaluates to
-- True. Order is preserved, and focus moves as described for 'delete'.
-- 'True'. Order is preserved, and focus moves as described for 'delete'.
--
filter :: (a -> Bool) -> Stack a -> StackOrNot a
filter :: (a -> Bool) -> Stack a -> Maybe (Stack a)
filter p (Stack f ls rs) = case L.filter p (f:rs) of
f':rs' -> Just $ Stack f' (L.filter p ls) rs' -- maybe move focus down
[] -> case L.filter p ls of -- filter back up
@@ -339,16 +325,14 @@ filter p (Stack f ls rs) = case L.filter p (f:rs) of
-- the head of the list. The implementation is given by the natural
-- integration of a one-hole list cursor, back to a list.
--
index :: StackSet i a s sd -> [a]
index :: StackSet i l a s sd -> [a]
index = with [] integrate
-- let is = t : r ++ reverse l in take (length is) (dropWhile (/= m) (cycle is))
-- |
-- /O(1), O(w) on the wrapping case/.
--
-- focusUp, focusDown. Move the window focus up or down the stack,
-- wrapping if we reach the end. The wrapping should model a -- 'cycle'
-- 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.
--
@@ -356,17 +340,21 @@ index = with [] integrate
-- if we reach the end. Again the wrapping model should 'cycle' on
-- the current stack.
--
focusUp, focusDown, swapUp, swapDown :: StackSet i a s sd -> StackSet i a s sd
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' focusDown'
swapUp = modify' swapUp'
swapDown = modify' (reverseStack . swapUp' . reverseStack)
focusUp', swapUp' :: Stack a -> Stack a
-- | Variants of 'focusUp' and 'focusDown' that work on a
-- 'Stack' rather than an entire 'StackSet'.
focusUp', focusDown' :: 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)
focusDown' = reverseStack . focusUp' . reverseStack
swapUp' :: Stack a -> Stack a
swapUp' (Stack t (l:ls) rs) = Stack t ls (l:rs)
swapUp' (Stack t [] rs) = Stack t (reverse rs) []
@@ -378,36 +366,70 @@ reverseStack (Stack t ls rs) = Stack t rs ls
-- | /O(1) on current window, O(n) in general/. Focus the window 'w',
-- and set its workspace as current.
--
focusWindow :: (Eq s, Eq a, Eq i) => a -> StackSet i a s sd -> StackSet i a s sd
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
n <- findTag w s
return $ until ((Just w ==) . peek) focusUp (view n s)
-- | 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 a s sd -> [Workspace i a]
-- | 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
-- | Is the given tag present in the StackSet?
tagMember :: Eq i => i -> StackSet i a s sd -> Bool
-- | 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
-- | Get the tag of the currently focused workspace.
currentTag :: StackSet i l a s sd -> i
currentTag = tag . workspace . current
-- | Is the given tag present in the 'StackSet'?
tagMember :: Eq i => i -> StackSet i l a s sd -> Bool
tagMember t = elem t . map tag . workspaces
-- |
-- Finding if a window is in the stackset is a little tedious. We could
-- keep a cache :: Map a i, but with more bookkeeping.
--
-- | 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
-- | /O(n)/. Is a window in the StackSet.
member :: Eq a => a -> StackSet i a s sd -> Bool
member a s = maybe False (const True) (findIndex a s)
-- | Ensure that a given set of workspace tags is present by renaming
-- existing workspaces and\/or creating new hidden workspaces as
-- necessary.
ensureTags :: Eq i => l -> [i] -> StackSet i l a s sd -> StackSet i l a s sd
ensureTags l allt st = et allt (map tag (workspaces st) \\ allt) st
where et [] _ s = s
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 l a s sd -> Bool
member a s = isJust (findTag a s)
-- | /O(1) on current window, O(n) in general/.
-- Return Just the workspace index of the given window, or Nothing
-- if the window is not in the StackSet.
findIndex :: Eq a => a -> StackSet i a s sd -> Maybe i
findIndex a s = listToMaybe
-- Return 'Just' the workspace tag of the given window, or 'Nothing'
-- if the window is not in the 'StackSet'.
findTag :: Eq a => a -> StackSet i l a s sd -> Maybe i
findTag a s = listToMaybe
[ tag w | w <- workspaces s, has a (stack w) ]
where has _ Nothing = False
has x (Just (Stack t l r)) = x `elem` (t : l ++ r)
@@ -416,12 +438,10 @@ findIndex a s = listToMaybe
-- $modifyStackset
-- |
-- /O(n)/. (Complexity due to duplicate check). Insert a new element into
-- the stack, above the currently focused element.
--
-- The new element is given focus, and is set as the master window.
-- The previously focused element is moved down. The previously
-- 'master' element is forgotten. (Thus, 'insert' will cause a retiling).
-- /O(n)/. (Complexity due to duplicate check). Insert a new element
-- into the stack, above the currently focused element. The new
-- element is given focus; the previously focused element is moved
-- down.
--
-- If the element is already in the stackset, the original stackset is
-- returned unmodified.
@@ -429,11 +449,11 @@ 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 sd -> StackSet i a s sd
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 (Just $ Stack a [] []) (\(Stack t l r) -> Just $ Stack a l (t:r)) s
-- insertDown :: a -> StackSet i a s sd -> StackSet i a s sd
-- 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 }
@@ -442,32 +462,41 @@ insertUp a s = if member a s then s else insert
-- /O(1) on current window, O(n) in general/. Delete window 'w' if it exists.
-- There are 4 cases to consider:
--
-- * delete on an Nothing workspace leaves it Nothing
-- * delete on an 'Nothing' workspace leaves it Nothing
--
-- * otherwise, try to move focus to the down
--
-- * otherwise, try to move focus to the up
-- * otherwise, you've got an empty workspace, becomes Nothing
--
-- * otherwise, you've got an empty workspace, becomes 'Nothing'
--
-- Behaviour with respect to the master:
--
-- * deleting the master window resets it to the newly focused window
--
-- * otherwise, delete doesn't affect the master.
--
delete :: (Ord a, Eq s) => a -> StackSet i a s sd -> StackSet i a s sd
delete w s = s { current = removeFromScreen (current s)
, visible = map removeFromScreen (visible s)
, hidden = map removeFromWorkspace (hidden s) }
delete :: (Ord a, Eq s) => a -> StackSet i l a s sd -> StackSet i l a s sd
delete w = sink w . delete' w
-- | Only temporarily remove the window from the stack, thereby not destroying special
-- information saved in the 'Stackset'
delete' :: (Eq a, Eq s) => a -> StackSet i l a s sd -> StackSet i l a s sd
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) }
removeFromScreen scr = scr { workspace = removeFromWorkspace (workspace scr) }
------------------------------------------------------------------------
-- | 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 a s sd -> StackSet i a s sd
-- 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 a s sd -> StackSet i a s sd
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) }
------------------------------------------------------------------------
@@ -476,24 +505,57 @@ sink w s = s { floating = M.delete w (floating s) }
-- | /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 sd -> StackSet i a s sd
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 [] (ys ++ x : rs) where (x:ys) = reverse ls
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.
-- | /O(s)/. Set the master window to the focused window.
-- The other windows are kept in order and shifted down on the stack, as if you
-- just hit mod-shift-k a bunch of times.
-- Focus stays with the item moved.
shiftMaster :: StackSet i l a s sd -> StackSet i l a s sd
shiftMaster = modify' $ \c -> case c of
Stack _ [] _ -> c -- already master.
Stack t ls rs -> Stack t [] (reverse ls ++ rs)
-- | /O(s)/. Set focus to the master window.
focusMaster :: StackSet i l a s sd -> StackSet i l a s sd
focusMaster = modify' $ \c -> case c of
Stack _ [] _ -> c
Stack t ls rs -> Stack x [] (xs ++ t : rs) where (x:xs) = reverse ls
--
-- ---------------------------------------------------------------------
-- $composite
-- | /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 :: (Ord a, Eq s, Eq i) => i -> StackSet i a s sd -> StackSet i a s sd
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))
where go w = view curtag . insertUp w . view n . delete' w $ s
curtag = currentTag 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 = findTag w s
go = on n (insertUp w) . on (fromJust from) (delete' w) $ s
on i f = view (currentTag s) . f . view i

View File

@@ -9,32 +9,23 @@ xmonad \- a tiling window manager
.PP
By default, \fBxmonad\fR provides three layout algorithms: tall, wide and fullscreen. In tall or wide mode, windows are tiled and arranged to prevent overlap and maximise screen use. Sets of windows are grouped together on virtual screens, and each screen retains its own layout, which may be reconfigured dynamically. Multiple physical monitors are supported via Xinerama, allowing simultaneous display of a number of screens.
.PP
By utilising the expressivity of a modern functional language with a rich static type system, \fBxmonad\fR provides a complete, featureful window manager in less than 500 lines of code, with an emphasis on correctness and robustness. Internal properties of the window manager are checked using a combination of static guarantees provided by the type system, and type-based automated testing. A benefit of this is that the code is simple to understand, and easy to modify.
By utilising the expressivity of a modern functional language with a rich static type system, \fBxmonad\fR provides a complete, featureful window manager in less than 1200 lines of code, with an emphasis on correctness and robustness. Internal properties of the window manager are checked using a combination of static guarantees provided by the type system, and type-based automated testing. A benefit of this is that the code is simple to understand, and easy to modify.
.SH USAGE
.PP
\fBxmonad\fR places each window into a "workspace". Each workspace can have any number of windows, which you can cycle though with mod-j and mod-k. Windows are either displayed full screen, tiled horizontally, or tiled vertically. You can toggle the layout mode with mod-space, which will cycle through the available modes.
.PP
You can switch to workspace N with mod-N. For example, to switch to workspace 5, you would press mod-5. Similarly, you can move the current window to another workspace with mod-shift-N.
.PP
When running with multiple monitors (Xinerama), each screen has exactly 1 workspace visible. When \fBxmonad\fR starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. If you switch to a workspace which is currently visible on another screen, \fBxmonad\fR simply switches focus to that screen. If you switch to a workspace which is *not* visible, \fBxmonad\fR replaces the workspace on the *current* screen with the workspace you selected.
When running with multiple monitors (Xinerama), each screen has exactly 1 workspace visible. mod-{w,e,r} switch the focus between screens, while shift-mod-{w,e,r} move the current window to that screen. When \fBxmonad\fR starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. When switching workspaces to one that is already visible, the current and visible workspaces are swapped.
.PP
For example, if you have the following configuration:
.RS
.PP
Screen 1: Workspace 2
.PP
Screen 2: Workspace 5 (current workspace)
.RE
.PP
and you wanted to view workspace 7 on screen 1, you would press:
.RS
.PP
mod-2 (to select workspace 2, and make screen 1 the current screen)
.PP
mod-7 (to select workspace 7)
.RE
.PP
Since switching to the workspace currently visible on a given screen is such a common operation, shortcuts are provided: mod-{w,e,r} switch to the workspace currently visible on screens 1, 2, and 3 respectively. Likewise, shift-mod-{w,e,r} moves the current window to the workspace on that screen. Using these keys, the above example would become mod-w mod-7.
.SS Flags
\fBxmonad\fR has several flags which you may pass to the executable. These flags are:
.TP
\fB--recompile
Recompiles your configuration in ~/.xmonad/xmonad.hs
.TP
\fB--version
Display version of \fBxmonad\fR.
.SS Default keyboard bindings
___KEYBINDINGS___
.SH EXAMPLES
@@ -44,6 +35,8 @@ xmonad
.RE
to your \fI~/.xinitrc\fR file
.SH CUSTOMIZATION
\fBxmonad\fR is customized by creating a custom Config.hs and (re)compiling the source code. After recompiling, 'restart' is used to fork the new version, with changes reflected immediately.
\fBxmonad\fR is customized in ~/.xmonad/xmonad.hs, and then restarting with mod-q.
.PP
You can find many extensions to the core feature set in the xmonad-contrib package, available through your package manager or from http://xmonad.org/.
.SH BUGS
Probably. If you find any, please report them: http://code.google.com/p/xmonad/issues/list

279
man/xmonad.hs Normal file
View File

@@ -0,0 +1,279 @@
--
-- xmonad example config file.
--
-- A template showing all available configuration hooks,
-- and how to override the defaults in your own xmonad.hs conf file.
--
-- Normally, you'd only override those defaults you care about.
--
import XMonad
import System.Exit
import qualified XMonad.StackSet as W
import qualified Data.Map as M
-- The preferred terminal program, which is used in a binding below and by
-- certain contrib modules.
--
myTerminal = "xterm"
-- Width of the window border in pixels.
--
myBorderWidth = 1
-- modMask lets you specify which modkey you want to use. The default
-- is mod1Mask ("left alt"). You may also consider using mod3Mask
-- ("right alt"), which does not conflict with emacs keybindings. The
-- "windows key" is usually mod4Mask.
--
myModMask = mod1Mask
-- The mask for the numlock key. Numlock status is "masked" from the
-- current modifier status, so the keybindings will work with numlock on or
-- off. You may need to change this on some systems.
--
-- You can find the numlock modifier by running "xmodmap" and looking for a
-- modifier with Num_Lock bound to it:
--
-- > $ xmodmap | grep Num
-- > mod2 Num_Lock (0x4d)
--
-- Set numlockMask = 0 if you don't have a numlock key, or want to treat
-- numlock status separately.
--
myNumlockMask = mod2Mask
-- The default number of workspaces (virtual screens) and their names.
-- By default we use numeric strings, but any string may be used as a
-- workspace name. The number of workspaces is determined by the length
-- of this list.
--
-- A tagging example:
--
-- > workspaces = ["web", "irc", "code" ] ++ map show [4..9]
--
myWorkspaces = ["1","2","3","4","5","6","7","8","9"]
-- Border colors for unfocused and focused windows, respectively.
--
myNormalBorderColor = "#dddddd"
myFocusedBorderColor = "#ff0000"
------------------------------------------------------------------------
-- Key bindings. Add, modify or remove key bindings here.
--
myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
-- launch a terminal
[ ((modMask .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
-- launch dmenu
, ((modMask, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
-- launch gmrun
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun")
-- close focused window
, ((modMask .|. shiftMask, xK_c ), kill)
-- Rotate through the available layout algorithms
, ((modMask, xK_space ), sendMessage NextLayout)
-- Reset the layouts on the current workspace to default
, ((modMask .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf)
-- Resize viewed windows to the correct size
, ((modMask, xK_n ), refresh)
-- Move focus to the next window
, ((modMask, xK_Tab ), windows W.focusDown)
-- Move focus to the next window
, ((modMask, xK_j ), windows W.focusDown)
-- Move focus to the previous window
, ((modMask, xK_k ), windows W.focusUp )
-- Move focus to the master window
, ((modMask, xK_m ), windows W.focusMaster )
-- Swap the focused window and the master window
, ((modMask, xK_Return), windows W.swapMaster)
-- Swap the focused window with the next window
, ((modMask .|. shiftMask, xK_j ), windows W.swapDown )
-- Swap the focused window with the previous window
, ((modMask .|. shiftMask, xK_k ), windows W.swapUp )
-- Shrink the master area
, ((modMask, xK_h ), sendMessage Shrink)
-- Expand the master area
, ((modMask, xK_l ), sendMessage Expand)
-- Push window back into tiling
, ((modMask, xK_t ), withFocused $ windows . W.sink)
-- Increment the number of windows in the master area
, ((modMask , xK_comma ), sendMessage (IncMasterN 1))
-- Deincrement the number of windows in the master area
, ((modMask , xK_period), sendMessage (IncMasterN (-1)))
-- toggle the status bar gap
-- TODO, update this binding with avoidStruts , ((modMask , xK_b ),
-- Quit xmonad
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess))
-- Restart xmonad
, ((modMask , xK_q ), restart "xmonad" True)
]
++
--
-- mod-[1..9], Switch to workspace N
-- mod-shift-[1..9], Move client to workspace N
--
[((m .|. modMask, k), windows $ f i)
| (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
++
--
-- mod-{w,e,r}, Switch to physical/Xinerama screens 1, 2, or 3
-- mod-shift-{w,e,r}, Move client to screen 1, 2, or 3
--
[((m .|. modMask, key), screenWorkspace sc >>= flip whenJust (windows . f))
| (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
------------------------------------------------------------------------
-- Mouse bindings: default actions bound to mouse events
--
myMouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList $
-- mod-button1, Set the window to floating mode and move by dragging
[ ((modMask, button1), (\w -> focus w >> mouseMoveWindow w
>> windows W.shiftMaster))
-- mod-button2, Raise the window to the top of the stack
, ((modMask, button2), (\w -> focus w >> windows W.shiftMaster))
-- mod-button3, Set the window to floating mode and resize by dragging
, ((modMask, button3), (\w -> focus w >> mouseResizeWindow w
>> windows W.shiftMaster))
-- you may also bind events to the mouse scroll wheel (button4 and button5)
]
------------------------------------------------------------------------
-- Layouts:
-- You can specify and transform your layouts by modifying these values.
-- If you change layout bindings be sure to use 'mod-shift-space' after
-- restarting (with 'mod-q') to reset your layout state to the new
-- defaults, as xmonad preserves your old layout settings by default.
--
-- The available layouts. Note that each layout is separated by |||,
-- which denotes layout choice.
--
myLayout = tiled ||| Mirror tiled ||| Full
where
-- default tiling algorithm partitions the screen into two panes
tiled = Tall nmaster delta ratio
-- The default number of windows in the master pane
nmaster = 1
-- Default proportion of screen occupied by master pane
ratio = 1/2
-- Percent of screen to increment by when resizing panes
delta = 3/100
------------------------------------------------------------------------
-- Window rules:
-- Execute arbitrary actions and WindowSet manipulations when managing
-- a new window. You can use this to, for example, always float a
-- particular program, or have a client always appear on a particular
-- workspace.
--
-- To find the property name associated with a program, use
-- > xprop | grep WM_CLASS
-- and click on the client you're interested in.
--
-- To match on the WM_NAME, you can use 'title' in the same way that
-- 'className' and 'resource' are used below.
--
myManageHook = composeAll
[ className =? "MPlayer" --> doFloat
, className =? "Gimp" --> doFloat
, resource =? "desktop_window" --> doIgnore
, resource =? "kdesktop" --> doIgnore ]
-- Whether focus follows the mouse pointer.
myFocusFollowsMouse :: Bool
myFocusFollowsMouse = True
------------------------------------------------------------------------
-- Status bars and logging
-- Perform an arbitrary action on each internal state change or X event.
-- See the 'DynamicLog' extension for examples.
--
-- To emulate dwm's status bar
--
-- > logHook = dynamicLogDzen
--
myLogHook = return ()
------------------------------------------------------------------------
-- Startup hook
-- Perform an arbitrary action each time xmonad starts or is restarted
-- with mod-q. Used by, e.g., XMonad.Layout.PerWorkspace to initialize
-- per-workspace layout choices.
--
-- By default, do nothing.
myStartupHook = return ()
------------------------------------------------------------------------
-- Now run xmonad with all the defaults we set up.
-- Run xmonad with the settings you specify. No need to modify this.
--
main = xmonad defaults
-- A structure containing your configuration settings, overriding
-- fields in the default config. Any you don't override, will
-- use the defaults defined in xmonad/XMonad/Config.hs
--
-- No need to modify this.
--
defaults = defaultConfig {
-- simple stuff
terminal = myTerminal,
focusFollowsMouse = myFocusFollowsMouse,
borderWidth = myBorderWidth,
modMask = myModMask,
numlockMask = myNumlockMask,
workspaces = myWorkspaces,
normalBorderColor = myNormalBorderColor,
focusedBorderColor = myFocusedBorderColor,
-- key bindings
keys = myKeys,
mouseBindings = myMouseBindings,
-- hooks, layouts
layoutHook = myLayout,
manageHook = myManageHook,
logHook = myLogHook,
startupHook = myStartupHook
}

View File

@@ -1,8 +1,11 @@
{-# OPTIONS -fglasgow-exts #-}
{-# OPTIONS -fglasgow-exts -w #-}
module Properties where
import StackSet hiding (filter)
import qualified StackSet as S (filter)
import Operations (tile)
import XMonad.StackSet hiding (filter)
import XMonad.Layout
import XMonad.Core hiding (workspaces,trace)
import XMonad.Operations ( applyResizeIncHint, applyMaxSizeHint )
import qualified XMonad.StackSet as S (filter)
import Debug.Trace
import Data.Word
@@ -11,8 +14,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
@@ -34,12 +39,13 @@ import qualified Data.Map as M
--
-- The all important Arbitrary instance for StackSet.
--
instance (Integral i, Integral s, Eq a, Arbitrary a, Arbitrary sd)
=> Arbitrary (StackSet i a s sd) 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
lay <- arbitrary -- pick any layout
sds <- replicateM sc arbitrary
ls <- vector sz -- a vector of sz workspaces
@@ -48,8 +54,7 @@ instance (Integral i, Integral s, Eq a, Arbitrary a, Arbitrary sd)
else liftM Just (choose ((-1),length s-1))
| s <- ls ]
return $ fromList (fromIntegral n, sds,fs,ls)
coarbitrary = error "no coarbitrary for StackSet"
return $ fromList (fromIntegral n, sds,fs,ls,lay)
-- | fromList. Build a new StackSet from a list of list of elements,
@@ -62,14 +67,14 @@ instance (Integral i, Integral s, Eq a, Arbitrary a, Arbitrary sd)
-- 'fs' random focused window on each workspace
-- 'xs' list of list of windows
--
fromList :: (Integral i, Integral s, Eq a) => (i, [sd], [Maybe Int], [[a]]) -> StackSet i a s sd
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 (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 [0..genericLength xs-1] 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
@@ -79,7 +84,7 @@ fromList (o,m,fs,xs) =
--
-- Just generate StackSets with Char elements.
--
type T = StackSet (NonNegative Int) Char Int 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
@@ -129,18 +134,20 @@ 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 ->
forAll (vector m) $ \ms ->
invariant $ new [0..fromIntegral n-1] ms
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) =
n `tagMember` x ==> invariant $ view (fromIntegral n) x
invariant $ view (fromIntegral n) x
prop_greedyView_I (n :: NonNegative Int) (x :: T) =
n `tagMember` x ==> invariant $ view (fromIntegral n) x
invariant $ greedyView (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]
@@ -167,6 +174,9 @@ prop_swap_right_I (n :: NonNegative Int) (x :: T) =
prop_shift_I (n :: NonNegative Int) (x :: T) =
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'
@@ -177,11 +187,11 @@ prop_empty (EmptyStackSet x) =
: map workspace (visible x) ++ hidden x ]
-- empty StackSets always have focus on first workspace
prop_empty_current (NonEmptyNubList ns) (NonEmptyNubList sds) =
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 ns sds :: T
where x = new l ns sds :: T
-- no windows will be a member of an empty workspace
prop_member_empty i (EmptyStackSet x)
@@ -229,6 +239,13 @@ prop_greedyView_current (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
where
i = fromIntegral n
-- greedyView leaves things unchanged for invalid workspaces
prop_greedyView_current_id (x :: T) (n :: NonNegative Int) = not (i `tagMember` x) ==>
tag (workspace $ current (greedyView i x)) == j
where
i = fromIntegral n
j = tag (workspace (current x))
-- 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 ==>
@@ -294,6 +311,9 @@ 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
@@ -305,6 +325,9 @@ 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
@@ -323,7 +346,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
@@ -332,20 +358,29 @@ prop_focusWindow_local (n :: NonNegative Int) (x::T ) =
i = fromIntegral n `mod` length s
in hidden_spaces (focusWindow (s !! i) x) == hidden_spaces x
-- On an invalid window, the stackset is unmodified
prop_focusWindow_identity (n :: Char) (x::T ) =
not (n `member` x) ==> focusWindow n x == x
-- ---------------------------------------------------------------------
-- member/findIndex
-- member/findTag
--
-- For all windows in the stackSet, findIndex should identify the
-- For all windows in the stackSet, findTag should identify the
-- correct workspace
--
prop_findIndex (x :: T) =
and [ tag w == fromJust (findIndex i x)
and [ tag w == fromJust (findTag i x)
| w <- workspace (current x) : map workspace (visible x) ++ hidden x
, t <- maybeToList (stack w)
, i <- focus t : up t ++ down t
]
prop_allWindowsMember w (x :: T) = (w `elem` allWindows x) ==> member w x
prop_currentTag (x :: T) =
currentTag x == tag (workspace (current x))
-- ---------------------------------------------------------------------
-- 'insert'
@@ -438,7 +473,7 @@ prop_delete_focus_not_end (x :: T) =
-- preserve order
prop_filter_order (x :: T) =
case stack $ workspace $ current x of
Nothing -> True
Nothing -> True
Just s@(Stack i _ _) -> integrate' (S.filter (/= i) s) == filter (/= i) (integrate' (Just s))
-- ---------------------------------------------------------------------
@@ -494,17 +529,287 @@ prop_shift_reversible i (x :: T) =
n = tag (workspace $ current y)
------------------------------------------------------------------------
-- some properties for layouts:
-- shiftMaster
-- focus/local/idempotent same as swapMaster:
prop_shift_master_focus (x :: T) = peek x == (peek $ shiftMaster x)
prop_shift_master_local (x :: T) = hidden_spaces x == hidden_spaces (shiftMaster x)
prop_shift_master_idempotent (x :: T) = shiftMaster (shiftMaster x) == shiftMaster x
-- ordering is constant modulo the focused window:
prop_shift_master_ordering (x :: T) = case peek x of
Nothing -> True
Just m -> L.delete m (index x) == L.delete m (index $ shiftMaster x)
-- ---------------------------------------------------------------------
-- shiftWin
-- shiftWin on current window is the same as shift
prop_shift_win_focus 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 && findTag 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
prop_float_geometry n (x :: T) =
n `member` x ==> let s = float n geom x
in M.lookup n (floating s) == Just geom
where
geom = RationalRect 100 100 100 100
prop_float_delete n (x :: T) =
n `member` x ==> let s = float n geom x
t = delete n s
in not (n `member` t)
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
-- |
-- Ensure that a given set of workspace tags is present by renaming
-- existing workspaces and\/or creating new hidden workspaces as
-- necessary.
--
prop_ensure (x :: T) l xs = let y = ensureTags l xs x
in and [ n `tagMember` y | n <- xs ]
-- adding a tag should create a new hidden workspace
prop_ensure_append (x :: T) l n =
not (n `tagMember` x)
==>
(hidden y /= hidden x -- doesn't append, renames
&&
and [ isNothing (stack z) && layout z == l | z <- hidden y, tag z == n ]
)
where
y = ensureTags l (n:ts) x
ts = [ tag z | z <- workspaces x ]
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)
------------------------------------------------------------------------
-- The Tall layout
-- 1 window should always be tiled fullscreen
{-
prop_tile_fullscreen rect = tile pct rect 1 1 == [rect]
where pct = 1/2
-- multiple windows
prop_tile_non_overlap rect windows nmaster = noOverlaps (tile pct rect nmaster windows)
where _ = rect :: Rectangle
pct = 3 % 100
pct = 3 % 100
-- splitting horizontally yields sensible results
prop_split_hoziontal (NonNegative n) x =
{-
trace (show (rect_x x
,rect_width x
,rect_x x + fromIntegral (rect_width x)
,map rect_x xs))
$
-}
sum (map rect_width xs) == rect_width x
&&
all (== rect_height x) (map rect_height xs)
&&
(map rect_x xs) == (sort $ map rect_x xs)
where
xs = splitHorizontally n x
-- splitting horizontally yields sensible results
prop_splitVertically (r :: Rational) x =
rect_x x == rect_x a && rect_x x == rect_x b
&&
rect_width x == rect_width a && rect_width x == rect_width b
{-
trace (show (rect_x x
,rect_width x
,rect_x x + fromIntegral (rect_width x)
,map rect_x xs))
$
-}
where
(a,b) = splitVerticallyBy r x
-- pureLayout works.
prop_purelayout_tall n r1 r2 rect (t :: T) =
isJust (peek t) ==>
length ts == length (index t)
&&
noOverlaps (map snd ts)
&&
description layoot == "Tall"
where layoot = Tall n r1 r2
st = fromJust . stack . workspace . current $ t
ts = pureLayout layoot rect st
-- Test message handling of Tall
-- what happens when we send a Shrink message to Tall
prop_shrink_tall (NonNegative n) (NonZero (NonNegative delta)) (NonNegative frac) =
n == n' && delta == delta' -- these state components are unchanged
&& frac' <= frac && (if frac' < frac then frac' == 0 || frac' == frac - delta
else frac == 0 )
-- remaining fraction should shrink
where
l1 = Tall n delta frac
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage Shrink)
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
-- what happens when we send a Shrink message to Tall
prop_expand_tall (NonNegative n)
(NonZero (NonNegative delta))
(NonNegative n1)
(NonZero (NonNegative d1)) =
n == n'
&& delta == delta' -- these state components are unchanged
&& frac' >= frac
&& (if frac' > frac
then frac' == 1 || frac' == frac + delta
else frac == 1 )
-- remaining fraction should shrink
where
frac = min 1 (n1 % d1)
l1 = Tall n delta frac
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage Expand)
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
-- what happens when we send an IncMaster message to Tall
prop_incmaster_tall (NonNegative n) (NonZero (NonNegative delta)) (NonNegative frac)
(NonNegative k) =
delta == delta' && frac == frac' && n' == n + k
where
l1 = Tall n delta frac
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage (IncMasterN k))
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
-- toMessage LT = SomeMessage Shrink
-- toMessage EQ = SomeMessage Expand
-- toMessage GT = SomeMessage (IncMasterN 1)
------------------------------------------------------------------------
-- Full layout
-- pureLayout works for Full
prop_purelayout_full rect (t :: T) =
isJust (peek t) ==>
length ts == 1 -- only one window to view
&&
snd (head ts) == rect -- and sets fullscreen
&&
fst (head ts) == fromJust (peek t) -- and the focused window is shown
where layoot = Full
st = fromJust . stack . workspace . current $ t
ts = pureLayout layoot rect st
-- what happens when we send an IncMaster message to Full --- Nothing
prop_sendmsg_full (NonNegative k) =
isNothing (Full `pureMessage` (SomeMessage (IncMasterN k)))
prop_desc_full = description Full == show Full
------------------------------------------------------------------------
prop_desc_mirror n r1 r2 = description (Mirror $! t) == "Mirror Tall"
where t = Tall n r1 r2
------------------------------------------------------------------------
noOverlaps [] = True
noOverlaps [_] = True
@@ -520,15 +825,36 @@ noOverlaps xs = and [ verts a `notOverlap` verts b
= (top1 < bottom2 || top2 < bottom1)
|| (right1 < left2 || right2 < left1)
-}
------------------------------------------------------------------------
-- Aspect ratios
prop_resize_inc (NonZero (NonNegative inc_w),NonZero (NonNegative inc_h)) b@(w,h) =
w' `mod` inc_w == 0 && h' `mod` inc_h == 0
where (w',h') = applyResizeIncHint a b
a = (inc_w,inc_h)
prop_resize_inc_extra ((NonNegative inc_w)) b@(w,h) =
(w,h) == (w',h')
where (w',h') = applyResizeIncHint a b
a = (-inc_w,0::Dimension)-- inc_h)
prop_resize_max (NonZero (NonNegative inc_w),NonZero (NonNegative inc_h)) b@(w,h) =
w' <= inc_w && h' <= inc_h
where (w',h') = applyMaxSizeHint a b
a = (inc_w,inc_h)
prop_resize_max_extra ((NonNegative inc_w)) b@(w,h) =
(w,h) == (w',h')
where (w',h') = applyMaxSizeHint a b
a = (-inc_w,0::Dimension)-- inc_h)
------------------------------------------------------------------------
main :: IO ()
main = do
args <- getArgs
args <- fmap (drop 1) getArgs
let n = if null args then 100 else read (head args)
(results, passed) <- liftM unzip $ mapM (\(s,a) -> printf "%-25s: " s >> a n) tests
(results, passed) <- liftM unzip $ mapM (\(s,a) -> printf "%-40s: " s >> a n) tests
printf "Passed %d tests!\n" (sum passed)
when (not . and $ results) $ fail "Not all tests passed!"
where
@@ -550,6 +876,7 @@ main = do
,("greedyView : invariant" , mytest prop_greedyView_I)
,("greedyView sets current" , mytest prop_greedyView_current)
,("greedyView is safe " , mytest prop_greedyView_current_id)
,("greedyView idempotent" , mytest prop_greedyView_idem)
,("greedyView reversible" , mytest prop_greedyView_reversible)
,("greedyView is local" , mytest prop_greedyView_local)
@@ -561,20 +888,29 @@ 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)
,("focusWindow identity", mytest prop_focusWindow_identity)
,("findIndex" , mytest prop_findIndex)
,("findTag" , mytest prop_findIndex)
,("allWindows/member" , mytest prop_allWindowsMember)
,("currentTag" , mytest prop_currentTag)
,("insert: invariant" , mytest prop_insertUp_I)
,("insert/new" , mytest prop_insert_empty)
@@ -609,13 +945,65 @@ main = do
,("swapUp is local" , mytest prop_swap_left_local)
,("swapDown is local" , mytest prop_swap_right_local)
,("shiftMaster id on focus", mytest prop_shift_master_focus)
,("shiftMaster is local", mytest prop_shift_master_local)
,("shiftMaster is idempotent", mytest prop_shift_master_idempotent)
,("shiftMaster preserves ordering", mytest prop_shift_master_ordering)
,("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)
,("floating sets geometry" , mytest prop_float_geometry)
,("floats can be deleted", mytest prop_float_delete)
,("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)
,("ensure hidden semantics", mytest prop_ensure_append)
,("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)
-- tall layout
{-
,("tile 1 window fullsize", mytest prop_tile_fullscreen)
,("tiles never overlap", mytest prop_tile_non_overlap)
-}
,("split hozizontally", mytest prop_split_hoziontal)
,("split verticalBy", mytest prop_splitVertically)
,("pure layout tall", mytest prop_purelayout_tall)
,("send shrink tall", mytest prop_shrink_tall)
,("send expand tall", mytest prop_expand_tall)
,("send incmaster tall", mytest prop_incmaster_tall)
-- full layout
,("pure layout full", mytest prop_purelayout_full)
,("send message full", mytest prop_sendmsg_full)
,("describe full", mytest prop_desc_full)
,("describe mirror", mytest prop_desc_mirror)
-- resize hints
,("window hints: inc", mytest prop_resize_inc)
,("window hints: inc all", mytest prop_resize_inc_extra)
,("window hints: max", mytest prop_resize_max)
,("window hints: max all ", mytest prop_resize_max_extra)
]
@@ -781,8 +1169,10 @@ instance Arbitrary EmptyStackSet where
arbitrary = do
(NonEmptyNubList ns) <- arbitrary
(NonEmptyNubList sds) <- arbitrary
l <- arbitrary
-- there cannot be more screens than workspaces:
return . EmptyStackSet . new ns $ take (min (length ns) (length sds)) sds
return . EmptyStackSet . new l ns $ take (min (length ns) (length sds)) sds
coarbitrary = error "coarbitrary EmptyStackSet"
-- | Generates a value that satisfies a predicate.
suchThat :: Gen a -> (a -> Bool) -> Gen a

10
tests/coverage.hs Normal file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env runhaskell
import System.Cmd
-- generate appropriate .hpc files
main = do
system $ "rm -rf *.tix"
system $ "dist/build/xmonad/xmonad --run-tests"
system $ "hpc markup xmonad --exclude=Main --exclude=Properties --exclude=XMonad --exclude=Paths_xmonad"
system $ "hpc report xmonad --exclude=Main --exclude=Properties --exclude=XMonad --exclude=Paths_xmonad"

View File

@@ -9,7 +9,6 @@ main = do foo <- getContents
-- uncomment the following to check for mistakes in isntcomment
-- putStr $ unlines $ actual_loc
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
@@ -42,6 +42,6 @@ replace :: Eq a => a -> a -> [a] -> [a]
replace x y = map (\a -> if a == x then y else a)
main = do
troffBindings <- (concatMap troff . allBindings) `liftM` readFile "./Config.hs"
troffBindings <- (concatMap troff . allBindings) `liftM` readFile "./XMonad/Config.hs"
let sed = unlines . replace "___KEYBINDINGS___" troffBindings . lines
readFile "./man/xmonad.1.in" >>= return . sed >>= writeFile "./man/xmonad.1"

View File

@@ -1,31 +1,77 @@
name: xmonad
version: 0.3
version: 0.8.1
homepage: http://xmonad.org
synopsis: A lightweight X11 window manager.
synopsis: A tiling window manager
description:
Xmonad is a minimalist tiling window manager for X, written in
Haskell. Windows are managed using automatic layout algorithms,
which can be dynamically reconfigured. At any time windows are
arranged so as to 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>=2.0, X11>=1.2.1, X11-extras>=0.3, 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
maintainer: xmonad@haskell.org
extra-source-files: README TODO CONFIG STYLE tests/loc.hs tests/Properties.hs
man/xmonad.1.in man/xmonad.1 man/xmonad.html man/xmonad.hs
util/GenerateManpage.hs
cabal-version: >= 1.2
build-type: Simple
executable: xmonad
main-is: Main.hs
other-modules: Config Operations StackSet XMonad
ghc-options: -funbox-strict-fields -O2 -fasm -Wall -optl-Wl,-s
ghc-prof-options: -prof -auto-all
extensions: GeneralizedNewtypeDeriving
-- Also requires deriving Typeable
flag small_base
description: Choose the new smaller, split-up base package.
flag testing
description: Testing mode, only build minimal components
default: False
library
exposed-modules: XMonad
XMonad.Main
XMonad.Core
XMonad.Config
XMonad.Layout
XMonad.ManageHook
XMonad.Operations
XMonad.StackSet
if flag(small_base)
build-depends: base < 4 && >=3, containers, directory, process
else
build-depends: base < 3
build-depends: X11>=1.4.3, mtl, unix
ghc-options: -funbox-strict-fields -Wall
ghc-prof-options: -prof -auto-all
extensions: CPP
if flag(testing)
buildable: False
executable xmonad
main-is: Main.hs
other-modules: XMonad
XMonad.Main
XMonad.Core
XMonad.Config
XMonad.Layout
XMonad.ManageHook
XMonad.Operations
XMonad.StackSet
ghc-options: -funbox-strict-fields -Wall
ghc-prof-options: -prof -auto-all
extensions: CPP
if flag(testing)
cpp-options: -DTESTING
hs-source-dirs: . tests/
build-depends: QuickCheck < 2
ghc-options: -Werror
if flag(testing) && flag(small_base)
build-depends: random