111 Commits
v0.8 ... v0.10

Author SHA1 Message Date
Adam Vogt
4be3b39cd2 Assume locale is utf8 for spawn.
This adds the utf8-string dependency so that users no longer need to encode
strings they (possibly indirectly) pass to spawn. This is the expected
behavior, since each Char in String should be an actual character.

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

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

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

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

With the change:

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

Can now be:
> appName ?= x <&&> classname ?= y --> doFloat <+> doIgnore
2009-07-10 01:13:08 +00:00
Adam Vogt
66e7715ea6 Pester the user with one (not two) xmessages on config errors 2009-03-21 23:37:36 +00:00
Wouter Swierstra
d9d3e40112 Minor bugfix in the creation of new StackSets. 2009-05-03 15:43:21 +00:00
Spencer Janssen
7385793c65 Avoid deadly cycle in man/xmonad.hs 2009-03-19 08:19:18 +00:00
wirtwolff
72885e7e24 X.Config.hs, ./man/xmonad.hs: update Event Hook doc 2009-02-09 18:38:37 +00:00
Spencer Janssen
a931776e54 Use records to document Tall's arguments 2009-02-21 23:06:28 +00:00
Joachim Breitner
61568318d6 Fix possible head []
This seems to be a rare case, but I just got hit by it.
2009-01-06 19:20:26 +00:00
Spencer Janssen
3caa989e20 ManageHook.doShift: use shiftWin instead of shift 2009-02-19 04:14:58 +00:00
Spencer Janssen
09fd11d13b Express shift in terms of shiftWin 2009-02-17 23:53:43 +00:00
Don Stewart
f33681de49 Use standard -fforce-recomp instead of undocumented -no-recomp 2009-02-08 16:55:18 +00:00
Daniel Schoepe
bf8bfc66a5 Support for custom event hooks 2009-02-03 15:55:36 +00:00
Daniel Schoepe
4075e2d9d3 Make X an instance of Typeable 2009-01-28 21:54:06 +00:00
Spencer Janssen
78856e1a6f Add uninstallSignalHandlers, use in spawn 2009-01-22 00:26:43 +00:00
Spencer Janssen
4222dd9ad3 Create a new session for forked processes 2009-01-22 00:04:23 +00:00
Spencer Janssen
34a547ce57 TAG 0.8.1 2009-01-18 08:39:10 +00:00
Spencer Janssen
353e7cd681 Close stdin in spawned processes 2009-01-17 04:00:24 +00:00
Spencer Janssen
72dece0769 Document spawnPID 2009-01-17 03:59:07 +00:00
Spencer Janssen
6e1c5e9b49 Asynchronously recompile/restart xmonad on mod-q 2009-01-17 03:53:00 +00:00
Spencer Janssen
bf8ba79090 Add --restart, a command line flag to cause a running xmonad process to restart 2009-01-17 03:49:59 +00:00
Spencer Janssen
5edfb1d262 Bump version to 0.8.1 2009-01-16 22:36:21 +00:00
Spencer Janssen
0fecae0abc Remove doubleFork, handle SIGCHLD
This is a rather big change.  Rather than make spawned processes become
children of init, we handle them in xmonad.  As a side effect of this change,
we never need to use waitForProcess in any contrib module -- in fact, doing so
will raise an exception.  The main benefit to handling SIGCHLD is that xmonad
can now be started with 'exec', and will correctly clean up after inherited
child processes.
2009-01-16 20:47:42 +00:00
gwern0
26f4f734f9 Main.hs: escape / in Haddocks
This lets haddocks for Main.hs, at least, to build with 2.3.0.
2008-12-07 02:09:15 +00:00
Daniel Schoepe
5e7df396b9 More flexible userCode function 2009-01-10 22:18:52 +00:00
Spencer Janssen
314ba78335 Call logHook as the very last action in windows 2008-12-09 23:37:00 +00:00
Spencer Janssen
7aa78ecc75 Accept inferior crossing events. This patch enables fmouse-focus-follows-screen 2008-12-05 04:51:30 +00:00
Spencer Janssen
ba8e26458e Tile all windows at once 2008-11-18 07:44:47 +00:00
Spencer Janssen
c627e8cc4d Factor rational rect scaling into a separate function 2008-11-18 07:28:49 +00:00
Spencer Janssen
04f894275d Change screen focus by clicking on the root window.
This is a modification of a patch from Joachim Breitner.
2008-11-06 22:40:31 +00:00
Spencer Janssen
edb752136f Fix #192. 2008-10-21 22:00:59 +00:00
Adam Vogt
2b463a632f select base < 4 for building on ghc 6.10 2008-10-13 21:45:09 +00:00
Joachim Breitner
ca122dd2cb add killWindow function
This is required to kill anything that is not focused, without
having to focus it first.
2008-10-05 00:18:04 +00:00
Devin Mullins
77657b65f9 add'l documentation 2008-09-27 23:46:39 +00:00
Spencer Janssen
28c57a837a Regression: ungrab buttons on *non* root windows 2008-10-07 21:43:51 +00:00
Spencer Janssen
afda20b56d Partial fix for #40
Improvements:
 - clicking on the root will change focus to that screen
 - moving the mouse from a window on a screen to an empty screen changes focus
   to that screen
The only remaining issue is that moving the mouse between two empty screens
does not change focus.  In order to solve this, we'd have to select motion events
on the root window, which is potentially expensive.
2008-10-07 21:20:53 +00:00
Spencer Janssen
0cc7b12fd0 Track mouse position via events received 2008-10-07 20:39:53 +00:00
Spencer Janssen
15a78ae715 Fix haddock 2008-10-07 09:46:41 +00:00
Spencer Janssen
18444799e0 Move screen locating code into pointScreen 2008-10-07 09:42:07 +00:00
Spencer Janssen
cc60fa73ad Make pointWithin a top-level binding 2008-10-07 09:02:29 +00:00
gwern0
8881e2ac78 sp README, CONFIG, STYLE, TODO 2008-09-13 02:44:57 +00:00
Spencer Janssen
533031e3d6 Use the same X11 dependency as xmonad-contrib 2008-09-21 06:15:08 +00:00
Spencer Janssen
76d4af15e4 Export focusUp' and focusDown' -- work entirely on stacks 2008-09-11 21:48:03 +00:00
Devin Mullins
74c6dd2721 add W.shiftMaster, fix float/tile-reordering bug 2008-09-11 05:39:09 +00:00
20 changed files with 780 additions and 327 deletions

2
CONFIG
View File

@@ -53,7 +53,7 @@ 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 then load this new file, and run it. If it is unable to, the defaults
are used. are used.
To load succesfully, both 'xmonad' and 'ghc' must be in your $PATH 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 environment variable. If GHC isn't in your path, for some reason, you
can compile the xmonad.hs file yourself: can compile the xmonad.hs file yourself:

39
Main.hs
View File

@@ -16,14 +16,17 @@ module Main (main) where
import XMonad import XMonad
import System.IO import Control.Monad (unless)
import System.Info import System.Info
import System.Environment import System.Environment
import System.Posix.Process (executeFile) import System.Posix.Process (executeFile)
import System.Exit (exitFailure)
import Paths_xmonad (version) import Paths_xmonad (version)
import Data.Version (showVersion) import Data.Version (showVersion)
import Graphics.X11.Xinerama (compiledWithXinerama)
#ifdef TESTING #ifdef TESTING
import qualified Properties import qualified Properties
#endif #endif
@@ -32,18 +35,27 @@ import qualified Properties
-- for xmonad, and if it doesn't find one, just launches the default. -- for xmonad, and if it doesn't find one, just launches the default.
main :: IO () main :: IO ()
main = do main = do
installSignalHandlers -- important to ignore SIGCHLD to avoid zombies
args <- getArgs args <- getArgs
let launch = catchIO buildLaunch >> xmonad defaultConfig let launch = catchIO buildLaunch >> xmonad defaultConfig
case args of case args of
[] -> launch [] -> launch
["--resume", _] -> launch ("--resume":_) -> launch
["--help"] -> usage ["--help"] -> usage
["--recompile"] -> recompile True >> return () ["--recompile"] -> recompile True >>= flip unless exitFailure
["--version"] -> putStrLn ("xmonad " ++ showVersion version) ["--replace"] -> launch
["--restart"] -> sendRestart >> return ()
["--version"] -> putStrLn $ unwords shortVersion
["--verbose-version"] -> putStrLn . unwords $ shortVersion ++ longVersion
#ifdef TESTING #ifdef TESTING
("--run-tests":_) -> Properties.main ("--run-tests":_) -> Properties.main
#endif #endif
_ -> fail "unrecognized flags" _ -> fail "unrecognized flags"
where
shortVersion = ["xmonad", showVersion version]
longVersion = [ "compiled by", compilerName, showVersion compilerVersion
, "for", arch ++ "-" ++ os
, "\nXinerama:", show compiledWithXinerama ]
usage :: IO () usage :: IO ()
usage = do usage = do
@@ -54,18 +66,20 @@ usage = do
" --help Print this message" : " --help Print this message" :
" --version Print the version number" : " --version Print the version number" :
" --recompile Recompile your ~/.xmonad/xmonad.hs" : " --recompile Recompile your ~/.xmonad/xmonad.hs" :
" --replace Replace the running window manager with xmonad" :
" --restart Request a running xmonad process to restart" :
#ifdef TESTING #ifdef TESTING
" --run-tests Run the test suite" : " --run-tests Run the test suite" :
#endif #endif
[] []
-- | Build "~/.xmonad/xmonad.hs" with ghc, then execute it. If there are no -- | 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 -- errors, this function does not return. An exception is raised in any of
-- these cases: -- these cases:
-- --
-- * ghc missing -- * ghc missing
-- --
-- * ~/.xmonad/xmonad.hs missing -- * both "~\/.xmonad\/xmonad.hs" and "~\/.xmonad\/xmonad-$arch-$os" missing
-- --
-- * xmonad.hs fails to compile -- * xmonad.hs fails to compile
-- --
@@ -73,7 +87,7 @@ usage = do
-- --
-- ** type error, syntax error, .. -- ** type error, syntax error, ..
-- --
-- * Missing xmonad/XMonadContrib modules due to ghc upgrade -- * Missing XMonad\/XMonadContrib modules due to ghc upgrade
-- --
buildLaunch :: IO () buildLaunch :: IO ()
buildLaunch = do buildLaunch = do
@@ -82,3 +96,14 @@ buildLaunch = do
args <- getArgs args <- getArgs
executeFile (dir ++ "/xmonad-"++arch++"-"++os) False args Nothing executeFile (dir ++ "/xmonad-"++arch++"-"++os) False args Nothing
return () return ()
sendRestart :: IO ()
sendRestart = do
dpy <- openDisplay ""
rw <- rootWindow dpy $ defaultScreen dpy
xmonad_restart <- internAtom dpy "XMONAD_RESTART" False
allocaXEvent $ \e -> do
setEventType e clientMessage
setClientMessageEvent e rw xmonad_restart 32 0 currentTime
sendEvent dpy rw False structureNotifyMask e
sync dpy False

4
README
View File

@@ -26,7 +26,7 @@ Building:
Building is quite straightforward, and requires a basic Haskell toolchain. Building is quite straightforward, and requires a basic Haskell toolchain.
On many systems xmonad is available as a binary package in your 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 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 in preference to a source build, as the dependency resolution will be
simpler. simpler.
@@ -128,7 +128,7 @@ XMonadContrib
Other useful programs: Other useful programs:
A nicer xterm replacment, that supports resizing better: A nicer xterm replacement, that supports resizing better:
urxvt http://software.schmorp.de/pkg/rxvt-unicode.html urxvt http://software.schmorp.de/pkg/rxvt-unicode.html

4
STYLE
View File

@@ -2,7 +2,7 @@
== Coding guidelines for contributing to == Coding guidelines for contributing to
== xmonad and the xmonad contributed extensions == xmonad and the xmonad contributed extensions
* Comment every top level function (particularly exported funtions), and * Comment every top level function (particularly exported functions), and
provide a type signature; use Haddock syntax in the comments. provide a type signature; use Haddock syntax in the comments.
* Follow the coding style of the other modules. * Follow the coding style of the other modules.
@@ -15,7 +15,7 @@
* Tabs are illegal. Use 4 spaces for indenting. * Tabs are illegal. Use 4 spaces for indenting.
* Any pure function added to the core should have QuickCheck properties * Any pure function added to the core should have QuickCheck properties
precisely defining its behaviour. precisely defining its behavior.
* New modules should identify the author, and be submitted under * New modules should identify the author, and be submitted under
the same license as xmonad (BSD3 license or freer). the same license as xmonad (BSD3 license or freer).

6
TODO
View File

@@ -1,7 +1,7 @@
- Write down invariants for the window life cycle, especially: - Write down invariants for the window life cycle, especially:
- When are borders set? Prove that the current handling is sufficient. - When are borders set? Prove that the current handling is sufficient.
- current floating layer handling is unoptimal. FocusUp should raise, - current floating layer handling is nonoptimal. FocusUp should raise,
for example for example
- Issues still with stacking order. - Issues still with stacking order.
@@ -15,7 +15,9 @@
* double check README build instructions * double check README build instructions
* test core with 6.6 and 6.8 * test core with 6.6 and 6.8
* bump xmonad.cabal version and X11 version * bump xmonad.cabal version and X11 version
* upload X11 and xmonad to hackage * upload X11 and xmonad to Hackage
* update links to hackage in download.html
* update #xmonad topic
* check examples/text in user-facing Config.hs * check examples/text in user-facing Config.hs
* check tour.html and intro.html are up to date, and mention all core bindings * check tour.html and intro.html are up to date, and mention all core bindings
* confirm template config is type correct * confirm template config is type correct

View File

@@ -25,20 +25,24 @@ module XMonad.Config (defaultConfig) where
-- Useful imports -- Useful imports
-- --
import XMonad.Core as XMonad hiding import XMonad.Core as XMonad hiding
(workspaces,manageHook,numlockMask,keys,logHook,startupHook,borderWidth,mouseBindings (workspaces,manageHook,keys,logHook,startupHook,borderWidth,mouseBindings
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse) ,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse
,handleEventHook)
import qualified XMonad.Core as XMonad import qualified XMonad.Core as XMonad
(workspaces,manageHook,numlockMask,keys,logHook,startupHook,borderWidth,mouseBindings (workspaces,manageHook,keys,logHook,startupHook,borderWidth,mouseBindings
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse) ,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse
,handleEventHook)
import XMonad.Layout import XMonad.Layout
import XMonad.Operations import XMonad.Operations
import XMonad.ManageHook import XMonad.ManageHook
import qualified XMonad.StackSet as W import qualified XMonad.StackSet as W
import Data.Bits ((.|.)) import Data.Bits ((.|.))
import Data.Monoid
import qualified Data.Map as M import qualified Data.Map as M
import System.Exit import System.Exit
import Graphics.X11.Xlib import Graphics.X11.Xlib
import Graphics.X11.Xlib.Extras
-- | The default number of workspaces (virtual screens) and their names. -- | The default number of workspaces (virtual screens) and their names.
-- By default we use numeric strings, but any string may be used as a -- By default we use numeric strings, but any string may be used as a
@@ -60,22 +64,6 @@ workspaces = map show [1 .. 9 :: Int]
defaultModMask :: KeyMask defaultModMask :: KeyMask
defaultModMask = mod1Mask 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. -- | Width of the window border in pixels.
-- --
borderWidth :: Dimension borderWidth :: Dimension
@@ -119,6 +107,15 @@ manageHook = composeAll
logHook :: X () logHook :: X ()
logHook = return () logHook = return ()
------------------------------------------------------------------------
-- Event handling
-- | Defines a custom handler function for X Events. The function should
-- return (All True) if the default handler is to be run afterwards.
-- To combine event hooks, use mappend or mconcat from Data.Monoid.
handleEventHook :: Event -> X All
handleEventHook _ = return (All True)
-- | Perform an arbitrary action at xmonad startup. -- | Perform an arbitrary action at xmonad startup.
startupHook :: X () startupHook :: X ()
startupHook = return () startupHook = return ()
@@ -168,7 +165,7 @@ keys :: XConfig Layout -> M.Map (KeyMask, KeySym) (X ())
keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
-- launching and killing programs -- launching and killing programs
[ ((modMask .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf) -- %! Launch terminal [ ((modMask .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf) -- %! Launch terminal
, ((modMask, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"") -- %! Launch dmenu , ((modMask, xK_p ), spawn "dmenu_run") -- %! Launch dmenu
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun") -- %! Launch gmrun , ((modMask .|. shiftMask, xK_p ), spawn "gmrun") -- %! Launch gmrun
, ((modMask .|. shiftMask, xK_c ), kill) -- %! Close the focused window , ((modMask .|. shiftMask, xK_c ), kill) -- %! Close the focused window
@@ -205,7 +202,7 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
-- quit, or restart -- quit, or restart
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit xmonad , ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit xmonad
, ((modMask , xK_q ), restart "xmonad" True) -- %! Restart xmonad , ((modMask , xK_q ), spawn "if type xmonad; then xmonad --recompile && xmonad --restart; else xmessage xmonad not in \\$PATH: \"$PATH\"; fi") -- %! Restart xmonad
] ]
++ ++
-- mod-[1..9] %! Switch to workspace N -- mod-[1..9] %! Switch to workspace N
@@ -223,15 +220,15 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
-- | Mouse bindings: default actions bound to mouse events -- | Mouse bindings: default actions bound to mouse events
-- --
mouseBindings :: XConfig Layout -> M.Map (KeyMask, Button) (Window -> X ()) mouseBindings :: XConfig Layout -> M.Map (KeyMask, Button) (Window -> X ())
mouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList $ mouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList
-- mod-button1 %! Set the window to floating mode and move by dragging -- mod-button1 %! Set the window to floating mode and move by dragging
[ ((modMask, button1), (\w -> focus w >> mouseMoveWindow w [ ((modMask, button1), \w -> focus w >> mouseMoveWindow w
>> windows W.swapMaster)) >> windows W.shiftMaster)
-- mod-button2 %! Raise the window to the top of the stack -- mod-button2 %! Raise the window to the top of the stack
, ((modMask, button2), (\w -> focus w >> windows W.swapMaster)) , ((modMask, button2), windows . (W.shiftMaster .) . W.focusWindow)
-- mod-button3 %! Set the window to floating mode and resize by dragging -- mod-button3 %! Set the window to floating mode and resize by dragging
, ((modMask, button3), (\w -> focus w >> mouseResizeWindow w , ((modMask, button3), \w -> focus w >> mouseResizeWindow w
>> windows W.swapMaster)) >> windows W.shiftMaster)
-- you may also bind events to the mouse scroll wheel (button4 and button5) -- you may also bind events to the mouse scroll wheel (button4 and button5)
] ]
@@ -243,11 +240,12 @@ defaultConfig = XConfig
, XMonad.terminal = terminal , XMonad.terminal = terminal
, XMonad.normalBorderColor = normalBorderColor , XMonad.normalBorderColor = normalBorderColor
, XMonad.focusedBorderColor = focusedBorderColor , XMonad.focusedBorderColor = focusedBorderColor
, XMonad.numlockMask = numlockMask
, XMonad.modMask = defaultModMask , XMonad.modMask = defaultModMask
, XMonad.keys = keys , XMonad.keys = keys
, XMonad.logHook = logHook , XMonad.logHook = logHook
, XMonad.startupHook = startupHook , XMonad.startupHook = startupHook
, XMonad.mouseBindings = mouseBindings , XMonad.mouseBindings = mouseBindings
, XMonad.manageHook = manageHook , XMonad.manageHook = manageHook
, XMonad.focusFollowsMouse = focusFollowsMouse } , XMonad.handleEventHook = handleEventHook
, XMonad.focusFollowsMouse = focusFollowsMouse
}

View File

@@ -1,7 +1,5 @@
{-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving, {-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving,
MultiParamTypeClasses, TypeSynonymInstances, CPP #-} MultiParamTypeClasses, TypeSynonymInstances, CPP, DeriveDataTypeable #-}
-- required for deriving Typeable
{-# OPTIONS_GHC -fglasgow-exts #-}
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- | -- |
@@ -24,28 +22,36 @@ module XMonad.Core (
XConf(..), XConfig(..), LayoutClass(..), XConf(..), XConfig(..), LayoutClass(..),
Layout(..), readsLayout, Typeable, Message, Layout(..), readsLayout, Typeable, Message,
SomeMessage(..), fromMessage, LayoutMessages(..), SomeMessage(..), fromMessage, LayoutMessages(..),
runX, catchX, userCode, io, catchIO, doubleFork, StateExtension(..), ExtensionClass(..),
runX, catchX, userCode, userCodeDef, io, catchIO, installSignalHandlers, uninstallSignalHandlers,
withDisplay, withWindowSet, isRoot, runOnWorkspaces, withDisplay, withWindowSet, isRoot, runOnWorkspaces,
getAtom, spawn, getXMonadDir, recompile, trace, whenJust, whenX, getAtom, spawn, spawnPID, xfork, getXMonadDir, recompile, trace, whenJust, whenX,
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, ManageHook, Query(..), runQuery atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, ManageHook, Query(..), runQuery
) where ) where
import XMonad.StackSet hiding (modify) import XMonad.StackSet hiding (modify)
import Prelude hiding ( catch ) import Prelude hiding ( catch )
import Control.Exception (catch, bracket, throw, Exception(ExitException)) import Codec.Binary.UTF8.String (encodeString)
import Control.Exception.Extensible (catch, fromException, try, bracket, throw, finally, SomeException(..))
import Control.Applicative import Control.Applicative
import Control.Monad.State import Control.Monad.State
import Control.Monad.Reader import Control.Monad.Reader
import System.FilePath
import System.IO import System.IO
import System.Info import System.Info
import System.Posix.Process (executeFile, forkProcess, getProcessStatus, createSession) import System.Posix.Process (executeFile, forkProcess, getAnyProcessStatus, createSession)
import System.Posix.Signals
import System.Posix.IO
import System.Posix.Types (ProcessID)
import System.Process import System.Process
import System.Directory import System.Directory
import System.Exit import System.Exit
import Graphics.X11.Xlib import Graphics.X11.Xlib
import Graphics.X11.Xlib.Extras (Event) import Graphics.X11.Xlib.Extras (Event)
import Data.Typeable import Data.Typeable
import Data.List ((\\))
import Data.Maybe (isJust,fromMaybe)
import Data.Monoid import Data.Monoid
import qualified Data.Map as M import qualified Data.Map as M
@@ -56,7 +62,14 @@ data XState = XState
{ windowset :: !WindowSet -- ^ workspace list { windowset :: !WindowSet -- ^ workspace list
, mapped :: !(S.Set Window) -- ^ the Set of mapped windows , mapped :: !(S.Set Window) -- ^ the Set of mapped windows
, waitingUnmap :: !(M.Map Window Int) -- ^ the number of expected UnmapEvents , waitingUnmap :: !(M.Map Window Int) -- ^ the number of expected UnmapEvents
, dragging :: !(Maybe (Position -> Position -> X (), X ())) } , dragging :: !(Maybe (Position -> Position -> X (), X ()))
, numberlockMask :: !KeyMask -- ^ The numlock modifier
, extensibleState :: !(M.Map String (Either String StateExtension))
-- ^ stores custom state information.
--
-- The module "XMonad.Utils.ExtensibleState" in xmonad-contrib
-- provides additional information and a simple interface for using this.
}
-- | XConf, the (read-only) window manager configuration. -- | XConf, the (read-only) window manager configuration.
data XConf = XConf data XConf = XConf
@@ -70,6 +83,9 @@ data XConf = XConf
, buttonActions :: !(M.Map (KeyMask, Button) (Window -> X ())) , buttonActions :: !(M.Map (KeyMask, Button) (Window -> X ()))
-- ^ a mapping of button presses to actions -- ^ a mapping of button presses to actions
, mouseFocused :: !Bool -- ^ was refocus caused by mouse action? , 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 -- todo, better name
@@ -79,8 +95,10 @@ data XConfig l = XConfig
, terminal :: !String -- ^ The preferred terminal application. Default: \"xterm\" , terminal :: !String -- ^ The preferred terminal application. Default: \"xterm\"
, layoutHook :: !(l Window) -- ^ The available layouts , layoutHook :: !(l Window) -- ^ The available layouts
, manageHook :: !ManageHook -- ^ The action to run when a new window is opened , manageHook :: !ManageHook -- ^ The action to run when a new window is opened
, handleEventHook :: !(Event -> X All) -- ^ Handle an X event, returns (All True) if the default handler
-- should also be run afterwards. mappend should be used for combining
-- event hooks in most cases.
, workspaces :: ![String] -- ^ The list of workspaces' names , workspaces :: ![String] -- ^ The list of workspaces' names
, numlockMask :: !KeyMask -- ^ The numlock modifier
, modMask :: !KeyMask -- ^ the mod modifier , modMask :: !KeyMask -- ^ the mod modifier
, keys :: !(XConfig Layout -> M.Map (ButtonMask,KeySym) (X ())) , keys :: !(XConfig Layout -> M.Map (ButtonMask,KeySym) (X ()))
-- ^ The key binding: a map from key presses and actions -- ^ The key binding: a map from key presses and actions
@@ -116,9 +134,7 @@ data ScreenDetail = SD { screenRect :: !Rectangle } deriving (Eq,Show, Read)
-- instantiated on 'XConf' and 'XState' automatically. -- instantiated on 'XConf' and 'XState' automatically.
-- --
newtype X a = X (ReaderT XConf (StateT XState IO) a) newtype X a = X (ReaderT XConf (StateT XState IO) a)
#ifndef __HADDOCK__ deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf, Typeable)
deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf)
#endif
instance Applicative X where instance Applicative X where
pure = return pure = return
@@ -130,9 +146,7 @@ instance (Monoid a) => Monoid (X a) where
type ManageHook = Query (Endo WindowSet) type ManageHook = Query (Endo WindowSet)
newtype Query a = Query (ReaderT Window X a) newtype Query a = Query (ReaderT Window X a)
#ifndef __HADDOCK__
deriving (Functor, Monad, MonadReader Window, MonadIO) deriving (Functor, Monad, MonadReader Window, MonadIO)
#endif
runQuery :: Query a -> Window -> X a runQuery :: Query a -> Window -> X a
runQuery (Query m) w = runReaderT m w runQuery (Query m) w = runReaderT m w
@@ -152,16 +166,21 @@ catchX :: X a -> X a -> X a
catchX job errcase = do catchX job errcase = do
st <- get st <- get
c <- ask c <- ask
(a, s') <- io $ runX c st job `catch` \e -> case e of (a, s') <- io $ runX c st job `catch` \e -> case fromException e of
ExitException {} -> throw e Just x -> throw e `const` (x `asTypeOf` ExitSuccess)
_ -> do hPrint stderr e; runX c st errcase _ -> do hPrint stderr e; runX c st errcase
put s' put s'
return a return a
-- | Execute the argument, catching all exceptions. Either this function or -- | Execute the argument, catching all exceptions. Either this function or
-- 'catchX' should be used at all callsites of user customized code. -- 'catchX' should be used at all callsites of user customized code.
userCode :: X () -> X () userCode :: X a -> X (Maybe a)
userCode a = catchX (a >> return ()) (return ()) 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 -- Convenient wrappers to state
@@ -325,6 +344,33 @@ data LayoutMessages = Hide -- ^ sent when a layout becomes non-visi
instance Message LayoutMessages instance Message LayoutMessages
-- ---------------------------------------------------------------------
-- Extensible state
--
-- | Every module must make the data it wants to store
-- an instance of this class.
--
-- Minimal complete definition: initialValue
class Typeable a => ExtensionClass a where
-- | Defines an initial value for the state extension
initialValue :: a
-- | Specifies whether the state extension should be
-- persistent. Setting this method to 'PersistentExtension'
-- will make the stored data survive restarts, but
-- requires a to be an instance of Read and Show.
--
-- It defaults to 'StateExtension', i.e. no persistence.
extensionType :: a -> StateExtension
extensionType = StateExtension
-- | Existential type to store a state extension.
data StateExtension =
forall a. ExtensionClass a => StateExtension a
-- ^ Non-persistent state extension
| forall a. (Read a, Show a, ExtensionClass a) => PersistentExtension a
-- ^ Persistent extension
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- | General utilities -- | General utilities
-- --
@@ -335,21 +381,30 @@ io = liftIO
-- | Lift an 'IO' action into the 'X' monad. If the action results in an 'IO' -- | 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. -- exception, log the exception to stderr and continue normal execution.
catchIO :: MonadIO m => IO () -> m () catchIO :: MonadIO m => IO () -> m ()
catchIO f = io (f `catch` \e -> hPrint stderr e >> hFlush stderr) catchIO f = io (f `catch` \(SomeException e) -> hPrint stderr e >> hFlush stderr)
-- | spawn. Launch an external application -- | spawn. Launch an external application. Specifically, it double-forks and
-- runs the 'String' you pass as a command to \/bin\/sh.
--
-- Note this function assumes your locale uses utf8.
spawn :: MonadIO m => String -> m () spawn :: MonadIO m => String -> m ()
spawn x = doubleFork $ executeFile "/bin/sh" False ["-c", x] Nothing spawn x = spawnPID x >> return ()
-- | Double fork and execute an 'IO' action (usually one of the exec family of -- | Like 'spawn', but returns the 'ProcessID' of the launched application
-- functions) spawnPID :: MonadIO m => String -> m ProcessID
doubleFork :: MonadIO m => IO () -> m () spawnPID x = xfork $ executeFile "/bin/sh" False ["-c", encodeString x] Nothing
doubleFork m = io $ do
pid <- forkProcess $ do -- | A replacement for 'forkProcess' which resets default signal handlers.
forkProcess (createSession >> m) xfork :: MonadIO m => IO () -> m ProcessID
exitWith ExitSuccess xfork x = io . forkProcess . finally nullStdin $ do
getProcessStatus True False pid uninstallSignalHandlers
return () createSession
x
where
nullStdin = do
fd <- openFd "/dev/null" ReadOnly Nothing defaultFileFlags
dupTo fd stdInput
closeFd fd
-- | This is basically a map function, running a function in the 'X' monad on -- | 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. -- each workspace with the output of that function being the modified workspace.
@@ -372,9 +427,11 @@ getXMonadDir = io $ getAppUserDataDirectory "xmonad"
-- --
-- * the xmonad executable does not exist -- * the xmonad executable does not exist
-- --
-- * the xmonad executable is older than xmonad.hs -- * the xmonad executable is older than xmonad.hs or any file in
-- ~\/.xmonad\/lib
-- --
-- The -i flag is used to restrict recompilation to the xmonad.hs file only. -- The -i flag is used to restrict recompilation to the xmonad.hs file only,
-- and any files in the ~\/.xmonad\/lib directory.
-- --
-- Compilation errors (if any) are logged to ~\/.xmonad\/xmonad.errors. If -- Compilation errors (if any) are logged to ~\/.xmonad\/xmonad.errors. If
-- GHC indicates failure with a non-zero exit code, an xmessage displaying -- GHC indicates failure with a non-zero exit code, an xmessage displaying
@@ -386,31 +443,46 @@ recompile :: MonadIO m => Bool -> m Bool
recompile force = io $ do recompile force = io $ do
dir <- getXMonadDir dir <- getXMonadDir
let binn = "xmonad-"++arch++"-"++os let binn = "xmonad-"++arch++"-"++os
bin = dir ++ "/" ++ binn bin = dir </> binn
base = dir ++ "/" ++ "xmonad" base = dir </> "xmonad"
err = base ++ ".errors" err = base ++ ".errors"
src = base ++ ".hs" src = base ++ ".hs"
lib = dir </> "lib"
libTs <- mapM getModTime . Prelude.filter isSource =<< allFiles lib
srcT <- getModTime src srcT <- getModTime src
binT <- getModTime bin binT <- getModTime bin
if (force || srcT > binT) if force || any (binT <) (srcT : libTs)
then do then do
status <- bracket (openFile err WriteMode) hClose $ \h -> do -- temporarily disable SIGCHLD ignoring:
waitForProcess =<< runProcess "ghc" ["--make", "xmonad.hs", "-i", "-no-recomp", "-v0", "-o",binn] (Just dir) uninstallSignalHandlers
status <- bracket (openFile err WriteMode) hClose $ \h ->
waitForProcess =<< runProcess "ghc" ["--make", "xmonad.hs", "-i", "-ilib", "-fforce-recomp", "-v0", "-o",binn] (Just dir)
Nothing Nothing Nothing (Just h) Nothing Nothing Nothing (Just h)
-- re-enable SIGCHLD:
installSignalHandlers
-- now, if it fails, run xmessage to let the user know: -- now, if it fails, run xmessage to let the user know:
when (status /= ExitSuccess) $ do when (status /= ExitSuccess) $ do
ghcErr <- readFile err ghcErr <- readFile err
let msg = unlines $ let msg = unlines $
["Error detected while loading xmonad configuration file: " ++ src] ["Error detected while loading xmonad configuration file: " ++ src]
++ lines ghcErr ++ ["","Please check the file for errors."] ++ lines (if null ghcErr then show status else ghcErr)
++ ["","Please check the file for errors."]
-- nb, the ordering of printing, then forking, is crucial due to -- nb, the ordering of printing, then forking, is crucial due to
-- lazy evaluation -- lazy evaluation
hPutStrLn stderr msg hPutStrLn stderr msg
doubleFork $ executeFile "xmessage" True ["-default", "okay", msg] Nothing forkProcess $ executeFile "xmessage" True ["-default", "okay", msg] Nothing
return ()
return (status == ExitSuccess) return (status == ExitSuccess)
else return True else return True
where getModTime f = catch (Just <$> getModificationTime f) (const $ return Nothing) where getModTime f = catch (Just <$> getModificationTime f) (\(SomeException _) -> return Nothing)
isSource = flip elem [".hs",".lhs",".hsc"]
allFiles t = do
let prep = map (t</>) . Prelude.filter (`notElem` [".",".."])
cs <- prep <$> catch (getDirectoryContents t) (\(SomeException _) -> return [])
ds <- filterM doesDirectoryExist cs
concat . ((cs \\ ds):) <$> mapM allFiles ds
-- | Conditionally run an action, using a @Maybe a@ to decide. -- | Conditionally run an action, using a @Maybe a@ to decide.
whenJust :: Monad m => Maybe a -> (a -> m ()) -> m () whenJust :: Monad m => Maybe a -> (a -> m ()) -> m ()
@@ -424,3 +496,21 @@ whenX a f = a >>= \b -> when b f
-- be found in your .xsession-errors file -- be found in your .xsession-errors file
trace :: MonadIO m => String -> m () trace :: MonadIO m => String -> m ()
trace = io . hPutStrLn stderr trace = io . hPutStrLn stderr
-- | Ignore SIGPIPE to avoid termination when a pipe is full, and SIGCHLD to
-- avoid zombie processes, and clean up any extant zombie processes.
installSignalHandlers :: MonadIO m => m ()
installSignalHandlers = io $ do
installHandler openEndedPipe Ignore Nothing
installHandler sigCHLD Ignore Nothing
(try :: IO a -> IO (Either SomeException a))
$ fix $ \more -> do
x <- getAnyProcessStatus False False
when (isJust x) more
return ()
uninstallSignalHandlers :: MonadIO m => m ()
uninstallSignalHandlers = io $ do
installHandler openEndedPipe Default Nothing
installHandler sigCHLD Default Nothing
return ()

View File

@@ -1,5 +1,4 @@
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances, DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
-- -------------------------------------------------------------------------- -- --------------------------------------------------------------------------
-- | -- |
@@ -51,7 +50,11 @@ instance LayoutClass Full a
-- | The builtin tiling mode of xmonad. Supports 'Shrink', 'Expand' and -- | The builtin tiling mode of xmonad. Supports 'Shrink', 'Expand' and
-- 'IncMasterN'. -- 'IncMasterN'.
data Tall a = Tall !Int !Rational !Rational deriving (Show, Read) data Tall a = Tall { tallNMaster :: !Int -- ^ The default number of windows in the master pane (default: 1)
, tallRatioIncrement :: !Rational -- ^ Percent of screen to increment by when resizing panes (default: 3/100)
, tallRatio :: !Rational -- ^ Default proportion of screen occupied by master pane (default: 1/2)
}
deriving (Show, Read)
-- TODO should be capped [0..1] .. -- TODO should be capped [0..1] ..
-- a nice pure layout, lots of properties for the layout, and its messages, in Properties.hs -- a nice pure layout, lots of properties for the layout, and its messages, in Properties.hs
@@ -122,7 +125,7 @@ instance LayoutClass l a => LayoutClass (Mirror l) a where
-- | Mirror a rectangle. -- | Mirror a rectangle.
mirrorRect :: Rectangle -> Rectangle mirrorRect :: Rectangle -> Rectangle
mirrorRect (Rectangle rx ry rw rh) = (Rectangle ry rx rh rw) mirrorRect (Rectangle rx ry rw rh) = Rectangle ry rx rh rw
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- LayoutClass selection manager -- LayoutClass selection manager
@@ -170,7 +173,7 @@ choose (Choose d l r) d' ml mr = f lr
instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
runLayout (W.Workspace i (Choose L l r) ms) = runLayout (W.Workspace i (Choose L l r) ms) =
fmap (second . fmap $ flip (Choose L) $ r) . runLayout (W.Workspace i l ms) fmap (second . fmap $ flip (Choose L) r) . runLayout (W.Workspace i l ms)
runLayout (W.Workspace i (Choose R l r) ms) = runLayout (W.Workspace i (Choose R l r) ms) =
fmap (second . fmap $ Choose R l) . runLayout (W.Workspace i r ms) fmap (second . fmap $ Choose R l) . runLayout (W.Workspace i r ms)
@@ -191,7 +194,7 @@ instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
R -> choose c R Nothing =<< handle r NextNoWrap R -> choose c R Nothing =<< handle r NextNoWrap
handleMessage c@(Choose _ l _) m | Just FirstLayout <- fromMessage m = do handleMessage c@(Choose _ l _) m | Just FirstLayout <- fromMessage m =
flip (choose c L) Nothing =<< handle l FirstLayout flip (choose c L) Nothing =<< handle l FirstLayout
handleMessage c@(Choose d l r) m | Just ReleaseResources <- fromMessage m = handleMessage c@(Choose d l r) m | Just ReleaseResources <- fromMessage m =

View File

@@ -15,19 +15,21 @@
module XMonad.Main (xmonad) where module XMonad.Main (xmonad) where
import Control.Arrow (second)
import Data.Bits import Data.Bits
import Data.List ((\\)) import Data.List ((\\))
import Data.Function
import qualified Data.Map as M import qualified Data.Map as M
import qualified Data.Set as S import qualified Data.Set as S
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.State import Control.Monad.State
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Monoid (getAll)
import Foreign.C import Foreign.C
import Foreign.Ptr import Foreign.Ptr
import System.Environment (getArgs) import System.Environment (getArgs)
import System.Posix.Signals
import Graphics.X11.Xlib hiding (refreshKeyboardMapping) import Graphics.X11.Xlib hiding (refreshKeyboardMapping)
import Graphics.X11.Xlib.Extras import Graphics.X11.Xlib.Extras
@@ -57,14 +59,31 @@ xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
xmonad initxmc = do xmonad initxmc = do
-- setup locale information from environment -- setup locale information from environment
withCString "" $ c_setlocale (#const LC_ALL) withCString "" $ c_setlocale (#const LC_ALL)
-- ignore SIGPIPE -- ignore SIGPIPE and SIGCHLD
installHandler openEndedPipe Ignore Nothing installSignalHandlers
-- First, wrap the layout in an existential, to keep things pretty: -- First, wrap the layout in an existential, to keep things pretty:
let xmc = initxmc { layoutHook = Layout $ layoutHook initxmc } let xmc = initxmc { layoutHook = Layout $ layoutHook initxmc }
dpy <- openDisplay "" dpy <- openDisplay ""
let dflt = defaultScreen dpy let dflt = defaultScreen dpy
rootw <- rootWindow dpy dflt rootw <- rootWindow dpy dflt
args <- getArgs
when ("--replace" `elem` args) $ replace dpy dflt rootw
-- 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 xinesc <- getCleanedScreenInfo dpy
nbc <- do v <- initColor dpy $ normalBorderColor xmc nbc <- do v <- initColor dpy $ normalBorderColor xmc
~(Just nbc_) <- initColor dpy $ normalBorderColor Default.defaultConfig ~(Just nbc_) <- initColor dpy $ normalBorderColor Default.defaultConfig
@@ -75,12 +94,10 @@ xmonad initxmc = do
return (fromMaybe fbc_ v) return (fromMaybe fbc_ v)
hSetBuffering stdout NoBuffering hSetBuffering stdout NoBuffering
args <- getArgs
let layout = layoutHook xmc let layout = layoutHook xmc
lreads = readsLayout layout lreads = readsLayout layout
initialWinset = new layout (workspaces xmc) $ map SD xinesc initialWinset = new layout (workspaces xmc) $ map SD xinesc
maybeRead reads' s = case reads' s of maybeRead reads' s = case reads' s of
[(x, "")] -> Just x [(x, "")] -> Just x
_ -> Nothing _ -> Nothing
@@ -90,6 +107,10 @@ xmonad initxmc = do
ws <- maybeRead reads s ws <- maybeRead reads s
return . W.ensureTags layout (workspaces xmc) return . W.ensureTags layout (workspaces xmc)
$ W.mapLayout (fromMaybe layout . maybeRead lreads) ws $ W.mapLayout (fromMaybe layout . maybeRead lreads) ws
extState = fromMaybe M.empty $ do
("--resume" : _ : dyns : _) <- return args
vals <- maybeRead reads dyns
return . M.fromList . map (second Left) $ vals
cf = XConf cf = XConf
{ display = dpy { display = dpy
@@ -99,23 +120,21 @@ xmonad initxmc = do
, focusedBorder = fbc , focusedBorder = fbc
, keyActions = keys xmc xmc , keyActions = keys xmc xmc
, buttonActions = mouseBindings xmc xmc , buttonActions = mouseBindings xmc xmc
, mouseFocused = False } , mouseFocused = False
, mousePosition = Nothing }
st = XState st = XState
{ windowset = initialWinset { windowset = initialWinset
, numberlockMask = 0
, mapped = S.empty , mapped = S.empty
, waitingUnmap = M.empty , waitingUnmap = M.empty
, dragging = Nothing } , dragging = Nothing
, extensibleState = extState
xSetErrorHandler -- in C, I'm too lazy to write the binding: dons }
-- setup initial X environment
sync dpy False
selectInput dpy rootw $ substructureRedirectMask .|. substructureNotifyMask
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
allocaXEvent $ \e -> allocaXEvent $ \e ->
runX cf st $ do runX cf st $ do
setNumlockMask
grabKeys grabKeys
grabButtons grabButtons
@@ -136,12 +155,26 @@ xmonad initxmc = do
userCode $ startupHook initxmc userCode $ startupHook initxmc
-- main loop, for all you HOF/recursion fans out there. -- main loop, for all you HOF/recursion fans out there.
forever_ $ handle =<< io (nextEvent dpy e >> getEvent e) forever $ prehandle =<< io (nextEvent dpy e >> getEvent e)
return () return ()
where forever_ a = a >> forever_ a where
-- if the event gives us the position of the pointer, set mousePosition
prehandle e = let mouse = do guard (ev_event_type e `elem` evs)
return (fromIntegral (ev_x_root e)
,fromIntegral (ev_y_root e))
in local (\c -> c { mousePosition = mouse }) (handleWithHook e)
evs = [ keyPress, keyRelease, enterNotify, leaveNotify
, buttonPress, buttonRelease]
-- | Runs handleEventHook from the configuration and runs the default handler
-- function if it returned True.
handleWithHook :: Event -> X ()
handleWithHook e = do
evHook <- asks (handleEventHook . config)
whenX (userCodeDef True $ getAll `fmap` evHook e) (handle e)
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- | Event handler. Map X events onto calls into Operations.hs, which -- | Event handler. Map X events onto calls into Operations.hs, which
-- modify our internal model of the window manager state. -- modify our internal model of the window manager state.
@@ -160,7 +193,7 @@ handle (KeyEvent {ev_event_type = t, ev_state = m, ev_keycode = code})
s <- io $ keycodeToKeysym dpy code 0 s <- io $ keycodeToKeysym dpy code 0
mClean <- cleanMask m mClean <- cleanMask m
ks <- asks keyActions ks <- asks keyActions
userCode $ whenJust (M.lookup (mClean, s) ks) id userCodeDef () $ whenJust (M.lookup (mClean, s) ks) id
-- manage a new window -- manage a new window
handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
@@ -189,7 +222,9 @@ handle (UnmapEvent {ev_window = w, ev_send_event = synthetic}) = whenX (isClient
-- set keyboard mapping -- set keyboard mapping
handle e@(MappingNotifyEvent {}) = do handle e@(MappingNotifyEvent {}) = do
io $ refreshKeyboardMapping e io $ refreshKeyboardMapping e
when (ev_request e == mappingKeyboard) grabKeys when (ev_request e `elem` [mappingKeyboard, mappingModifier]) $ do
setNumlockMask
grabKeys
-- handle button release, which may finish dragging. -- handle button release, which may finish dragging.
handle e@(ButtonEvent {ev_event_type = t}) handle e@(ButtonEvent {ev_event_type = t})
@@ -214,16 +249,16 @@ handle e@(ButtonEvent {ev_window = w,ev_event_type = t,ev_button = b })
-- grabbed in grabButtons. Otherwise, it's click-to-focus. -- grabbed in grabButtons. Otherwise, it's click-to-focus.
isr <- isRoot w isr <- isRoot w
m <- cleanMask $ ev_state e m <- cleanMask $ ev_state e
ba <- asks buttonActions mact <- asks (M.lookup (m, b) . buttonActions)
if isr then userCode $ whenJust (M.lookup (m, b) ba) ($ ev_subwindow e) case mact of
else focus w (Just act) | isr -> act $ ev_subwindow e
_ -> focus w
broadcastMessage e -- Always send button events. broadcastMessage e -- Always send button events.
-- entered a normal window: focus it if focusFollowsMouse is set to -- entered a normal window: focus it if focusFollowsMouse is set to
-- True in the user's config. -- True in the user's config.
handle e@(CrossingEvent {ev_window = w, ev_event_type = t}) handle e@(CrossingEvent {ev_window = w, ev_event_type = t})
| t == enterNotify && ev_mode e == notifyNormal | t == enterNotify && ev_mode e == notifyNormal
&& ev_detail e /= notifyInferior
= whenX (asks $ focusFollowsMouse . config) (focus w) = whenX (asks $ focusFollowsMouse . config) (focus w)
-- left a window, check if we need to focus root -- left a window, check if we need to focus root
@@ -262,8 +297,15 @@ handle e@(ConfigureRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
handle (ConfigureEvent {ev_window = w}) = whenX (isRoot w) rescreen handle (ConfigureEvent {ev_window = w}) = whenX (isRoot w) rescreen
-- property notify -- property notify
handle PropertyEvent { ev_event_type = t, ev_atom = a } handle event@(PropertyEvent { ev_event_type = t, ev_atom = a })
| t == propertyNotify && a == wM_NAME = userCode =<< asks (logHook . config) | t == propertyNotify && a == wM_NAME = asks (logHook . config) >>= userCodeDef () >>
broadcastMessage event
handle e@ClientMessageEvent { ev_message_type = mt } = do
a <- getAtom "XMONAD_RESTART"
if (mt == a)
then restart "xmonad" True
else broadcastMessage e
handle e = broadcastMessage e -- trace (eventName e) -- ignoring handle e = broadcastMessage e -- trace (eventName e) -- ignoring
@@ -289,6 +331,18 @@ scan dpy rootw = do
return $ not (wa_override_redirect wa) return $ not (wa_override_redirect wa)
&& (wa_map_state wa == waIsViewable || ic) && (wa_map_state wa == waIsViewable || ic)
setNumlockMask :: X ()
setNumlockMask = do
dpy <- asks display
ms <- io $ getModifierMapping dpy
xs <- sequence [ do
ks <- io $ keycodeToKeysym dpy kc 0
if ks == xK_Num_Lock
then return (setBit 0 (fromIntegral m))
else return (0 :: KeyMask)
| (m, kcs) <- ms, kc <- kcs, kc /= 0]
modify (\s -> s { numberlockMask = foldr (.|.) 0 xs })
-- | Grab the keys back -- | Grab the keys back
grabKeys :: X () grabKeys :: X ()
grabKeys = do grabKeys = do
@@ -300,7 +354,7 @@ grabKeys = do
kc <- io $ keysymToKeycode dpy sym kc <- io $ keysymToKeycode dpy sym
-- "If the specified KeySym is not defined for any KeyCode, -- "If the specified KeySym is not defined for any KeyCode,
-- XKeysymToKeycode() returns zero." -- XKeysymToKeycode() returns zero."
when (kc /= '\0') $ mapM_ (grab kc . (mask .|.)) =<< extraModifiers when (kc /= 0) $ mapM_ (grab kc . (mask .|.)) =<< extraModifiers
-- | XXX comment me -- | XXX comment me
grabButtons :: X () grabButtons :: X ()
@@ -312,3 +366,36 @@ grabButtons = do
ems <- extraModifiers ems <- extraModifiers
ba <- asks buttonActions ba <- asks buttonActions
mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys $ ba) mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys $ ba)
-- | @replace@ to signals compliant window managers to exit.
replace :: Display -> ScreenNumber -> Window -> IO ()
replace dpy dflt rootw = do
-- check for other WM
wmSnAtom <- internAtom dpy ("WM_S" ++ show dflt) False
currentWmSnOwner <- xGetSelectionOwner dpy wmSnAtom
when (currentWmSnOwner /= 0) $ do
-- prepare to receive destroyNotify for old WM
selectInput dpy currentWmSnOwner structureNotifyMask
-- create off-screen window
netWmSnOwner <- allocaSetWindowAttributes $ \attributes -> do
set_override_redirect attributes True
set_event_mask attributes propertyChangeMask
let screen = defaultScreenOfDisplay dpy
visual = defaultVisualOfScreen screen
attrmask = cWOverrideRedirect .|. cWEventMask
createWindow dpy rootw (-100) (-100) 1 1 0 copyFromParent copyFromParent visual attrmask attributes
-- try to acquire wmSnAtom, this should signal the old WM to terminate
xSetSelectionOwner dpy wmSnAtom netWmSnOwner currentTime
-- SKIPPED: check if we acquired the selection
-- SKIPPED: send client message indicating that we are now the WM
-- wait for old WM to go away
fix $ \again -> do
evt <- allocaXEvent $ \event -> do
windowEvent dpy currentWmSnOwner structureNotifyMask event
get_EventType event
when (evt /= destroyNotify) again

View File

@@ -22,7 +22,7 @@ import Prelude hiding (catch)
import XMonad.Core import XMonad.Core
import Graphics.X11.Xlib.Extras import Graphics.X11.Xlib.Extras
import Graphics.X11.Xlib (Display, Window, internAtom, wM_NAME) import Graphics.X11.Xlib (Display, Window, internAtom, wM_NAME)
import Control.Exception (bracket, catch) import Control.Exception.Extensible (bracket, catch, SomeException(..))
import Control.Monad.Reader import Control.Monad.Reader
import Data.Maybe import Data.Maybe
import Data.Monoid import Data.Monoid
@@ -34,20 +34,24 @@ liftX :: X a -> Query a
liftX = Query . lift liftX = Query . lift
-- | The identity hook that returns the WindowSet unchanged. -- | The identity hook that returns the WindowSet unchanged.
idHook :: ManageHook idHook :: Monoid m => m
idHook = doF id idHook = mempty
-- | Compose two 'ManageHook's. -- | Infix 'mappend'. Compose two 'ManageHook' from right to left.
(<+>) :: ManageHook -> ManageHook -> ManageHook (<+>) :: Monoid m => m -> m -> m
(<+>) = mappend (<+>) = mappend
-- | Compose the list of 'ManageHook's. -- | Compose the list of 'ManageHook's.
composeAll :: [ManageHook] -> ManageHook composeAll :: Monoid m => [m] -> m
composeAll = mconcat composeAll = mconcat
infix 0 -->
-- | @p --> x@. If @p@ returns 'True', execute the 'ManageHook'. -- | @p --> x@. If @p@ returns 'True', execute the 'ManageHook'.
(-->) :: Query Bool -> ManageHook -> ManageHook --
p --> f = p >>= \b -> if b then f else mempty -- > (-->) :: Monoid m => Query Bool -> Query m -> Query m -- a simpler type
(-->) :: (Monad m, Monoid a) => m Bool -> m a -> m a
p --> f = p >>= \b -> if b then f else return mempty
-- | @q =? x@. if the result of @q@ equals @x@, return 'True'. -- | @q =? x@. if the result of @q@ equals @x@, return 'True'.
(=?) :: Eq a => Query a -> a -> Query Bool (=?) :: Eq a => Query a -> a -> Query Bool
@@ -70,9 +74,10 @@ title = ask >>= \w -> liftX $ do
let let
getProp = getProp =
(internAtom d "_NET_WM_NAME" False >>= getTextProperty d w) (internAtom d "_NET_WM_NAME" False >>= getTextProperty d w)
`catch` \_ -> getTextProperty d w wM_NAME `catch` \(SomeException _) -> getTextProperty d w wM_NAME
extract = fmap head . wcTextPropertyToTextList d extract prop = do l <- wcTextPropertyToTextList d prop
io $ bracket getProp (xFree . tp_value) extract `catch` \_ -> return "" return $ if null l then "" else head l
io $ bracket getProp (xFree . tp_value) extract `catch` \(SomeException _) -> return ""
-- | Return the application name. -- | Return the application name.
appName :: Query String appName :: Query String
@@ -98,7 +103,7 @@ getStringProperty d w p = do
return $ fmap (map (toEnum . fromIntegral)) md return $ fmap (map (toEnum . fromIntegral)) md
-- | Modify the 'WindowSet' with a pure function. -- | Modify the 'WindowSet' with a pure function.
doF :: (WindowSet -> WindowSet) -> ManageHook doF :: (s -> s) -> Query (Endo s)
doF = return . Endo doF = return . Endo
-- | Move the window to the floating layer. -- | Move the window to the floating layer.
@@ -111,4 +116,4 @@ doIgnore = ask >>= \w -> liftX (reveal w) >> doF (W.delete w)
-- | Move the window to a given workspace -- | Move the window to a given workspace
doShift :: WorkspaceId -> ManageHook doShift :: WorkspaceId -> ManageHook
doShift = doF . W.shift doShift i = doF . W.shiftWin i =<< ask

View File

@@ -1,5 +1,4 @@
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
-- -------------------------------------------------------------------------- -- --------------------------------------------------------------------------
@@ -23,7 +22,7 @@ import XMonad.Layout (Full(..))
import qualified XMonad.StackSet as W import qualified XMonad.StackSet as W
import Data.Maybe import Data.Maybe
import Data.Monoid (appEndo) import Data.Monoid (Endo(..))
import Data.List (nub, (\\), find) import Data.List (nub, (\\), find)
import Data.Bits ((.|.), (.&.), complement) import Data.Bits ((.|.), (.&.), complement)
import Data.Ratio import Data.Ratio
@@ -33,9 +32,8 @@ import qualified Data.Set as S
import Control.Applicative import Control.Applicative
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.State import Control.Monad.State
import qualified Control.Exception as C import qualified Control.Exception.Extensible as C
import System.IO
import System.Posix.Process (executeFile) import System.Posix.Process (executeFile)
import Graphics.X11.Xlib import Graphics.X11.Xlib
import Graphics.X11.Xinerama (getScreenInfo) import Graphics.X11.Xinerama (getScreenInfo)
@@ -68,7 +66,7 @@ manage w = whenX (not <$> isClient w) $ withDisplay $ \d -> do
where i = W.tag $ W.workspace $ W.current ws where i = W.tag $ W.workspace $ W.current ws
mh <- asks (manageHook . config) mh <- asks (manageHook . config)
g <- fmap appEndo (runQuery mh w) `catchX` return id g <- appEndo <$> userCodeDef (Endo id) (runQuery mh w)
windows (g . f) windows (g . f)
-- | unmanage. A window no longer exists, remove it from the window -- | unmanage. A window no longer exists, remove it from the window
@@ -77,14 +75,14 @@ manage w = whenX (not <$> isClient w) $ withDisplay $ \d -> do
unmanage :: Window -> X () unmanage :: Window -> X ()
unmanage = windows . W.delete unmanage = windows . W.delete
-- | Kill the currently focused client. If we do kill it, we'll get a -- | Kill the specified window. If we do kill it, we'll get a
-- delete notify back from X. -- delete notify back from X.
-- --
-- There are two ways to delete a window. Either just kill it, or if it -- 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) -- supports the delete protocol, send a delete event (e.g. firefox)
-- --
kill :: X () killWindow :: Window -> X ()
kill = withDisplay $ \d -> withFocused $ \w -> do killWindow w = withDisplay $ \d -> do
wmdelt <- atom_WM_DELETE_WINDOW ; wmprot <- atom_WM_PROTOCOLS wmdelt <- atom_WM_DELETE_WINDOW ; wmprot <- atom_WM_PROTOCOLS
protocols <- io $ getWMProtocols d w protocols <- io $ getWMProtocols d w
@@ -95,6 +93,10 @@ kill = withDisplay $ \d -> withFocused $ \w -> do
sendEvent d w False noEventMask ev sendEvent d w False noEventMask ev
else killClient d w >> return () else killClient d w >> return ()
-- | Kill the currently focused client.
kill :: X ()
kill = withFocused killWindow
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- Managing windows -- Managing windows
@@ -120,38 +122,36 @@ windows f = do
-- for each workspace, layout the currently visible workspaces -- for each workspace, layout the currently visible workspaces
let allscreens = W.screens ws let allscreens = W.screens ws
summed_visible = scanl (++) [] $ map (W.integrate' . W.stack . W.workspace) allscreens summed_visible = scanl (++) [] $ map (W.integrate' . W.stack . W.workspace) allscreens
visible <- fmap concat $ forM (zip allscreens summed_visible) $ \ (w, vis) -> do rects <- fmap concat $ forM (zip allscreens summed_visible) $ \ (w, vis) -> do
let wsp = W.workspace w let wsp = W.workspace w
this = W.view n ws this = W.view n ws
n = W.tag wsp n = W.tag wsp
flt = filter (flip M.member (W.floating ws)) (W.index this)
tiled = (W.stack . W.workspace . W.current $ this) tiled = (W.stack . W.workspace . W.current $ this)
>>= W.filter (`M.notMember` W.floating ws) >>= W.filter (`M.notMember` W.floating ws)
>>= W.filter (`notElem` vis) >>= W.filter (`notElem` vis)
viewrect@(Rectangle sx sy sw sh) = screenRect $ W.screenDetail w viewrect = screenRect $ W.screenDetail w
-- just the tiled windows: -- just the tiled windows:
-- now tile the windows on this workspace, modified by the gap -- now tile the windows on this workspace, modified by the gap
(rs, ml') <- runLayout wsp { W.stack = tiled } viewrect `catchX` (rs, ml') <- runLayout wsp { W.stack = tiled } viewrect `catchX`
runLayout wsp { W.stack = tiled, W.layout = Layout Full } viewrect runLayout wsp { W.stack = tiled, W.layout = Layout Full } viewrect
mapM_ (uncurry tileWindow) rs
updateLayout n ml' updateLayout n ml'
-- now the floating windows: let m = W.floating ws
-- move/resize the floating windows, if there are any flt = [(fw, scaleRationalRect viewrect r)
forM_ flt $ \fw -> whenJust (M.lookup fw (W.floating ws)) $ | fw <- filter (flip M.member m) (W.index this)
\(W.RationalRect rx ry rw rh) -> do , Just r <- [M.lookup fw m]]
tileWindow fw $ Rectangle vs = flt ++ rs
(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 (map fst vs)
io $ restackWindows d vs
-- return the visible windows for this workspace: -- return the visible windows for this workspace:
return vs return vs
let visible = map fst rects
mapM_ (uncurry tileWindow) rects
whenJust (W.peek ws) $ \w -> io $ setWindowBorder d w fbc whenJust (W.peek ws) $ \w -> io $ setWindowBorder d w fbc
asks (logHook . config) >>= userCode
mapM_ reveal visible mapM_ reveal visible
setTopFocus setTopFocus
@@ -167,6 +167,13 @@ windows f = do
isMouseFocused <- asks mouseFocused isMouseFocused <- asks mouseFocused
unless isMouseFocused $ clearEvents enterWindowMask 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. set the WM_STATE property
setWMState :: Window -> Int -> X () setWMState :: Window -> Int -> X ()
@@ -192,7 +199,7 @@ reveal :: Window -> X ()
reveal w = withDisplay $ \d -> do reveal w = withDisplay $ \d -> do
setWMState w normalState setWMState w normalState
io $ mapWindow d w io $ mapWindow d w
modify (\s -> s { mapped = S.insert w (mapped s) }) whenX (isClient w) $ modify (\s -> s { mapped = S.insert w (mapped s) })
-- | The client events that xmonad is interested in -- | The client events that xmonad is interested in
clientMask :: EventMask clientMask :: EventMask
@@ -202,7 +209,7 @@ clientMask = structureNotifyMask .|. enterWindowMask .|. propertyChangeMask
setInitialProperties :: Window -> X () setInitialProperties :: Window -> X ()
setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
setWMState w iconicState setWMState w iconicState
io $ selectInput d w $ clientMask io $ selectInput d w clientMask
bw <- asks (borderWidth . config) bw <- asks (borderWidth . config)
io $ setWindowBorderWidth d w bw io $ setWindowBorderWidth d w bw
-- we must initially set the color of new windows, to maintain invariants -- we must initially set the color of new windows, to maintain invariants
@@ -294,11 +301,17 @@ setTopFocus = withWindowSet $ maybe (setFocusX =<< asks theRoot) setFocusX . W.p
-- This happens if X notices we've moved the mouse (and perhaps moved -- This happens if X notices we've moved the mouse (and perhaps moved
-- the mouse to a new screen). -- the mouse to a new screen).
focus :: Window -> X () focus :: Window -> X ()
focus w = withWindowSet $ \s -> do focus w = local (\c -> c { mouseFocused = True }) $ withWindowSet $ \s -> do
if W.member w s then when (W.peek s /= Just w) $ do let stag = W.tag . W.workspace
local (\c -> c { mouseFocused = True }) $ do curr = stag $ W.current s
windows (W.focusWindow w) mnew <- maybe (return Nothing) (fmap (fmap stag) . uncurry pointScreen)
else whenX (isRoot w) $ setFocusX w =<< 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. -- | Call X to set the keyboard focus details.
setFocusX :: Window -> X () setFocusX :: Window -> X ()
@@ -306,14 +319,13 @@ setFocusX w = withWindowSet $ \ws -> do
dpy <- asks display dpy <- asks display
-- clear mouse button grab and border on other windows -- clear mouse button grab and border on other windows
forM_ (W.current ws : W.visible ws) $ \wk -> do forM_ (W.current ws : W.visible ws) $ \wk ->
forM_ (W.index (W.view (W.tag (W.workspace wk)) ws)) $ \otherw -> do forM_ (W.index (W.view (W.tag (W.workspace wk)) ws)) $ \otherw ->
setButtonGrab True otherw setButtonGrab True otherw
-- If we ungrab buttons on the root window, we lose our mouse bindings. -- If we ungrab buttons on the root window, we lose our mouse bindings.
whenX (not <$> isRoot w) $ setButtonGrab False w whenX (not <$> isRoot w) $ setButtonGrab False w
io $ do setInputFocus dpy w revertToPointerRoot 0 io $ setInputFocus dpy w revertToPointerRoot 0
-- raiseWindow dpy w
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Message handling -- Message handling
@@ -324,7 +336,7 @@ sendMessage :: Message a => a -> X ()
sendMessage a = do sendMessage a = do
w <- W.workspace . W.current <$> gets windowset w <- W.workspace . W.current <$> gets windowset
ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
whenJust ml' $ \l' -> do whenJust ml' $ \l' ->
windows $ \ws -> ws { W.current = (W.current ws) windows $ \ws -> ws { W.current = (W.current ws)
{ W.workspace = (W.workspace $ W.current ws) { W.workspace = (W.workspace $ W.current ws)
{ W.layout = l' }}} { W.layout = l' }}}
@@ -374,18 +386,18 @@ isClient w = withWindowSet $ return . W.member w
-- (numlock and capslock) -- (numlock and capslock)
extraModifiers :: X [KeyMask] extraModifiers :: X [KeyMask]
extraModifiers = do extraModifiers = do
nlm <- asks (numlockMask . config) nlm <- gets numberlockMask
return [0, nlm, lockMask, nlm .|. lockMask ] return [0, nlm, lockMask, nlm .|. lockMask ]
-- | Strip numlock\/capslock from a mask -- | Strip numlock\/capslock from a mask
cleanMask :: KeyMask -> X KeyMask cleanMask :: KeyMask -> X KeyMask
cleanMask km = do cleanMask km = do
nlm <- asks (numlockMask . config) nlm <- gets numberlockMask
return (complement (nlm .|. lockMask) .&. km) return (complement (nlm .|. lockMask) .&. km)
-- | Get the 'Pixel' value for a named color -- | Get the 'Pixel' value for a named color
initColor :: Display -> String -> IO (Maybe Pixel) initColor :: Display -> String -> IO (Maybe Pixel)
initColor dpy c = C.handle (\_ -> return Nothing) $ initColor dpy c = C.handle (\(C.SomeException _) -> return Nothing) $
(Just . color_pixel . fst) <$> allocNamedColor dpy colormap c (Just . color_pixel . fst) <$> allocNamedColor dpy colormap c
where colormap = defaultColormap dpy (defaultScreen dpy) where colormap = defaultColormap dpy (defaultScreen dpy)
@@ -398,9 +410,13 @@ restart :: String -> Bool -> X ()
restart prog resume = do restart prog resume = do
broadcastMessage ReleaseResources broadcastMessage ReleaseResources
io . flush =<< asks display io . flush =<< asks display
args <- if resume then gets (("--resume":) . return . showWs . windowset) else return [] let wsData = show . W.mapLayout show . windowset
maybeShow (t, Right (PersistentExtension ext)) = Just (t, show ext)
maybeShow (t, Left str) = Just (t, str)
maybeShow _ = Nothing
extState = return . show . catMaybes . map maybeShow . M.toList . extensibleState
args <- if resume then gets (\s -> "--resume":wsData s:extState s) else return []
catchIO (executeFile prog True args Nothing) catchIO (executeFile prog True args Nothing)
where showWs = show . W.mapLayout show
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- | Floating layer support -- | Floating layer support
@@ -412,22 +428,30 @@ floatLocation w = withDisplay $ \d -> do
ws <- gets windowset ws <- gets windowset
wa <- io $ getWindowAttributes d w wa <- io $ getWindowAttributes d w
bw <- fi <$> asks (borderWidth . config) bw <- fi <$> asks (borderWidth . config)
sc <- fromMaybe (W.current ws) <$> pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
-- XXX horrible let sr = screenRect . W.screenDetail $ sc
let sc = fromMaybe (W.current ws) $ find (pointWithin (fi $ wa_x wa) (fi $ wa_y wa) . screenRect . W.screenDetail) $ W.screens ws
sr = screenRect . W.screenDetail $ sc
rr = W.RationalRect ((fi (wa_x wa) - fi (rect_x sr)) % fi (rect_width sr)) 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_y wa) - fi (rect_y sr)) % fi (rect_height sr))
(fi (wa_width wa + bw*2) % fi (rect_width sr)) (fi (wa_width wa + bw*2) % fi (rect_width sr))
(fi (wa_height wa + bw*2) % fi (rect_height sr)) (fi (wa_height wa + bw*2) % fi (rect_height sr))
return (W.screen $ sc, rr) return (W.screen sc, rr)
where fi x = fromIntegral x where fi x = fromIntegral x
pointWithin :: Integer -> Integer -> Rectangle -> Bool
pointWithin x y r = x >= fi (rect_x r) && -- | Given a point, determine the screen (if any) that contains it.
x < fi (rect_x r) + fi (rect_width r) && pointScreen :: Position -> Position
y >= fi (rect_y r) && -> X (Maybe (W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail))
y < fi (rect_y r) + fi (rect_height r) 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 -- | Make a tiled window floating, using its suggested rectangle
float :: Window -> X () float :: Window -> X ()
@@ -482,7 +506,7 @@ mouseResizeWindow w = whenX (isClient w) $ withDisplay $ \d -> do
wa <- io $ getWindowAttributes d w wa <- io $ getWindowAttributes d w
sh <- io $ getWMNormalHints d w sh <- io $ getWMNormalHints d w
io $ warpPointer d none w 0 0 0 0 (fromIntegral (wa_width wa)) (fromIntegral (wa_height wa)) io $ warpPointer d none w 0 0 0 0 (fromIntegral (wa_width wa)) (fromIntegral (wa_height wa))
mouseDrag (\ex ey -> do mouseDrag (\ex ey ->
io $ resizeWindow d w `uncurry` io $ resizeWindow d w `uncurry`
applySizeHintsContents sh (ex - fromIntegral (wa_x wa), applySizeHintsContents sh (ex - fromIntegral (wa_x wa),
ey - fromIntegral (wa_y wa))) ey - fromIntegral (wa_y wa)))

View File

@@ -35,14 +35,14 @@ module XMonad.StackSet (
-- * Operations on the current stack -- * Operations on the current stack
-- $stackOperations -- $stackOperations
peek, index, integrate, integrate', differentiate, peek, index, integrate, integrate', differentiate,
focusUp, focusDown, focusMaster, focusWindow, focusUp, focusDown, focusUp', focusDown', focusMaster, focusWindow,
tagMember, renameTag, ensureTags, member, findTag, mapWorkspace, mapLayout, tagMember, renameTag, ensureTags, member, findTag, mapWorkspace, mapLayout,
-- * Modifying the stackset -- * Modifying the stackset
-- $modifyStackset -- $modifyStackset
insertUp, delete, delete', filter, insertUp, delete, delete', filter,
-- * Setting the master window -- * Setting the master window
-- $settingMW -- $settingMW
swapUp, swapDown, swapMaster, modify, modify', float, sink, -- needed by users swapUp, swapDown, swapMaster, shiftMaster, modify, modify', float, sink, -- needed by users
-- * Composite operations -- * Composite operations
-- $composite -- $composite
shift, shiftWin, shift, shiftWin,
@@ -52,7 +52,7 @@ module XMonad.StackSet (
) where ) where
import Prelude hiding (filter) import Prelude hiding (filter)
import Data.Maybe (listToMaybe,fromJust,isJust) import Data.Maybe (listToMaybe,isJust,fromMaybe)
import qualified Data.List as L (deleteBy,find,splitAt,filter,nub) import qualified Data.List as L (deleteBy,find,splitAt,filter,nub)
import Data.List ( (\\) ) import Data.List ( (\\) )
import qualified Data.Map as M (Map,insert,delete,empty) import qualified Data.Map as M (Map,insert,delete,empty)
@@ -155,7 +155,7 @@ data RationalRect = RationalRect Rational Rational Rational Rational
deriving (Show, Read, Eq) deriving (Show, Read, Eq)
-- | -- |
-- A stack is a cursor onto a (possibly empty) window list. -- A stack is a cursor onto a window list.
-- The data structure tracks focus by construction, and -- The data structure tracks focus by construction, and
-- the master window is by convention the top-most item. -- the master window is by convention the top-most item.
-- Focus operations will not reorder the list that results from -- Focus operations will not reorder the list that results from
@@ -194,7 +194,8 @@ abort x = error $ "xmonad: StackSet: " ++ x
-- Xinerama: Virtual workspaces are assigned to physical screens, starting at 0. -- Xinerama: Virtual workspaces are assigned to physical screens, starting at 0.
-- --
new :: (Integral s) => l -> [i] -> [sd] -> StackSet i l a s sd 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 new l wids m | not (null wids) && length m <= length wids && not (null m)
= StackSet cur visi unseen M.empty
where (seen,unseen) = L.splitAt (length m) $ map (\i -> Workspace i l Nothing) wids 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 ] (cur:visi) = [ Screen i s sd | (i, s, sd) <- zip3 seen [0..] m ]
-- now zip up visibles with their screen id -- now zip up visibles with their screen id
@@ -342,15 +343,19 @@ index = with [] integrate
-- --
focusUp, focusDown, swapUp, swapDown :: StackSet i l a s sd -> StackSet i l a s sd focusUp, focusDown, swapUp, swapDown :: StackSet i l a s sd -> StackSet i l a s sd
focusUp = modify' focusUp' focusUp = modify' focusUp'
focusDown = modify' (reverseStack . focusUp' . reverseStack) focusDown = modify' focusDown'
swapUp = modify' swapUp' swapUp = modify' swapUp'
swapDown = modify' (reverseStack . swapUp' . reverseStack) 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 (l:ls) rs) = Stack l ls (t:rs)
focusUp' (Stack t [] rs) = Stack x xs [] where (x:xs) = reverse (t:rs) focusUp' (Stack t [] rs) = Stack x xs [] where (x:xs) = 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 (l:ls) rs) = Stack t ls (l:rs)
swapUp' (Stack t [] rs) = Stack t (reverse rs) [] swapUp' (Stack t [] rs) = Stack t (reverse rs) []
@@ -364,7 +369,7 @@ reverseStack (Stack t ls rs) = Stack t rs ls
-- --
focusWindow :: (Eq s, Eq a, Eq i) => a -> StackSet i l a s sd -> StackSet i l a s sd focusWindow :: (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 focusWindow w s | Just w == peek s = s
| otherwise = maybe s id $ do | otherwise = fromMaybe s $ do
n <- findTag w s n <- findTag w s
return $ until ((Just w ==) . peek) focusUp (view n s) return $ until ((Just w ==) . peek) focusUp (view n s)
@@ -508,6 +513,15 @@ swapMaster = modify' $ \c -> case c of
-- natural! keep focus, move current to the top, move top to current. -- natural! keep focus, move current to the top, move top to current.
-- | /O(s)/. Set 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. -- | /O(s)/. Set focus to the master window.
focusMaster :: StackSet i l a s sd -> StackSet i l a s sd focusMaster :: StackSet i l a s sd -> StackSet i l a s sd
focusMaster = modify' $ \c -> case c of focusMaster = modify' $ \c -> case c of
@@ -525,10 +539,7 @@ focusMaster = modify' $ \c -> case c of
-- element on the current stack, the original stackSet is returned. -- element on the current stack, the original stackSet is returned.
-- --
shift :: (Ord a, Eq s, Eq i) => i -> StackSet i l a s sd -> StackSet i l a s sd shift :: (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) shift n s = maybe s (\w -> shiftWin n w s) (peek s)
| otherwise = 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 -- | /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 -- of the stackSet and moves it to stack 'n', leaving it as the focused
@@ -536,13 +547,12 @@ shift n s | n `tagMember` s && n /= curtag = maybe s go (peek s)
-- focused element on that workspace. -- focused element on that workspace.
-- The actual focused workspace doesn't change. If the window is not -- The actual focused workspace doesn't change. If the window is not
-- found in the stackSet, the original stackSet is returned. -- 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 :: (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 shiftWin n w s = case findTag w s of
| n `tagMember` s && (Just n) /= from = go Just from | n `tagMember` s && n /= from -> go from s
| otherwise = s _ -> s
where from = findTag w s where go from = onWorkspace n (insertUp w) . onWorkspace from (delete' w)
go = on n (insertUp w) . on (fromJust from) (delete' w) $ s
on i f = view (currentTag s) . f . view i
onWorkspace :: (Eq i, Eq s) => i -> (StackSet i l a s sd -> StackSet i l a s sd)
-> (StackSet i l a s sd -> StackSet i l a s sd)
onWorkspace n f s = view (currentTag s) . f . view n $ s

68
man/HCAR.tex Normal file
View File

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

View File

@@ -1,40 +0,0 @@
./" man page created by David Lazar on April 24, 2007
./" uses ``tmac.an'' macro set
.TH xmonad 1 "18 April 07" xmonad\-1.0 "xmonad manual"
.SH NAME
xmonad \- a tiling window manager
.SH DESCRIPTION
.PP
\fBxmonad\fR is a minimalist tiling window manager for X, written in Haskell. Windows are managed using automatic layout algorithms, which can be dynamically reconfigured. At any time windows are arranged so as to maximise the use of screen real estate. All features of the window manager are accessible purely from the keyboard: a mouse is entirely optional. \fBxmonad\fR is configured in Haskell, and custom layout algorithms may be implemented by the user in config files. A principle of \fBxmonad\fR is predictability: the user should know in advance precisely the window arrangement that will result from any action.
.PP
By default, \fBxmonad\fR provides three layout algorithms: tall, wide and fullscreen. In tall or wide mode, windows are tiled and arranged to prevent overlap and maximise screen use. Sets of windows are grouped together on virtual screens, and each screen retains its own layout, which may be reconfigured dynamically. Multiple physical monitors are supported via Xinerama, allowing simultaneous display of a number of screens.
.PP
By utilising the expressivity of a modern functional language with a rich static type system, \fBxmonad\fR provides a complete, featureful window manager in less than 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. 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
.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
To use \fBxmonad\fR as your window manager add:
.RS
xmonad
.RE
to your \fI~/.xinitrc\fR file
.SH CUSTOMIZATION
\fBxmonad\fR is customized in ~/.xmonad/xmonad.hs, and then restarting with mod-q.
.SH BUGS
Probably. If you find any, please report them: http://code.google.com/p/xmonad/issues/list

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

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

View File

@@ -8,6 +8,7 @@
-- --
import XMonad import XMonad
import Data.Monoid
import System.Exit import System.Exit
import qualified XMonad.StackSet as W import qualified XMonad.StackSet as W
@@ -18,6 +19,10 @@ import qualified Data.Map as M
-- --
myTerminal = "xterm" myTerminal = "xterm"
-- Whether focus follows the mouse pointer.
myFocusFollowsMouse :: Bool
myFocusFollowsMouse = True
-- Width of the window border in pixels. -- Width of the window border in pixels.
-- --
myBorderWidth = 1 myBorderWidth = 1
@@ -29,21 +34,6 @@ myBorderWidth = 1
-- --
myModMask = mod1Mask 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. -- The default number of workspaces (virtual screens) and their names.
-- By default we use numeric strings, but any string may be used as a -- 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 -- workspace name. The number of workspaces is determined by the length
@@ -63,73 +53,76 @@ myFocusedBorderColor = "#ff0000"
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Key bindings. Add, modify or remove key bindings here. -- Key bindings. Add, modify or remove key bindings here.
-- --
myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $ myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $
-- launch a terminal -- launch a terminal
[ ((modMask .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf) [ ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
-- launch dmenu -- launch dmenu
, ((modMask, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"") , ((modm, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
-- launch gmrun -- launch gmrun
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun") , ((modm .|. shiftMask, xK_p ), spawn "gmrun")
-- close focused window -- close focused window
, ((modMask .|. shiftMask, xK_c ), kill) , ((modm .|. shiftMask, xK_c ), kill)
-- Rotate through the available layout algorithms -- Rotate through the available layout algorithms
, ((modMask, xK_space ), sendMessage NextLayout) , ((modm, xK_space ), sendMessage NextLayout)
-- Reset the layouts on the current workspace to default -- Reset the layouts on the current workspace to default
, ((modMask .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf) , ((modm .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf)
-- Resize viewed windows to the correct size -- Resize viewed windows to the correct size
, ((modMask, xK_n ), refresh) , ((modm, xK_n ), refresh)
-- Move focus to the next window -- Move focus to the next window
, ((modMask, xK_Tab ), windows W.focusDown) , ((modm, xK_Tab ), windows W.focusDown)
-- Move focus to the next window -- Move focus to the next window
, ((modMask, xK_j ), windows W.focusDown) , ((modm, xK_j ), windows W.focusDown)
-- Move focus to the previous window -- Move focus to the previous window
, ((modMask, xK_k ), windows W.focusUp ) , ((modm, xK_k ), windows W.focusUp )
-- Move focus to the master window -- Move focus to the master window
, ((modMask, xK_m ), windows W.focusMaster ) , ((modm, xK_m ), windows W.focusMaster )
-- Swap the focused window and the master window -- Swap the focused window and the master window
, ((modMask, xK_Return), windows W.swapMaster) , ((modm, xK_Return), windows W.swapMaster)
-- Swap the focused window with the next window -- Swap the focused window with the next window
, ((modMask .|. shiftMask, xK_j ), windows W.swapDown ) , ((modm .|. shiftMask, xK_j ), windows W.swapDown )
-- Swap the focused window with the previous window -- Swap the focused window with the previous window
, ((modMask .|. shiftMask, xK_k ), windows W.swapUp ) , ((modm .|. shiftMask, xK_k ), windows W.swapUp )
-- Shrink the master area -- Shrink the master area
, ((modMask, xK_h ), sendMessage Shrink) , ((modm, xK_h ), sendMessage Shrink)
-- Expand the master area -- Expand the master area
, ((modMask, xK_l ), sendMessage Expand) , ((modm, xK_l ), sendMessage Expand)
-- Push window back into tiling -- Push window back into tiling
, ((modMask, xK_t ), withFocused $ windows . W.sink) , ((modm, xK_t ), withFocused $ windows . W.sink)
-- Increment the number of windows in the master area -- Increment the number of windows in the master area
, ((modMask , xK_comma ), sendMessage (IncMasterN 1)) , ((modm , xK_comma ), sendMessage (IncMasterN 1))
-- Deincrement the number of windows in the master area -- Deincrement the number of windows in the master area
, ((modMask , xK_period), sendMessage (IncMasterN (-1))) , ((modm , xK_period), sendMessage (IncMasterN (-1)))
-- toggle the status bar gap -- Toggle the status bar gap
-- TODO, update this binding with avoidStruts , ((modMask , xK_b ), -- Use this binding with avoidStruts from Hooks.ManageDocks.
-- See also the statusBar function from Hooks.DynamicLog.
--
-- , ((modm , xK_b ), sendMessage ToggleStruts)
-- Quit xmonad -- Quit xmonad
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) , ((modm .|. shiftMask, xK_q ), io (exitWith ExitSuccess))
-- Restart xmonad -- Restart xmonad
, ((modMask , xK_q ), restart "xmonad" True) , ((modm , xK_q ), spawn "xmonad --recompile; xmonad --restart")
] ]
++ ++
@@ -137,7 +130,7 @@ myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
-- mod-[1..9], Switch to workspace N -- mod-[1..9], Switch to workspace N
-- mod-shift-[1..9], Move client to workspace N -- mod-shift-[1..9], Move client to workspace N
-- --
[((m .|. modMask, k), windows $ f i) [((m .|. modm, k), windows $ f i)
| (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9] | (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]] , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
++ ++
@@ -146,7 +139,7 @@ myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
-- mod-{w,e,r}, Switch to physical/Xinerama screens 1, 2, or 3 -- mod-{w,e,r}, Switch to physical/Xinerama screens 1, 2, or 3
-- mod-shift-{w,e,r}, Move client to screen 1, 2, or 3 -- mod-shift-{w,e,r}, Move client to screen 1, 2, or 3
-- --
[((m .|. modMask, key), screenWorkspace sc >>= flip whenJust (windows . f)) [((m .|. modm, key), screenWorkspace sc >>= flip whenJust (windows . f))
| (key, sc) <- zip [xK_w, xK_e, xK_r] [0..] | (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]] , (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
@@ -154,16 +147,18 @@ myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Mouse bindings: default actions bound to mouse events -- Mouse bindings: default actions bound to mouse events
-- --
myMouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList $ myMouseBindings (XConfig {XMonad.modMask = modm}) = M.fromList $
-- mod-button1, Set the window to floating mode and move by dragging -- mod-button1, Set the window to floating mode and move by dragging
[ ((modMask, button1), (\w -> focus w >> mouseMoveWindow w)) [ ((modm, button1), (\w -> focus w >> mouseMoveWindow w
>> windows W.shiftMaster))
-- mod-button2, Raise the window to the top of the stack -- mod-button2, Raise the window to the top of the stack
, ((modMask, button2), (\w -> focus w >> windows W.swapMaster)) , ((modm, button2), (\w -> focus w >> windows W.shiftMaster))
-- mod-button3, Set the window to floating mode and resize by dragging -- mod-button3, Set the window to floating mode and resize by dragging
, ((modMask, button3), (\w -> focus w >> mouseResizeWindow w)) , ((modm, button3), (\w -> focus w >> mouseResizeWindow w
>> windows W.shiftMaster))
-- you may also bind events to the mouse scroll wheel (button4 and button5) -- you may also bind events to the mouse scroll wheel (button4 and button5)
] ]
@@ -214,20 +209,22 @@ myManageHook = composeAll
, resource =? "desktop_window" --> doIgnore , resource =? "desktop_window" --> doIgnore
, resource =? "kdesktop" --> doIgnore ] , resource =? "kdesktop" --> doIgnore ]
-- Whether focus follows the mouse pointer. ------------------------------------------------------------------------
myFocusFollowsMouse :: Bool -- Event handling
myFocusFollowsMouse = True
-- * EwmhDesktops users should change this to ewmhDesktopsEventHook
--
-- Defines a custom handler function for X Events. The function should
-- return (All True) if the default handler is to be run afterwards. To
-- combine event hooks use mappend or mconcat from Data.Monoid.
--
myEventHook = mempty
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Status bars and logging -- Status bars and logging
-- Perform an arbitrary action on each internal state change or X event. -- Perform an arbitrary action on each internal state change or X event.
-- See the 'DynamicLog' extension for examples. -- See the 'XMonad.Hooks.DynamicLog' extension for examples.
--
-- To emulate dwm's status bar
--
-- > logHook = dynamicLogDzen
-- --
myLogHook = return () myLogHook = return ()
@@ -260,7 +257,6 @@ defaults = defaultConfig {
focusFollowsMouse = myFocusFollowsMouse, focusFollowsMouse = myFocusFollowsMouse,
borderWidth = myBorderWidth, borderWidth = myBorderWidth,
modMask = myModMask, modMask = myModMask,
numlockMask = myNumlockMask,
workspaces = myWorkspaces, workspaces = myWorkspaces,
normalBorderColor = myNormalBorderColor, normalBorderColor = myNormalBorderColor,
focusedBorderColor = myFocusedBorderColor, focusedBorderColor = myFocusedBorderColor,
@@ -272,6 +268,7 @@ defaults = defaultConfig {
-- hooks, layouts -- hooks, layouts
layoutHook = myLayout, layoutHook = myLayout,
manageHook = myManageHook, manageHook = myManageHook,
handleEventHook = myEventHook,
logHook = myLogHook, logHook = myLogHook,
startupHook = myStartupHook startupHook = myStartupHook
} }

View File

@@ -14,7 +14,7 @@ import Data.Ratio
import Data.Maybe import Data.Maybe
import System.Environment import System.Environment
import Control.Exception (assert) import Control.Exception (assert)
import qualified Control.Exception as C import qualified Control.Exception.Extensible as C
import Control.Monad import Control.Monad
import Test.QuickCheck hiding (promote) import Test.QuickCheck hiding (promote)
import System.IO.Unsafe import System.IO.Unsafe
@@ -528,6 +528,18 @@ prop_shift_reversible i (x :: T) =
y = swapMaster x y = swapMaster x
n = tag (workspace $ current y) n = tag (workspace $ current y)
------------------------------------------------------------------------
-- shiftMaster
-- focus/local/idempotent same as swapMaster:
prop_shift_master_focus (x :: T) = peek x == (peek $ shiftMaster x)
prop_shift_master_local (x :: T) = hidden_spaces x == hidden_spaces (shiftMaster x)
prop_shift_master_idempotent (x :: T) = shiftMaster (shiftMaster x) == shiftMaster x
-- ordering is constant modulo the focused window:
prop_shift_master_ordering (x :: T) = case peek x of
Nothing -> True
Just m -> L.delete m (index x) == L.delete m (index $ shiftMaster x)
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- shiftWin -- shiftWin
@@ -601,13 +613,13 @@ prop_lookup_visible (x :: T) =
-- and help out hpc -- and help out hpc
prop_abort x = unsafePerformIO $ C.catch (abort "fail") prop_abort x = unsafePerformIO $ C.catch (abort "fail")
(\e -> return $ show e == "xmonad: StackSet: fail" ) (\(C.SomeException e) -> return $ show e == "xmonad: StackSet: fail" )
where where
_ = x :: Int _ = x :: Int
-- new should fail with an abort -- new should fail with an abort
prop_new_abort x = unsafePerformIO $ C.catch f prop_new_abort x = unsafePerformIO $ C.catch f
(\e -> return $ show e == "xmonad: StackSet: non-positive argument to StackSet.new" ) (\(C.SomeException e) -> return $ show e == "xmonad: StackSet: non-positive argument to StackSet.new" )
where where
f = new undefined{-layout-} [] [] `seq` return False f = new undefined{-layout-} [] [] `seq` return False
@@ -933,6 +945,11 @@ main = do
,("swapUp is local" , mytest prop_swap_left_local) ,("swapUp is local" , mytest prop_swap_left_local)
,("swapDown is local" , mytest prop_swap_right_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: invariant" , mytest prop_shift_I)
,("shift is reversible" , mytest prop_shift_reversible) ,("shift is reversible" , mytest prop_shift_reversible)
,("shiftWin: invariant" , mytest prop_shift_win_I) ,("shiftWin: invariant" , mytest prop_shift_win_I)

View File

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

View File

@@ -1,7 +1,14 @@
-- Unlike the rest of xmonad, this file is copyright under the terms of the
-- GPL.
-- --
-- Generates man/xmonad.1 from man/xmonad.1.in by filling the list of -- Generates man/xmonad.1 from man/xmonad.1.in by filling the list of
-- keybindings with values scraped from Config.hs -- keybindings with values scraped from Config.hs
-- --
-- Uses cabal to grab the xmonad version from xmonad.cabal
--
-- Uses pandoc to convert the "xmonad.1.markdown" to "xmonad.1"
--
-- Format for the docstrings in Config.hs takes the following form: -- Format for the docstrings in Config.hs takes the following form:
-- --
-- -- mod-x %! Frob the whatsit -- -- mod-x %! Frob the whatsit
@@ -14,12 +21,23 @@
-- [ ((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. -- Here, mod-shift-return will be used as the keybinding name.
--
import Control.Monad import Control.Monad
import Control.Applicative
import Text.Regex.Posix import Text.Regex.Posix
import Data.Char import Data.Char
import Data.List import Data.List
import Distribution.PackageDescription.Parse
import Distribution.Verbosity
import Distribution.Package
import Distribution.PackageDescription
import Text.PrettyPrint.HughesPJ
import Distribution.Text
import Text.Pandoc -- works with 1.6
releaseDate = "25 October 09"
trim :: String -> String trim :: String -> String
trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace
@@ -35,13 +53,47 @@ 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? -- FIXME: What escaping should we be doing on these strings?
troff :: (String, String) -> String markdownDefn :: (String, String) -> String
troff (key, desc) = ".IP\n \\fB" ++ key ++ "\\fR\n" ++ desc ++ "\n" markdownDefn (key, desc) = key ++ "\n: " ++ desc
replace :: Eq a => a -> a -> [a] -> [a] replace :: Eq a => a -> a -> [a] -> [a]
replace x y = map (\a -> if a == x then y else a) replace x y = map (\a -> if a == x then y else a)
-- rawSystem "pandoc" ["--read=markdown","--write=man","man/xmonad.1.markdown"]
main = do main = do
troffBindings <- (concatMap troff . allBindings) `liftM` readFile "./XMonad/Config.hs" releaseName <- (show . disp . package . packageDescription)
let sed = unlines . replace "___KEYBINDINGS___" troffBindings . lines `liftM`readPackageDescription normal "xmonad.cabal"
readFile "./man/xmonad.1.in" >>= return . sed >>= writeFile "./man/xmonad.1" keybindings <- (intercalate "\n\n" . map markdownDefn . allBindings)
`liftM` readFile "./XMonad/Config.hs"
let manHeader = unwords [".TH xmonad 1","\""++releaseDate++"\"",releaseName,"\"xmonad manual\""]
writeOpts = defaultWriterOptions -- { writerLiterateHaskell = True }
parsed <- readMarkdown defaultParserState { stateLiterateHaskell = True }
. unlines
. replace "___KEYBINDINGS___" keybindings
. lines
<$> readFile "./man/xmonad.1.markdown"
Right template <- getDefaultTemplate Nothing "man"
writeFile "./man/xmonad.1"
. (manHeader ++)
. writeMan writeOpts{ writerStandalone = True, writerTemplate = template }
$ parsed
putStrLn "Documentation created: man/xmonad.1"
Right template <- getDefaultTemplate Nothing "html"
writeFile "./man/xmonad.1.html"
. writeHtmlString writeOpts
{ writerVariables =
[("include-before"
,"<h1>"++releaseName++"</h1>"++
"<p>Section: xmonad manual (1)<br/>"++
"Updated: "++releaseDate++"</p>"++
"<hr/>")]
, writerStandalone = True
, writerTemplate = template
, writerTableOfContents = True }
$ parsed
putStrLn "Documentation created: man/xmonad.1.html"

View File

@@ -1,5 +1,5 @@
name: xmonad name: xmonad
version: 0.8 version: 0.10
homepage: http://xmonad.org homepage: http://xmonad.org
synopsis: A tiling window manager synopsis: A tiling window manager
description: description:
@@ -18,11 +18,13 @@ license-file: LICENSE
author: Spencer Janssen author: Spencer Janssen
maintainer: xmonad@haskell.org maintainer: xmonad@haskell.org
extra-source-files: README TODO CONFIG STYLE tests/loc.hs tests/Properties.hs 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 man/xmonad.1.markdown man/xmonad.1 man/xmonad.1.html
util/GenerateManpage.hs util/GenerateManpage.hs
cabal-version: >= 1.2 cabal-version: >= 1.2
build-type: Simple build-type: Simple
data-files: man/xmonad.hs
flag small_base flag small_base
description: Choose the new smaller, split-up base package. description: Choose the new smaller, split-up base package.
@@ -41,12 +43,18 @@ library
XMonad.StackSet XMonad.StackSet
if flag(small_base) if flag(small_base)
build-depends: base >= 3, containers, directory, process build-depends: base < 5 && >=3, containers, directory, process, filepath, extensible-exceptions
else else
build-depends: base < 3 build-depends: base < 3
build-depends: X11>=1.4.1, mtl, unix build-depends: X11>=1.5.0.0 && < 1.6, mtl, unix,
utf8-string >= 0.3 && < 0.4
if true
ghc-options: -funbox-strict-fields -Wall ghc-options: -funbox-strict-fields -Wall
if impl(ghc >= 6.12.1)
ghc-options: -fno-warn-unused-do-bind
ghc-prof-options: -prof -auto-all ghc-prof-options: -prof -auto-all
extensions: CPP extensions: CPP
@@ -64,7 +72,12 @@ executable xmonad
XMonad.Operations XMonad.Operations
XMonad.StackSet XMonad.StackSet
if true
ghc-options: -funbox-strict-fields -Wall ghc-options: -funbox-strict-fields -Wall
if impl(ghc >= 6.12.1)
ghc-options: -fno-warn-unused-do-bind
ghc-prof-options: -prof -auto-all ghc-prof-options: -prof -auto-all
extensions: CPP extensions: CPP
@@ -74,4 +87,4 @@ executable xmonad
build-depends: QuickCheck < 2 build-depends: QuickCheck < 2
ghc-options: -Werror ghc-options: -Werror
if flag(testing) && flag(small_base) if flag(testing) && flag(small_base)
build-depends: random build-depends: filepath, process, directory, mtl, unix, X11, base, containers, random, extensible-exceptions