106 Commits
v0.5 ... v0.7

Author SHA1 Message Date
Spencer Janssen
ef516142b9 Remove -fhpc from ghc-options (annoying hackage workaround) 2008-03-29 20:58:04 +00:00
Spencer Janssen
cb51875da6 Remove version numbers from README 2008-03-29 20:41:58 +00:00
Spencer Janssen
167a6e155b Bump version to 0.7 2008-03-29 19:13:36 +00:00
Don Stewart
2b2774f81d no need to expose --resume to the user 2008-03-28 21:42:19 +00:00
Spencer Janssen
16725dfe0d Rename property to stringProperty 2008-03-25 20:18:14 +00:00
Brent Yorgey
15db3c6f0a ManageHook: add a 'property' Query that can get an arbitrary String property from a window (such as WM_WINDOW_ROLE, for example) 2008-03-25 14:54:14 +00:00
Brent Yorgey
6db444eb1a Main.hs: startupHook should be guarded by userCode 2008-03-25 17:12:41 +00:00
Spencer Janssen
46bc3bbd17 Also print compilation errors to stderr 2008-03-24 22:58:57 +00:00
Don Stewart
d948210935 clean up for style 2008-03-22 21:41:16 +00:00
Andrea Rossato
db08970071 add sendMessageWithNoRefresh and have broadcastMessage use it
This patch:
- moves broadcastMessage and restart from Core to Operations (to avoid
  circular imports);
- in Operations introduces sendMessageWithNoRefresh and move
 updateLayout outside windows.
- broadcastMessage now uses sendMessageWithNoRefresh to obey to this
  rules:
  1. if handleMessage returns Nothing no action is taken;
  2. if handleMessage returns a Just ml *only* the layout field of the
     workspace record will be updated.
2008-02-23 13:07:02 +00:00
Spencer Janssen
4c69a85b3f --recompile now forces recompilation of xmonad.hs 2008-03-24 21:24:53 +00:00
Lukas Mai
ac103b8472 add --help option 2008-01-29 23:52:58 +00:00
Don Stewart
029965e4d4 add mod-shift-tab to the default bindings, from Mathias Stearn 2008-03-23 21:14:21 +00:00
Don Stewart
9fd1d4f9d0 more tests 2008-03-23 00:34:36 +00:00
Don Stewart
dbbd934b0b some tests for the size increment handling in Operations.hs 2008-03-22 23:49:52 +00:00
Don Stewart
750544fda9 more properties for splitting horizontally and vertically 2008-03-22 20:18:35 +00:00
Don Stewart
90eae3fd63 test message handling of Full layout 2008-03-22 19:27:28 +00:00
Don Stewart
d6233d0463 formatting 2008-03-22 19:26:35 +00:00
Don Stewart
5f088f4e99 strict fields on layout messages 2008-03-22 19:22:48 +00:00
Don Stewart
f8a7d8d381 QuickCheck properties to fully specify the Tall layout, and its messages 2008-03-22 04:18:01 +00:00
Don Stewart
f7686746c6 clean up Layout.hs, not entirely happy about the impure layouts. 2008-03-22 04:17:18 +00:00
Don Stewart
04ee55c3ca comments 2008-03-22 04:16:54 +00:00
Don Stewart
50ce362626 add hpc generation script 2008-03-22 04:16:40 +00:00
Don Stewart
209b88f821 add QuickCheck property for Full: it produces one window, it is fullscreen, and it is the current window 2008-03-22 00:20:26 +00:00
Don Stewart
c5cca485df QC for pureLayout. confirm pureLayout . Tall produces no overlaps 2008-03-22 00:12:29 +00:00
Don Stewart
0593a282ca whitespace 2008-03-22 00:12:08 +00:00
Don Stewart
351de8d2b6 reenable quickcheck properties for layouts (no overlap, fullscreen) 2008-03-21 23:40:15 +00:00
Don Stewart
4bd9073937 formatting 2008-03-21 23:09:56 +00:00
Don Stewart
79754fd5d3 Revert float location patch. Not Xinerama safe 2008-03-21 21:41:29 +00:00
Lukas Mai
b14de19e8b XMonad.Core: ignore SIGPIPE, let write calls throw 2008-03-21 17:19:11 +00:00
Brent Yorgey
e97c326ff0 update documentation 2008-03-11 16:07:27 +00:00
Andrea Rossato
bc13b4ba07 Reimplement Mirror with runLayout 2008-02-25 08:32:36 +00:00
Andrea Rossato
5bea59a823 Reimplement Choose with runLayout 2008-02-22 19:31:19 +00:00
Andrea Rossato
669a162cfc runLayout is now a LayoutClass method and takes the Workspace and the screen Rectangle 2008-02-22 17:58:15 +00:00
Don Stewart
310c22694e add property for ensureTags behaviour on hidden workspaces 2008-03-10 18:25:57 +00:00
robreim
1c930ba955 Small linecount fix :) 2008-03-08 02:19:39 +00:00
robreim
797204fe6c Change floats to always use the current screen 2008-03-08 01:58:29 +00:00
Don Stewart
a3ecf5d304 use -fhpc by default when testing. All developers should have 6.8.x 2008-03-07 18:42:23 +00:00
Don Stewart
1a4a4a5000 more general properties for view, greedyView 2008-03-07 18:16:57 +00:00
Don Stewart
a8d3564653 rework failure cases in StackSet.view 2008-03-07 18:16:34 +00:00
Don Stewart
d5955b023c bit more code coverage 2008-03-07 18:09:05 +00:00
Don Stewart
4d9a6c2681 more tests. slightly better test coverage 2008-02-27 18:01:13 +00:00
Don Stewart
87193ff61e test geometry setting 2008-02-27 17:55:54 +00:00
Don Stewart
3303c6e05d incorrect invariant test for greedyView 2008-02-25 18:03:50 +00:00
Brent Yorgey
9d9acba45f Add a startupHook.
The only thing I am not sure about here is at what exact point the 
startupHook should get run.  I picked a place that seems to make sense: 
as late as possible, right before entering the main loop.  That way all
the layouts/workspaces/other state are set up and the startupHook can
manipulate them.
2008-02-04 19:24:45 +00:00
Brent Yorgey
cc2754d82a Core.hs: add an Applicative instance for X 2008-02-04 19:23:48 +00:00
gwern0
cea3492d28 update LOC claim in man page 2008-02-15 21:14:20 +00:00
Don Stewart
14d9a194ff add quickstart instructions 2008-02-12 20:35:02 +00:00
Spencer Janssen
e8d1d028ba Remove non-existent windows on restart 2008-02-07 09:11:40 +00:00
Don Stewart
695860f1fd Lift initColor exceptions into Maybe
We should audit all X11 Haskell lib calls we make for whether
they throw undocumented exceptions, and then banish that.
2008-02-06 19:48:58 +00:00
Don Stewart
261f742404 some things to do 2008-02-06 19:25:33 +00:00
Don Stewart
1de1bcded2 module uses CPP 2008-02-06 19:05:21 +00:00
Spencer Janssen
0c697ebbb4 Rename runManageHook to runQuery 2008-02-04 05:33:36 +00:00
daniel
a626083721 let enter dismiss compile errors 2008-02-03 20:28:52 +00:00
Brent Yorgey
481e42ab72 Core.hs, StackSet.hs: some documentation updates 2008-02-01 19:06:53 +00:00
Andrea Rossato
e751c4b62f Make Mirror implement emptyLayout 2008-01-28 00:18:34 +00:00
"Valery V. Vorotyntsev"
730984fd60 xmonad.cabal: add `build-type' to make Cabal happy 2008-01-31 16:32:13 +00:00
Daniel Neri
ad85e11a4a Get version from the Paths_xmonad module generated by Cabal
No need to bump version in more than one place.
2008-01-29 14:40:37 +00:00
Spencer Janssen
2da09787da Kill stale xmonad 0.1 comments 2008-01-28 21:14:18 +00:00
Spencer Janssen
162a54d992 Point to 0.6 release of contrib 2008-01-28 10:11:15 +00:00
Don Stewart
d00d4ca046 notes on releases 2008-01-28 17:10:12 +00:00
Don Stewart
0dd54885eb bump output of --version 2008-01-28 17:08:40 +00:00
Spencer Janssen
f80d593d57 Generalize the type of catchIO, use it in Main.hs 2008-01-28 05:46:51 +00:00
Andrea Rossato
10be8aaae0 Add emptyLayout to LayoutClass, a method to be called when a workspace is empty 2008-01-24 01:32:07 +00:00
Don Stewart
66f623b656 clarify copyright 2008-01-08 18:56:40 +00:00
Spencer Janssen
b86351f3c3 TAG 0.6 2008-01-27 22:06:33 +00:00
Spencer Janssen
8399e80327 More other-modules 2008-01-27 22:01:52 +00:00
Spencer Janssen
3e3d516092 Update example config 2008-01-27 21:23:31 +00:00
Spencer Janssen
d2ae7310d6 Bump version to 0.6 2008-01-27 20:50:00 +00:00
Austin Seipp
ca3e277d2b Updated ./man/xmonad.1.in to contain new command line parameters 2008-01-22 07:01:53 +00:00
Spencer Janssen
bb2b6c7bf8 Depend on QuickCheck < 2 when building tests 2008-01-22 07:02:25 +00:00
Spencer Janssen
d74814af35 Roll testing into the main executable, use Cabal to build the tests 2008-01-19 09:12:15 +00:00
Spencer Janssen
f9799422f9 Simplify duplicate/cloned screen logic 2008-01-18 03:22:28 +00:00
Joachim Breitner
be5e27038f Put the screen removing stuff in getCleanedScreenInfo 2007-12-31 18:15:56 +00:00
Joachim Breitner
1f4b8cb5f6 Ignore cloned screens
This patch ignores screens that are just clones of existing ones,
or are completely contained in another. Currently only for rescreen, not yet for
xmonad start.
2007-12-31 18:06:28 +00:00
Spencer Janssen
da7ca1c29d -Werror when flag(testing) only 2008-01-18 01:48:27 +00:00
nicolas.pouillard
e095621ab9 Export doubleFork 2008-01-14 20:26:12 +00:00
Lukas Mai
93c55c948e reword comment (previous version didn't make sense to me) 2007-11-22 16:59:25 +00:00
nicolas.pouillard
9ff105340e The recompile function now returns a boolean status instead of (). 2008-01-05 22:55:00 +00:00
Spencer Janssen
5e61b137fb Make focus-follows-mouse configurable 2007-12-29 02:33:01 +00:00
Spencer Janssen
aeef36f74c Strictify all XConfig fields, gives nice error messages when a field is forgotten on construction 2007-12-29 02:19:23 +00:00
Spencer Janssen
673f303646 Spelling 2007-12-29 02:16:28 +00:00
Spencer Janssen
7f3c6823d4 Wibble 2007-12-29 02:15:19 +00:00
Spencer Janssen
79f23d6cec Broadcast button events to all layouts, fix for issue #111 2007-12-27 08:03:56 +00:00
Brent Yorgey
46f5e68cfa Config.hs: too many users seem to be ignoring/missing the polite warning not to modify this file; change it to something a bit less polite/more obvious. 2007-12-20 20:15:49 +00:00
Spencer Janssen
76d2bddaf0 Remove desktop manageHook rules in favor of ManageDocks 2007-12-22 11:37:35 +00:00
Spencer Janssen
f5e55f3a27 Wibble 2007-12-22 04:11:51 +00:00
Spencer Janssen
6c72a03fb1 Add support for several flags:
--version: print xmonad's version
 --recompile: recompile xmonad.hs if it is out of date
 --force-recompile: recompile xmonad.hs unconditionally
2007-12-22 02:05:20 +00:00
Spencer Janssen
31c7734f7b Remove getProgName capability from restart, we don't use it anymore 2007-12-19 21:50:11 +00:00
Spencer Janssen
d1af7d986d Flush pending X calls before restarting 2007-12-19 16:20:29 +00:00
tim.thelion
da167bfc11 Allow for sharing of home directory across architectures. 2007-12-18 06:51:46 +00:00
Spencer Janssen
c46f3ad549 Call 'broadcastMessage ReleaseResources' in restart 2007-12-19 06:57:10 +00:00
Adam Vogt
5b42a58d06 Manpage now describes config in ~/.xmonad/xmonad.hs 2007-12-19 02:39:18 +00:00
Adam Vogt
e8292e0e9d Update manpage to describe greedyView 2007-12-19 02:37:26 +00:00
Spencer Janssen
6cd46e12bb Depend on X11-1.4.1, it has crucial bugfixes 2007-12-15 02:21:00 +00:00
Don Stewart
2441275122 1.4.1 X11 dep 2007-12-14 16:05:58 +00:00
Spencer Janssen
f70ab7964e Set withdrawnState after calling hide 2007-12-12 06:02:50 +00:00
Spencer Janssen
237fdbf037 Remove stale comment 2007-12-11 08:42:36 +00:00
Spencer Janssen
5166ede96b Make windows responsible for setting withdrawn state 2007-12-11 08:01:17 +00:00
Spencer Janssen
56463b2391 Remove stale comment 2007-12-11 07:56:41 +00:00
Spencer Janssen
f427c2b0e9 Clean up stale mapped/waitingUnmap state in handle rather than unmanage.
This is an attempt to fix issue #96.  Thanks to jcreigh for the insights
necessary to fix the bug.
2007-12-11 07:48:10 +00:00
Spencer Janssen
287d364e0d Delete windows from waitingUnmap that aren't waitng for any unmaps 2007-12-11 07:45:06 +00:00
Brent Yorgey
8c31768b79 man/xmonad.hs: add some documentation explaining that 'title' can be used in the manageHook just like 'resource' and 'className'. 2007-12-10 17:33:57 +00:00
Lukas Mai
9ceef229c3 normalize Module headers 2007-12-10 08:53:27 +00:00
Spencer Janssen
40581c9bf8 Add 'testing' mode, this should reduce 'darcs check' time significantly 2007-12-10 00:47:04 +00:00
Spencer Janssen
161ade3593 Use XMonad meta-module in Main.hs 2007-12-10 00:44:56 +00:00
17 changed files with 775 additions and 369 deletions

24
LICENSE
View File

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

45
Main.hs
View File

@@ -1,6 +1,6 @@
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
-- | -- |
-- Module : Main.hs -- Module : Main
-- Copyright : (c) Spencer Janssen 2007 -- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE) -- License : BSD3-style (see LICENSE)
-- --
@@ -14,21 +14,50 @@
module Main (main) where module Main (main) where
import XMonad.Main import XMonad
import XMonad.Config
import XMonad.Core (getXMonadDir, recompile)
import Control.Exception (handle)
import System.IO import System.IO
import System.Info
import System.Environment import System.Environment
import System.Posix.Process (executeFile) import System.Posix.Process (executeFile)
import Paths_xmonad (version)
import Data.Version (showVersion)
#ifdef TESTING
import qualified Properties
#endif
-- | The entry point into xmonad. Attempts to compile any custom main -- | The entry point into xmonad. Attempts to compile any custom main
-- 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
handle (hPrint stderr) buildLaunch args <- getArgs
xmonad defaultConfig -- if buildLaunch returns, execute the trusted core let launch = catchIO buildLaunch >> xmonad defaultConfig
case args of
[] -> launch
["--resume", _] -> launch
["--help"] -> usage
["--recompile"] -> recompile True >> return ()
["--version"] -> putStrLn ("xmonad " ++ showVersion version)
#ifdef TESTING
("--run-tests":_) -> Properties.main
#endif
_ -> fail "unrecognized flags"
usage :: IO ()
usage = do
self <- getProgName
putStr . unlines $
concat ["Usage: ", self, " [OPTION]"] :
"Options:" :
" --help Print this message" :
" --version Print the version number" :
" --recompile Recompile your ~/.xmonad/xmonad.hs" :
#ifdef TESTING
" --run-tests Run the test suite" :
#endif
[]
-- | 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
@@ -45,5 +74,5 @@ buildLaunch = do
recompile False recompile False
dir <- getXMonadDir dir <- getXMonadDir
args <- getArgs args <- getArgs
executeFile (dir ++ "/xmonad") False args Nothing executeFile (dir ++ "/xmonad-"++arch++"-"++os) False args Nothing
return () return ()

16
README
View File

@@ -12,6 +12,16 @@
workspace. Xinerama is fully supported, allowing windows to be tiled workspace. Xinerama is fully supported, allowing windows to be tiled
on several physical screens. on several physical screens.
Quick start:
Obtain the dependent libraries, then build with:
runhaskell Setup.lhs configure --user --prefix=$HOME
runhaskell Setup.lhs build
runhaskell Setup.lhs install --user
For the full story, read on.
Building: Building:
Building is quite straightforward, and requries a basic Haskell toolchain. Building is quite straightforward, and requries a basic Haskell toolchain.
@@ -72,7 +82,7 @@ Building:
mtl http://hackage.haskell.org/cgi-bin/hackage-scripts/package/mtl-1.0 mtl http://hackage.haskell.org/cgi-bin/hackage-scripts/package/mtl-1.0
unix http://hackage.haskell.org/cgi-bin/hackage-scripts/package/unix-2.0 unix http://hackage.haskell.org/cgi-bin/hackage-scripts/package/unix-2.0
X11 http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-1.3.0 X11 http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-1.4.1
* Build xmonad: * Build xmonad:
@@ -123,9 +133,9 @@ XMonadContrib
prompt/program launcher, and various other useful modules. prompt/program launcher, and various other useful modules.
XMonadContrib is available at: XMonadContrib is available at:
0.5 release: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmonad-contrib-0.5 latest release: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmonad-contrib
darcs version: darcs get http://code.haskell.org/XMonadContrib darcs version: darcs get http://code.haskell.org/XMonadContrib
------------------------------------------------------------------------ ------------------------------------------------------------------------

1
TODO
View File

@@ -18,3 +18,4 @@
* upload X11 and xmonad to hackage * upload X11 and xmonad to hackage
* 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

View File

@@ -10,8 +10,12 @@
-- Portability : portable -- Portability : portable
-- --
-- This module specifies the default configuration values for xmonad. -- This module specifies the default configuration values for xmonad.
-- Users should not modify this file. Rather, they should provide their --
-- own @~\/.xmonad\/xmonad.hs@ that overrides specific fields in defaultConfig. -- DO NOT MODIFY THIS FILE! It won't work. You may configure xmonad
-- by providing your own @~\/.xmonad\/xmonad.hs@ that overrides
-- specific fields in 'defaultConfig'. For a starting point, you can
-- copy the @xmonad.hs@ found in the @man@ directory, or look at
-- examples on the xmonad wiki.
-- --
------------------------------------------------------------------------ ------------------------------------------------------------------------
@@ -21,11 +25,13 @@ 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,borderWidth,mouseBindings (workspaces,manageHook,numlockMask,keys,logHook,startupHook,borderWidth,mouseBindings
,defaultGaps,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor) ,defaultGaps,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor
,focusFollowsMouse)
import qualified XMonad.Core as XMonad import qualified XMonad.Core as XMonad
(workspaces,manageHook,numlockMask,keys,logHook,borderWidth,mouseBindings (workspaces,manageHook,numlockMask,keys,logHook,startupHook,borderWidth,mouseBindings
,defaultGaps,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor) ,defaultGaps,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor
,focusFollowsMouse)
import XMonad.Layout import XMonad.Layout
import XMonad.Operations import XMonad.Operations
@@ -113,9 +119,7 @@ defaultGaps = [(0,0,0,0)] -- 15 for default dzen font
manageHook :: ManageHook manageHook :: ManageHook
manageHook = composeAll manageHook = composeAll
[ className =? "MPlayer" --> doFloat [ className =? "MPlayer" --> doFloat
, className =? "Gimp" --> doFloat , className =? "Gimp" --> doFloat ]
, resource =? "desktop_window" --> doIgnore
, resource =? "kdesktop" --> doIgnore ]
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Logging -- Logging
@@ -130,6 +134,10 @@ manageHook = composeAll
logHook :: X () logHook :: X ()
logHook = return () logHook = return ()
-- | Perform an arbitrary action at xmonad startup.
startupHook :: X ()
startupHook = return ()
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Extensible layouts -- Extensible layouts
-- --
@@ -163,6 +171,10 @@ layout = tiled ||| Mirror tiled ||| Full
terminal :: String terminal :: String
terminal = "xterm" terminal = "xterm"
-- | Whether focus follows the mouse pointer.
focusFollowsMouse :: Bool
focusFollowsMouse = True
-- | The xmonad key bindings. Add, modify or remove key bindings here. -- | The xmonad key bindings. Add, modify or remove key bindings here.
-- --
-- (The comment formatting character is used when generating the manpage) -- (The comment formatting character is used when generating the manpage)
@@ -182,6 +194,7 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
-- move focus up or down the window stack -- move focus up or down the window stack
, ((modMask, xK_Tab ), windows W.focusDown) -- %! Move focus to the next window , ((modMask, xK_Tab ), windows W.focusDown) -- %! Move focus to the next window
, ((modMask .|. shiftMask, xK_Tab ), windows W.focusUp ) -- %! Move focus to the previous window
, ((modMask, xK_j ), windows W.focusDown) -- %! Move focus to the next window , ((modMask, xK_j ), windows W.focusDown) -- %! Move focus to the next window
, ((modMask, xK_k ), windows W.focusUp ) -- %! Move focus to the previous window , ((modMask, xK_k ), windows W.focusUp ) -- %! Move focus to the previous window
, ((modMask, xK_m ), windows W.focusMaster ) -- %! Move focus to the master window , ((modMask, xK_m ), windows W.focusMaster ) -- %! Move focus to the master window
@@ -207,7 +220,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 ), broadcastMessage ReleaseResources >> restart (Just "xmonad") True) -- %! Restart xmonad , ((modMask , xK_q ), restart "xmonad" True) -- %! Restart xmonad
] ]
++ ++
-- mod-[1..9] %! Switch to workspace N -- mod-[1..9] %! Switch to workspace N
@@ -248,5 +261,7 @@ defaultConfig = XConfig
, XMonad.modMask = defaultModMask , XMonad.modMask = defaultModMask
, XMonad.keys = keys , XMonad.keys = keys
, XMonad.logHook = logHook , XMonad.logHook = logHook
, XMonad.startupHook = startupHook
, XMonad.mouseBindings = mouseBindings , XMonad.mouseBindings = mouseBindings
, XMonad.manageHook = manageHook } , XMonad.manageHook = manageHook
, XMonad.focusFollowsMouse = focusFollowsMouse }

View File

@@ -1,11 +1,11 @@
{-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving, {-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving,
MultiParamTypeClasses, TypeSynonymInstances #-} MultiParamTypeClasses, TypeSynonymInstances, CPP #-}
-- required for deriving Typeable -- required for deriving Typeable
{-# OPTIONS_GHC -fglasgow-exts #-} {-# OPTIONS_GHC -fglasgow-exts #-}
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- | -- |
-- Module : XMonad/Core.hs -- Module : XMonad.Core
-- Copyright : (c) Spencer Janssen 2007 -- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE) -- License : BSD3-style (see LICENSE)
-- --
@@ -23,14 +23,14 @@ module XMonad.Core (
ScreenId(..), ScreenDetail(..), XState(..), ScreenId(..), ScreenDetail(..), XState(..),
XConf(..), XConfig(..), LayoutClass(..), XConf(..), XConfig(..), LayoutClass(..),
Layout(..), readsLayout, Typeable, Message, Layout(..), readsLayout, Typeable, Message,
SomeMessage(..), fromMessage, runLayout, LayoutMessages(..), SomeMessage(..), fromMessage, LayoutMessages(..),
runX, catchX, userCode, io, catchIO, runX, catchX, userCode, io, catchIO, doubleFork,
withDisplay, withWindowSet, isRoot, withDisplay, withWindowSet, isRoot, runOnWorkspaces,
getAtom, spawn, restart, getXMonadDir, recompile, trace, whenJust, whenX, getAtom, spawn, getXMonadDir, recompile, trace, whenJust, whenX,
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, ManageHook, Query(..), runManageHook atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, ManageHook, Query(..), runQuery
) where ) where
import XMonad.StackSet import XMonad.StackSet hiding (modify)
import Prelude hiding ( catch ) import Prelude hiding ( catch )
import Control.Exception (catch, bracket, throw, Exception(ExitException)) import Control.Exception (catch, bracket, throw, Exception(ExitException))
@@ -38,11 +38,11 @@ import Control.Applicative
import Control.Monad.State import Control.Monad.State
import Control.Monad.Reader import Control.Monad.Reader
import System.IO import System.IO
import System.Info
import System.Posix.Process (executeFile, forkProcess, getProcessStatus, createSession) import System.Posix.Process (executeFile, forkProcess, getProcessStatus, createSession)
import System.Process import System.Process
import System.Directory import System.Directory
import System.Exit import System.Exit
import System.Environment
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
@@ -51,14 +51,14 @@ import Data.Monoid
import qualified Data.Map as M import qualified Data.Map as M
import qualified Data.Set as S import qualified Data.Set as S
-- | XState, the window manager state. -- | XState, the (mutable) window manager state.
-- Just the display, width, height and a window list
data XState = XState 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 ())) }
-- | XConf, the (read-only) window manager configuration.
data XConf = XConf data XConf = XConf
{ display :: Display -- ^ the X11 display { display :: Display -- ^ the X11 display
, config :: !(XConfig Layout) -- ^ initial user configuration , config :: !(XConfig Layout) -- ^ initial user configuration
@@ -73,43 +73,45 @@ data XConf = XConf
-- todo, better name -- todo, better name
data XConfig l = XConfig data XConfig l = XConfig
{ normalBorderColor :: !String -- ^ Non focused windows border color. Default: \"#dddddd\" { normalBorderColor :: !String -- ^ Non focused windows border color. Default: \"#dddddd\"
, focusedBorderColor :: !String -- ^ Focused windows border color. Default: \"#ff0000\" , focusedBorderColor :: !String -- ^ Focused windows border color. Default: \"#ff0000\"
, terminal :: !String -- ^ The preferred terminal application. Default: \"xterm\" , terminal :: !String -- ^ The preferred terminal application. Default: \"xterm\"
, layoutHook :: !(l Window) -- ^ The avaiable layouts , layoutHook :: !(l Window) -- ^ The available layouts
, manageHook :: !ManageHook , manageHook :: !ManageHook -- ^ The action to run when a new window is opened
-- ^ The action to run when a new window is opened , workspaces :: ![String] -- ^ The list of workspaces' names
, workspaces :: [String] -- ^ The list of workspaces' names , defaultGaps :: ![(Int,Int,Int,Int)] -- ^ The list of gaps, per screen
, defaultGaps :: [(Int,Int,Int,Int)] -- ^ The list of gaps, per screen , numlockMask :: !KeyMask -- ^ The numlock modifier
, 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 , mouseBindings :: !(XConfig Layout -> M.Map (ButtonMask, Button) (Window -> X ()))
, mouseBindings :: XConfig Layout -> M.Map (ButtonMask, Button) (Window -> X ()) -- ^ The mouse bindings
-- ^ The mouse bindings , borderWidth :: !Dimension -- ^ The border width
, borderWidth :: !Dimension -- ^ The border width , logHook :: !(X ()) -- ^ The action to perform when the windows set is changed
, logHook :: X () -- ^ The action to perform when the windows set is changed , startupHook :: !(X ()) -- ^ The action to perform on startup
, focusFollowsMouse :: !Bool -- ^ Whether window entry events can change focus
} }
type WindowSet = StackSet WorkspaceId (Layout Window) Window ScreenId ScreenDetail type WindowSet = StackSet WorkspaceId (Layout Window) Window ScreenId ScreenDetail
type WindowSpace = Workspace WorkspaceId (Layout Window) Window type WindowSpace = Workspace WorkspaceId (Layout Window) Window
-- | Virtual workspace indicies -- | Virtual workspace indices
type WorkspaceId = String type WorkspaceId = String
-- | Physical screen indicies -- | Physical screen indices
newtype ScreenId = S Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real) newtype ScreenId = S Int deriving (Eq,Ord,Show,Read,Enum,Num,Integral,Real)
-- | The 'Rectangle' with screen dimensions and the list of gaps -- | The 'Rectangle' with screen dimensions and the list of gaps
data ScreenDetail = SD { screenRect :: !Rectangle data ScreenDetail = SD { screenRect :: !Rectangle
, statusGap :: !(Int,Int,Int,Int) -- ^ width of status bar on the screen , statusGap :: !(Int,Int,Int,Int) -- ^ gaps on the sides of the screen that shouldn't be tiled, usually for status bars
} deriving (Eq,Show, Read) } deriving (Eq,Show, Read)
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- | The X monad, a StateT transformer over IO encapsulating the window -- | The X monad, ReaderT and StateT transformers over IO
-- manager state -- encapsulating the window manager configuration and state,
-- respectively.
-- --
-- Dynamic components may be retrieved with 'get', static components -- Dynamic components may be retrieved with 'get', static components
-- with 'ask'. With newtype deriving we get readers and state monads -- with 'ask'. With newtype deriving we get readers and state monads
@@ -120,6 +122,10 @@ newtype X a = X (ReaderT XConf (StateT XState IO) a)
deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf) deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf)
#endif #endif
instance Applicative X where
pure = return
(<*>) = ap
instance (Monoid a) => Monoid (X a) where instance (Monoid a) => Monoid (X a) where
mempty = return mempty mempty = return mempty
mappend = liftM2 mappend mappend = liftM2 mappend
@@ -130,8 +136,8 @@ newtype Query a = Query (ReaderT Window X a)
deriving (Functor, Monad, MonadReader Window, MonadIO) deriving (Functor, Monad, MonadReader Window, MonadIO)
#endif #endif
runManageHook :: ManageHook -> Window -> X (WindowSet -> WindowSet) runQuery :: Query a -> Window -> X a
runManageHook (Query m) w = appEndo <$> runReaderT m w runQuery (Query m) w = runReaderT m w
instance Monoid a => Monoid (Query a) where instance Monoid a => Monoid (Query a) where
mempty = return mempty mempty = return mempty
@@ -185,91 +191,135 @@ atom_WM_DELETE_WINDOW = getAtom "WM_DELETE_WINDOW"
atom_WM_STATE = getAtom "WM_STATE" atom_WM_STATE = getAtom "WM_STATE"
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- | LayoutClass handling. See particular instances in Operations.hs -- LayoutClass handling. See particular instances in Operations.hs
-- | An existential type that can hold any object that is in Read and LayoutClass. -- | An existential type that can hold any object that is in 'Read'
-- and 'LayoutClass'.
data Layout a = forall l. (LayoutClass l a, Read (l a)) => Layout (l a) data Layout a = forall l. (LayoutClass l a, Read (l a)) => Layout (l a)
-- | Using the 'Layout' as a witness, parse existentially wrapped windows -- | Using the 'Layout' as a witness, parse existentially wrapped windows
-- from a 'String' -- from a 'String'.
readsLayout :: Layout a -> String -> [(Layout a, String)] readsLayout :: Layout a -> String -> [(Layout a, String)]
readsLayout (Layout l) s = [(Layout (asTypeOf x l), rs) | (x, rs) <- reads s] readsLayout (Layout l) s = [(Layout (asTypeOf x l), rs) | (x, rs) <- reads s]
-- | The different layout modes -- | Every layout must be an instance of 'LayoutClass', which defines
-- the basic layout operations along with a sensible default for each.
-- --
-- 'doLayout': given a Rectangle and a Stack, layout the stack elements -- Minimal complete definition:
-- inside the given Rectangle. If an element is not given a Rectangle
-- by 'doLayout', then it is not shown on screen. Windows are restacked
-- according to the order they are returned by 'doLayout'.
-- --
-- * 'runLayout' || (('doLayout' || 'pureLayout') && 'emptyLayout'), and
--
-- * 'handleMessage' || 'pureMessage'
--
-- You should also strongly consider implementing 'description',
-- although it is not required.
--
-- Note that any code which /uses/ 'LayoutClass' methods should only
-- ever call 'runLayout', 'handleMessage', and 'description'! In
-- other words, the only calls to 'doLayout', 'pureMessage', and other
-- such methods should be from the default implementations of
-- 'runLayout', 'handleMessage', and so on. This ensures that the
-- proper methods will be used, regardless of the particular methods
-- that any 'LayoutClass' instance chooses to define.
class Show (layout a) => LayoutClass layout a where class Show (layout a) => LayoutClass layout a where
-- | Given a Rectangle in which to place the windows, and a Stack of -- | By default, 'runLayout' calls 'doLayout' if there are any
-- windows, return a list of windows and their corresponding Rectangles. -- windows to be laid out, and 'emptyLayout' otherwise. Most
-- The order of windows in this list should be the desired stacking order. -- instances of 'LayoutClass' probably do not need to implement
-- Also return a modified layout, if this layout needs to be modified -- 'runLayout'; it is only useful for layouts which wish to make
-- (e.g. if we keep track of the windows we have displayed). -- use of more of the 'Workspace' information (for example,
doLayout :: layout a -> Rectangle -> Stack a -> X ([(a, Rectangle)], Maybe (layout a)) -- "XMonad.Layout.PerWorkspace").
runLayout :: Workspace WorkspaceId (layout a) a
-> Rectangle
-> X ([(a, Rectangle)], Maybe (layout a))
runLayout (Workspace _ l ms) r = maybe (emptyLayout l r) (doLayout l r) ms
-- | Given a 'Rectangle' in which to place the windows, and a 'Stack'
-- of windows, return a list of windows and their corresponding
-- Rectangles. If an element is not given a Rectangle by
-- 'doLayout', then it is not shown on screen. The order of
-- windows in this list should be the desired stacking order.
--
-- Also possibly return a modified layout (by returning @Just
-- newLayout@), if this layout needs to be modified (e.g. if it
-- keeps track of some sort of state). Return @Nothing@ if the
-- layout does not need to be modified.
--
-- Layouts which do not need access to the 'X' monad ('IO', window
-- manager state, or configuration) and do not keep track of their
-- own state should implement 'pureLayout' instead of 'doLayout'.
doLayout :: layout a -> Rectangle -> Stack a
-> X ([(a, Rectangle)], Maybe (layout a))
doLayout l r s = return (pureLayout l r s, Nothing) doLayout l r s = return (pureLayout l r s, Nothing)
-- | This is a pure version of doLayout, for cases where we don't need -- | This is a pure version of 'doLayout', for cases where we
-- access to the X monad to determine how to layout the windows, and -- don't need access to the 'X' monad to determine how to lay out
-- we don't need to modify our layout itself. -- the windows, and we don't need to modify the layout itself.
pureLayout :: layout a -> Rectangle -> Stack a -> [(a, Rectangle)] pureLayout :: layout a -> Rectangle -> Stack a -> [(a, Rectangle)]
pureLayout _ r s = [(focus s, r)] pureLayout _ r s = [(focus s, r)]
-- | 'handleMessage' performs message handling for that layout. If -- | 'emptyLayout' is called when there are no windows.
-- 'handleMessage' returns Nothing, then the layout did not respond to emptyLayout :: layout a -> Rectangle -> X ([(a, Rectangle)], Maybe (layout a))
-- that message and the screen is not refreshed. Otherwise, 'handleMessage' emptyLayout _ _ = return ([], Nothing)
-- returns an updated 'Layout' and the screen is refreshed.
-- | 'handleMessage' performs message handling. If
-- 'handleMessage' returns @Nothing@, then the layout did not
-- respond to the message and the screen is not refreshed.
-- Otherwise, 'handleMessage' returns an updated layout and the
-- screen is refreshed.
-- --
-- Layouts which do not need access to the 'X' monad to decide how
-- to handle messages should implement 'pureMessage' instead of
-- 'handleMessage' (this restricts the risk of error, and makes
-- testing much easier).
handleMessage :: layout a -> SomeMessage -> X (Maybe (layout a)) handleMessage :: layout a -> SomeMessage -> X (Maybe (layout a))
handleMessage l = return . pureMessage l handleMessage l = return . pureMessage l
-- | Respond to a message by (possibly) changing our layout, but taking -- | Respond to a message by (possibly) changing our layout, but
-- no other action. If the layout changes, the screen will be refreshed. -- taking no other action. If the layout changes, the screen will
-- be refreshed.
pureMessage :: layout a -> SomeMessage -> Maybe (layout a) pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
pureMessage _ _ = Nothing pureMessage _ _ = Nothing
-- | This should be a human-readable string that is used when selecting -- | This should be a human-readable string that is used when
-- layouts by name. -- selecting layouts by name. The default implementation is
-- 'show', which is in some cases a poor default.
description :: layout a -> String description :: layout a -> String
description = show description = show
instance LayoutClass Layout Window where instance LayoutClass Layout Window where
runLayout (Workspace i (Layout l) ms) r = fmap (fmap Layout) `fmap` runLayout (Workspace i l ms) r
doLayout (Layout l) r s = fmap (fmap Layout) `fmap` doLayout l r s doLayout (Layout l) r s = fmap (fmap Layout) `fmap` doLayout l r s
emptyLayout (Layout l) r = fmap (fmap Layout) `fmap` emptyLayout l r
handleMessage (Layout l) = fmap (fmap Layout) . handleMessage l handleMessage (Layout l) = fmap (fmap Layout) . handleMessage l
description (Layout l) = description l description (Layout l) = description l
instance Show (Layout a) where show (Layout l) = show l instance Show (Layout a) where show (Layout l) = show l
-- | This calls doLayout if there are any windows to be laid out. -- | Based on ideas in /An Extensible Dynamically-Typed Hierarchy of
runLayout :: LayoutClass l a => l a -> Rectangle -> Maybe (Stack a) -> X ([(a, Rectangle)], Maybe (l a)) -- Exceptions/, Simon Marlow, 2006. Use extensible messages to the
runLayout l r = maybe (return ([], Nothing)) (doLayout l r) -- 'handleMessage' handler.
-- | Based on ideas in /An Extensible Dynamically-Typed Hierarchy of Exceptions/,
-- Simon Marlow, 2006. Use extensible messages to the handleMessage handler.
-- --
-- User-extensible messages must be a member of this class. -- User-extensible messages must be a member of this class.
-- --
class Typeable a => Message a class Typeable a => Message a
-- | -- |
-- A wrapped value of some type in the Message class. -- A wrapped value of some type in the 'Message' class.
-- --
data SomeMessage = forall a. Message a => SomeMessage a data SomeMessage = forall a. Message a => SomeMessage a
-- | -- |
-- And now, unwrap a given, unknown Message type, performing a (dynamic) -- And now, unwrap a given, unknown 'Message' type, performing a (dynamic)
-- type check on the result. -- type check on the result.
-- --
fromMessage :: Message m => SomeMessage -> Maybe m fromMessage :: Message m => SomeMessage -> Maybe m
fromMessage (SomeMessage m) = cast m fromMessage (SomeMessage m) = cast m
-- | X Events are valid Messages -- X Events are valid Messages.
instance Message Event instance Message Event
-- | LayoutMessages are core messages that all layouts (especially stateful -- | 'LayoutMessages' are core messages that all layouts (especially stateful
-- layouts) should consider handling. -- layouts) should consider handling.
data LayoutMessages = Hide -- ^ sent when a layout becomes non-visible data LayoutMessages = Hide -- ^ sent when a layout becomes non-visible
| ReleaseResources -- ^ sent when xmonad is exiting or restarting | ReleaseResources -- ^ sent when xmonad is exiting or restarting
@@ -286,7 +336,7 @@ 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 :: IO () -> X () catchIO :: MonadIO m => IO () -> m ()
catchIO f = io (f `catch` \e -> hPrint stderr e >> hFlush stderr) catchIO f = io (f `catch` \e -> hPrint stderr e >> hFlush stderr)
-- | spawn. Launch an external application -- | spawn. Launch an external application
@@ -303,26 +353,21 @@ doubleFork m = io $ do
getProcessStatus True False pid getProcessStatus True False pid
return () return ()
-- | Restart xmonad via exec(). -- | 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.
-- If the first parameter is 'Just name', restart will attempt to execute the runOnWorkspaces :: (WindowSpace -> X WindowSpace) -> X ()
-- program corresponding to 'name'. Otherwise, xmonad will attempt to execute runOnWorkspaces job = do
-- the name of the current program. ws <- gets windowset
-- h <- mapM job $ hidden ws
-- When the second parameter is 'True', xmonad will attempt to resume with the c:v <- mapM (\s -> (\w -> s { workspace = w}) <$> job (workspace s))
-- current window state. $ current ws : visible ws
restart :: Maybe String -> Bool -> X () modify $ \s -> s { windowset = ws { current = c, visible = v, hidden = h } }
restart mprog resume = do
prog <- maybe (io getProgName) return mprog
args <- if resume then gets (("--resume":) . return . showWs . windowset) else return []
catchIO (executeFile prog True args Nothing)
where showWs = show . mapLayout show
-- | Return the path to @~\/.xmonad@. -- | Return the path to @~\/.xmonad@.
getXMonadDir :: MonadIO m => m String getXMonadDir :: MonadIO m => m String
getXMonadDir = io $ getAppUserDataDirectory "xmonad" getXMonadDir = io $ getAppUserDataDirectory "xmonad"
-- | 'recompile force', recompile ~\/.xmonad\/xmonad.hs when any of the -- | 'recompile force', recompile @~\/.xmonad\/xmonad.hs@ when any of the
-- following apply: -- following apply:
-- * force is True -- * force is True
-- * the xmonad executable does not exist -- * the xmonad executable does not exist
@@ -331,20 +376,25 @@ getXMonadDir = io $ getAppUserDataDirectory "xmonad"
-- 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.
-- --
-- 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 containing -- GHC indicates failure with a non-zero exit code, an xmessage displaying
-- GHC's is spawned. -- that file is spawned.
-- --
recompile :: MonadIO m => Bool -> m () -- False is returned if there are compilation errors.
--
recompile :: MonadIO m => Bool -> m Bool
recompile force = io $ do recompile force = io $ do
dir <- getXMonadDir dir <- getXMonadDir
let bin = dir ++ "/" ++ "xmonad" let binn = "xmonad-"++arch++"-"++os
err = bin ++ ".errors" bin = dir ++ "/" ++ binn
src = bin ++ ".hs" base = dir ++ "/" ++ "xmonad"
err = base ++ ".errors"
src = base ++ ".hs"
srcT <- getModTime src srcT <- getModTime src
binT <- getModTime bin binT <- getModTime bin
when (force || srcT > binT) $ do if (force || srcT > binT)
then do
status <- bracket (openFile err WriteMode) hClose $ \h -> do status <- bracket (openFile err WriteMode) hClose $ \h -> do
waitForProcess =<< runProcess "ghc" ["--make", "xmonad.hs", "-i", "-no-recomp", "-v0"] (Just dir) waitForProcess =<< runProcess "ghc" ["--make", "xmonad.hs", "-i", "-no-recomp", "-v0", "-o",binn] (Just dir)
Nothing Nothing Nothing (Just h) Nothing Nothing Nothing (Just h)
-- now, if it fails, run xmessage to let the user know: -- now, if it fails, run xmessage to let the user know:
@@ -353,10 +403,15 @@ recompile force = io $ do
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 ghcErr ++ ["","Please check the file for errors."]
doubleFork $ executeFile "xmessage" True [msg] Nothing -- nb, the ordering of printing, then forking, is crucial due to
-- lazy evaluation
hPutStrLn stderr msg
doubleFork $ executeFile "xmessage" True ["-default", "okay", msg] Nothing
return (status == ExitSuccess)
else return True
where getModTime f = catch (Just <$> getModificationTime f) (const $ return Nothing) where getModTime f = catch (Just <$> getModificationTime f) (const $ return Nothing)
-- | Run a side effecting action with the current workspace. Like 'when' but -- | 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 ()
whenJust mg f = maybe (return ()) f mg whenJust mg f = maybe (return ()) f mg

View File

@@ -3,7 +3,7 @@
-- -------------------------------------------------------------------------- -- --------------------------------------------------------------------------
-- | -- |
-- Module : Layouts.hs -- Module : XMonad.Layout
-- Copyright : (c) Spencer Janssen 2007 -- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE) -- License : BSD3-style (see LICENSE)
-- --
@@ -15,9 +15,14 @@
-- --
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
module XMonad.Layout (ChangeLayout(..), Choose, (|||), Resize(..), IncMasterN(..), module XMonad.Layout (
Full(..), Tall(..), Mirror(..), mirrorRect, splitVertically, ChangeLayout(..), Choose, (|||), Resize(..), IncMasterN(..),
splitHorizontally, splitHorizontallyBy, splitVerticallyBy) where Full(..), Tall(..), Mirror(..), mirrorRect, splitVertically,
splitHorizontally, splitHorizontallyBy, splitVerticallyBy,
tile
) where
import XMonad.Core import XMonad.Core
@@ -27,64 +32,8 @@ import Control.Arrow ((***), second)
import Control.Monad import Control.Monad
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- LayoutClass selection manager -- | Builtin basic layout algorithms:
-- | A layout that allows users to switch between various layout options.
-- | Messages to change the current layout.
data ChangeLayout = FirstLayout | NextLayout deriving (Eq, Show, Typeable)
instance Message ChangeLayout
-- | The layout choice combinator
(|||) :: (LayoutClass l a, LayoutClass r a) => l a -> r a -> Choose l r a
(|||) = flip SLeft
infixr 5 |||
data Choose l r a = SLeft (r a) (l a)
| SRight (l a) (r a) deriving (Read, Show)
data NextNoWrap = NextNoWrap deriving (Eq, Show, Typeable)
instance Message NextNoWrap
-- This has lots of pseudo duplicated code, we must find a better way
instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
doLayout (SLeft r l) = (fmap (second . fmap $ SLeft r) .) . doLayout l
doLayout (SRight l r) = (fmap (second . fmap $ SRight l) .) . doLayout r
description (SLeft _ l) = description l
description (SRight _ r) = description r
handleMessage lr m | Just FirstLayout <- fromMessage m = case lr of
SLeft {} -> return Nothing
SRight l r -> fmap (Just . flip SLeft l . fromMaybe r)
$ handleMessage r (SomeMessage Hide)
handleMessage lr m | Just NextLayout <- fromMessage m = do
mlr <- handleMessage lr $ SomeMessage NextNoWrap
maybe (handleMessage lr $ SomeMessage FirstLayout) (return . Just) mlr
handleMessage (SLeft r l) m | Just NextNoWrap <- fromMessage m = do
handleMessage l (SomeMessage Hide)
mr <- handleMessage r (SomeMessage FirstLayout)
return . Just . SRight l $ fromMaybe r mr
handleMessage lr m | Just ReleaseResources <- fromMessage m =
liftM2 ((Just .) . cons)
(fmap (fromMaybe l) $ handleMessage l m)
(fmap (fromMaybe r) $ handleMessage r m)
where (cons, l, r) = case lr of
(SLeft r' l') -> (flip SLeft, l', r')
(SRight l' r') -> (SRight, l', r')
-- The default cases for left and right:
handleMessage (SLeft r l) m = fmap (fmap $ SLeft r) $ handleMessage l m
handleMessage (SRight l r) m = fmap (fmap $ SRight l) $ handleMessage r m
--
-- | Builtin layout algorithms:
-- --
-- > fullscreen mode -- > fullscreen mode
-- > tall mode -- > tall mode
@@ -97,7 +46,7 @@ instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
data Resize = Shrink | Expand deriving Typeable data Resize = Shrink | Expand deriving Typeable
-- | You can also increase the number of clients in the master pane -- | You can also increase the number of clients in the master pane
data IncMasterN = IncMasterN Int deriving Typeable data IncMasterN = IncMasterN !Int deriving Typeable
instance Message Resize instance Message Resize
instance Message IncMasterN instance Message IncMasterN
@@ -107,34 +56,26 @@ data Full a = Full deriving (Show, Read)
instance LayoutClass Full a instance LayoutClass Full a
-- | The inbuilt tiling mode of xmonad, and its operations. -- | The builtin tiling mode of xmonad, and its operations.
data Tall a = Tall Int Rational Rational deriving (Show, Read) data Tall a = Tall !Int !Rational !Rational deriving (Show, Read)
-- TODO should be capped [0..1] ..
-- a nice pure layout, lots of properties for the layout, and its messages, in Properties.hs
instance LayoutClass Tall a where instance LayoutClass Tall a where
pureLayout (Tall nmaster _ frac) r s = zip ws rs pureLayout (Tall nmaster _ frac) r s = zip ws rs
where ws = W.integrate s where ws = W.integrate s
rs = tile frac r nmaster (length ws) rs = tile frac r nmaster (length ws)
pureMessage (Tall nmaster delta frac) m = msum [fmap resize (fromMessage m) pureMessage (Tall nmaster delta frac) m =
,fmap incmastern (fromMessage m)] msum [fmap resize (fromMessage m)
where resize Shrink = Tall nmaster delta (max 0 $ frac-delta) ,fmap incmastern (fromMessage m)]
resize Expand = Tall nmaster delta (min 1 $ frac+delta)
incmastern (IncMasterN d) = Tall (max 0 (nmaster+d)) delta frac where resize Shrink = Tall nmaster delta (max 0 $ frac-delta)
resize Expand = Tall nmaster delta (min 1 $ frac+delta)
incmastern (IncMasterN d) = Tall (max 0 (nmaster+d)) delta frac
description _ = "Tall" description _ = "Tall"
-- | Mirror a rectangle
mirrorRect :: Rectangle -> Rectangle
mirrorRect (Rectangle rx ry rw rh) = (Rectangle ry rx rh rw)
-- | Mirror a layout, compute its 90 degree rotated form.
data Mirror l a = Mirror (l a) deriving (Show, Read)
instance LayoutClass l a => LayoutClass (Mirror l) a where
doLayout (Mirror l) r s = (map (second mirrorRect) *** fmap Mirror)
`fmap` doLayout l (mirrorRect r) s
handleMessage (Mirror l) = fmap (fmap Mirror) . handleMessage l
description (Mirror l) = "Mirror "++ description l
-- | tile. Compute the positions for windows using the default 2 pane tiling algorithm. -- | tile. Compute the positions for windows using the default 2 pane tiling algorithm.
-- --
-- The screen is divided (currently) into two panes. all clients are -- The screen is divided (currently) into two panes. all clients are
@@ -163,6 +104,7 @@ splitVertically n (Rectangle sx sy sw sh) = Rectangle sx sy sw smallh :
splitVertically (n-1) (Rectangle sx (sy+fromIntegral smallh) sw (sh-smallh)) splitVertically (n-1) (Rectangle sx (sy+fromIntegral smallh) sw (sh-smallh))
where smallh = sh `div` fromIntegral n --hmm, this is a fold or map. where smallh = sh `div` fromIntegral n --hmm, this is a fold or map.
-- Not used in the core, but exported
splitHorizontally n = map mirrorRect . splitVertically n . mirrorRect splitHorizontally n = map mirrorRect . splitVertically n . mirrorRect
-- Divide the screen into two rectangles, using a rational to specify the ratio -- Divide the screen into two rectangles, using a rational to specify the ratio
@@ -172,4 +114,79 @@ splitHorizontallyBy f (Rectangle sx sy sw sh) =
, Rectangle (sx + fromIntegral leftw) sy (sw-fromIntegral leftw) sh) , Rectangle (sx + fromIntegral leftw) sy (sw-fromIntegral leftw) sh)
where leftw = floor $ fromIntegral sw * f where leftw = floor $ fromIntegral sw * f
-- Not used in the core, but exported
splitVerticallyBy f = (mirrorRect *** mirrorRect) . splitHorizontallyBy f . mirrorRect splitVerticallyBy f = (mirrorRect *** mirrorRect) . splitHorizontallyBy f . mirrorRect
------------------------------------------------------------------------
-- | Mirror a layout, compute its 90 degree rotated form.
-- | Mirror a layout, compute its 90 degree rotated form.
data Mirror l a = Mirror (l a) deriving (Show, Read)
instance LayoutClass l a => LayoutClass (Mirror l) a where
runLayout (W.Workspace i (Mirror l) ms) r = (map (second mirrorRect) *** fmap Mirror)
`fmap` runLayout (W.Workspace i l ms) (mirrorRect r)
handleMessage (Mirror l) = fmap (fmap Mirror) . handleMessage l
description (Mirror l) = "Mirror "++ description l
-- | Mirror a rectangle
mirrorRect :: Rectangle -> Rectangle
mirrorRect (Rectangle rx ry rw rh) = (Rectangle ry rx rh rw)
------------------------------------------------------------------------
-- LayoutClass selection manager
-- Layouts that transition between other layouts
-- | A layout that allows users to switch between various layout options.
-- | Messages to change the current layout.
data ChangeLayout = FirstLayout | NextLayout deriving (Eq, Show, Typeable)
instance Message ChangeLayout
-- | The layout choice combinator
(|||) :: (LayoutClass l a, LayoutClass r a) => l a -> r a -> Choose l r a
(|||) = flip SLeft
infixr 5 |||
data Choose l r a = SLeft (r a) (l a)
| SRight (l a) (r a) deriving (Read, Show)
data NextNoWrap = NextNoWrap deriving (Eq, Show, Typeable)
instance Message NextNoWrap
-- This has lots of pseudo duplicated code, we must find a better way
instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
runLayout (W.Workspace i (SLeft r l) ms) =
fmap (second . fmap $ SLeft r) . runLayout (W.Workspace i l ms)
runLayout (W.Workspace i (SRight l r) ms) =
fmap (second . fmap $ SRight l) . runLayout (W.Workspace i r ms)
description (SLeft _ l) = description l
description (SRight _ r) = description r
handleMessage lr m | Just FirstLayout <- fromMessage m = case lr of
SLeft {} -> return Nothing
SRight l r -> fmap (Just . flip SLeft l . fromMaybe r)
$ handleMessage r (SomeMessage Hide)
handleMessage lr m | Just NextLayout <- fromMessage m = do
mlr <- handleMessage lr $ SomeMessage NextNoWrap
maybe (handleMessage lr $ SomeMessage FirstLayout) (return . Just) mlr
handleMessage (SLeft r l) m | Just NextNoWrap <- fromMessage m = do
handleMessage l (SomeMessage Hide)
mr <- handleMessage r (SomeMessage FirstLayout)
return . Just . SRight l $ fromMaybe r mr
handleMessage lr m | Just ReleaseResources <- fromMessage m =
liftM2 ((Just .) . cons)
(fmap (fromMaybe l) $ handleMessage l m)
(fmap (fromMaybe r) $ handleMessage r m)
where (cons, l, r) = case lr of
(SLeft r' l') -> (flip SLeft, l', r')
(SRight l' r') -> (SRight, l', r')
-- The default cases for left and right:
handleMessage (SLeft r l) m = fmap (fmap $ SLeft r) $ handleMessage l m
handleMessage (SRight l r) m = fmap (fmap $ SRight l) $ handleMessage r m

View File

@@ -1,7 +1,7 @@
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses, FlexibleContexts #-}
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
-- | -- |
-- Module : Core.hs -- Module : XMonad.Main
-- Copyright : (c) Spencer Janssen 2007 -- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE) -- License : BSD3-style (see LICENSE)
-- --
@@ -16,6 +16,7 @@
module XMonad.Main (xmonad) where module XMonad.Main (xmonad) where
import Data.Bits import Data.Bits
import Data.List ((\\))
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
@@ -23,12 +24,13 @@ import Control.Monad.State
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
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
import Graphics.X11.Xinerama (getScreenInfo)
import XMonad.Core import XMonad.Core
import qualified XMonad.Config as Default
import XMonad.StackSet (new, floating, member) import XMonad.StackSet (new, floating, member)
import qualified XMonad.StackSet as W import qualified XMonad.StackSet as W
import XMonad.Operations import XMonad.Operations
@@ -40,15 +42,23 @@ import System.IO
-- --
xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO () xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
xmonad initxmc = do xmonad initxmc = do
-- ignore SIGPIPE
installHandler openEndedPipe Ignore Nothing
-- 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
xinesc <- getScreenInfo dpy xinesc <- getCleanedScreenInfo dpy
nbc <- initColor dpy $ normalBorderColor xmc nbc <- do v <- initColor dpy $ normalBorderColor xmc
fbc <- initColor dpy $ focusedBorderColor xmc ~(Just nbc_) <- initColor dpy $ normalBorderColor Default.defaultConfig
return (fromMaybe nbc_ v)
fbc <- do v <- initColor dpy $ focusedBorderColor xmc
~(Just fbc_) <- initColor dpy $ focusedBorderColor Default.defaultConfig
return (fromMaybe fbc_ v)
hSetBuffering stdout NoBuffering hSetBuffering stdout NoBuffering
args <- getArgs args <- getArgs
@@ -97,15 +107,19 @@ xmonad initxmc = do
io $ sync dpy False io $ sync dpy False
ws <- io $ scan dpy rootw
-- bootstrap the windowset, Operations.windows will identify all -- bootstrap the windowset, Operations.windows will identify all
-- the windows in winset as new and set initial properties for -- the windows in winset as new and set initial properties for
-- those windows -- those windows. Remove all windows that are no longer top-level
windows (const winset) -- children of the root, they may have disappeared since
-- restarting.
windows . const . foldr W.delete winset $ W.allWindows winset \\ ws
-- scan for all top-level windows, add the unmanaged ones to the -- manage the as-yet-unmanaged windows
-- windowset mapM_ manage (ws \\ W.allWindows winset)
ws <- io $ scan dpy rootw
mapM_ manage ws 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_ $ handle =<< io (nextEvent dpy e >> getEvent e)
@@ -143,7 +157,10 @@ handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
-- window destroyed, unmanage it -- window destroyed, unmanage it
-- window gone, unmanage it -- window gone, unmanage it
handle (DestroyWindowEvent {ev_window = w}) = whenX (isClient w) $ unmanage w handle (DestroyWindowEvent {ev_window = w}) = whenX (isClient w) $ do
unmanage w
modify (\s -> s { mapped = S.delete w (mapped s)
, waitingUnmap = M.delete w (waitingUnmap s)})
-- We track expected unmap events in waitingUnmap. We ignore this event unless -- We track expected unmap events in waitingUnmap. We ignore this event unless
-- it is synthetic or we are not expecting an unmap notification from a window. -- it is synthetic or we are not expecting an unmap notification from a window.
@@ -151,7 +168,9 @@ handle (UnmapEvent {ev_window = w, ev_send_event = synthetic}) = whenX (isClient
e <- gets (fromMaybe 0 . M.lookup w . waitingUnmap) e <- gets (fromMaybe 0 . M.lookup w . waitingUnmap)
if (synthetic || e == 0) if (synthetic || e == 0)
then unmanage w then unmanage w
else modify (\s -> s { waitingUnmap = M.adjust pred w (waitingUnmap s) }) else modify (\s -> s { waitingUnmap = M.update mpred w (waitingUnmap s) })
where mpred 1 = Nothing
mpred n = Just $ pred n
-- set keyboard mapping -- set keyboard mapping
handle e@(MappingNotifyEvent {}) = do handle e@(MappingNotifyEvent {}) = do
@@ -184,12 +203,14 @@ handle e@(ButtonEvent {ev_window = w,ev_event_type = t,ev_button = b })
ba <- asks buttonActions ba <- asks buttonActions
if isr then userCode $ whenJust (M.lookup (m, b) ba) ($ ev_subwindow e) if isr then userCode $ whenJust (M.lookup (m, b) ba) ($ ev_subwindow e)
else focus w else focus w
sendMessage e -- Always send button events. broadcastMessage e -- Always send button events.
-- entered a normal window, makes this focused. -- entered a normal window: focus it if focusFollowsMouse is set to
-- True in the user's config.
handle e@(CrossingEvent {ev_window = w, ev_event_type = t}) 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 = focus w && ev_detail e /= notifyInferior
= 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
handle e@(CrossingEvent {ev_event_type = t}) handle e@(CrossingEvent {ev_event_type = t})

View File

@@ -2,7 +2,7 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- | -- |
-- Module : XMonad/ManageHook.hs -- Module : XMonad.ManageHook
-- Copyright : (c) Spencer Janssen 2007 -- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE) -- License : BSD3-style (see LICENSE)
-- --
@@ -20,6 +20,7 @@ module XMonad.ManageHook where
import XMonad.Core import XMonad.Core
import Graphics.X11.Xlib.Extras import Graphics.X11.Xlib.Extras
import Graphics.X11.Xlib (Display,Window)
import Control.Monad.Reader import Control.Monad.Reader
import Data.Maybe import Data.Maybe
import Data.Monoid import Data.Monoid
@@ -65,6 +66,17 @@ title = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap (fromMaybe "") $ io
resource = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resName $ io $ getClassHint d w) resource = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resName $ io $ getClassHint d w)
className = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resClass $ io $ getClassHint d w) className = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap resClass $ io $ getClassHint d w)
-- | A query that can return an arbitrary X property of type String,
-- identified by name.
stringProperty :: String -> Query String
stringProperty p = ask >>= (\w -> liftX $ withDisplay $ \d -> fmap (fromMaybe "") $ getStringProperty d w p)
getStringProperty :: Display -> Window -> String -> X (Maybe String)
getStringProperty d w p = do
a <- getAtom p
md <- io $ getWindowProperty8 d a w
return $ fmap (map (toEnum . fromIntegral)) md
-- | Modify the 'WindowSet' with a pure function. -- | Modify the 'WindowSet' with a pure function.
doF :: (WindowSet -> WindowSet) -> ManageHook doF :: (WindowSet -> WindowSet) -> ManageHook
doF = return . Endo doF = return . Endo

View File

@@ -4,7 +4,7 @@
-- -------------------------------------------------------------------------- -- --------------------------------------------------------------------------
-- | -- |
-- Module : Operations.hs -- Module : XMonad.Operations
-- Copyright : (c) Spencer Janssen 2007 -- Copyright : (c) Spencer Janssen 2007
-- License : BSD3-style (see LICENSE) -- License : BSD3-style (see LICENSE)
-- --
@@ -23,6 +23,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.List (nub, (\\), find) import Data.List (nub, (\\), find)
import Data.Bits ((.|.), (.&.), complement) import Data.Bits ((.|.), (.&.), complement)
import Data.Ratio import Data.Ratio
@@ -30,10 +31,12 @@ import qualified Data.Map as M
import qualified Data.Set as S import qualified Data.Set as S
import Control.Applicative import Control.Applicative
import Control.Monad.State
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.State
import qualified Control.Exception as C
import System.IO import System.IO
import System.Posix.Process (executeFile)
import Graphics.X11.Xlib import Graphics.X11.Xlib
import Graphics.X11.Xinerama (getScreenInfo) import Graphics.X11.Xinerama (getScreenInfo)
import Graphics.X11.Xlib.Extras import Graphics.X11.Xlib.Extras
@@ -65,19 +68,14 @@ manage w = whenX (not <$> isClient w) $ withDisplay $ \d -> do
where i = fromMaybe (W.tag . W.workspace . W.current $ ws) $ W.lookupWorkspace sc ws where i = fromMaybe (W.tag . W.workspace . W.current $ ws) $ W.lookupWorkspace sc ws
mh <- asks (manageHook . config) mh <- asks (manageHook . config)
g <- runManageHook mh w `catchX` return id g <- fmap appEndo (runQuery mh w) `catchX` return id
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
-- list, on whatever workspace it is. -- list, on whatever workspace it is.
-- --
-- should also unmap?
--
unmanage :: Window -> X () unmanage :: Window -> X ()
unmanage w = do unmanage = windows . W.delete
windows (W.delete w)
setWMState w withdrawnState
modify (\s -> s {mapped = S.delete w (mapped s), waitingUnmap = M.delete w (waitingUnmap s)})
-- | Modify the size of the status gap at the top of the current screen -- | Modify the size of the status gap at the top of the current screen
-- Taking a function giving the current screen, and current geometry. -- Taking a function giving the current screen, and current geometry.
@@ -116,23 +114,25 @@ windows f = do
let oldvisible = concatMap (W.integrate' . W.stack . W.workspace) $ W.current old : W.visible old let oldvisible = concatMap (W.integrate' . W.stack . W.workspace) $ W.current old : W.visible old
ws = f old ws = f old
XConf { display = d , normalBorder = nbc, focusedBorder = fbc } <- ask XConf { display = d , normalBorder = nbc, focusedBorder = fbc } <- ask
mapM_ setInitialProperties (W.allWindows ws \\ W.allWindows old) mapM_ setInitialProperties (W.allWindows ws \\ W.allWindows old)
whenJust (W.peek old) $ \otherw -> io $ setWindowBorder d otherw nbc whenJust (W.peek old) $ \otherw -> io $ setWindowBorder d otherw nbc
modify (\s -> s { windowset = ws }) modify (\s -> s { windowset = ws })
-- notify non visibility -- notify non visibility
let tags_oldvisible = map (W.tag . W.workspace) $ W.current old : W.visible old let tags_oldvisible = map (W.tag . W.workspace) $ W.current old : W.visible old
gottenhidden = filter (`elem` tags_oldvisible) $ map W.tag $ W.hidden ws gottenhidden = filter (flip elem tags_oldvisible . W.tag) $ W.hidden ws
sendMessageToWorkspaces Hide gottenhidden mapM_ (sendMessageWithNoRefresh Hide) gottenhidden
-- 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 visible <- fmap concat $ forM (zip allscreens summed_visible) $ \ (w, vis) -> do
let n = W.tag (W.workspace w) let wsp = W.workspace w
this = W.view n ws this = W.view n ws
l = W.layout (W.workspace w) n = W.tag wsp
flt = filter (flip M.member (W.floating ws)) (W.index this) 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)
@@ -143,11 +143,10 @@ windows f = do
-- 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 l viewrect tiled `catchX` runLayout (Layout Full) viewrect tiled (rs, ml') <- runLayout wsp { W.stack = tiled } viewrect `catchX`
runLayout wsp { W.stack = tiled, W.layout = Layout Full } viewrect
mapM_ (uncurry tileWindow) rs mapM_ (uncurry tileWindow) rs
whenJust ml' $ \l' -> runOnWorkspaces (\ww -> if W.tag ww == n updateLayout n ml'
then return $ ww { W.layout = l'}
else return ww)
-- now the floating windows: -- now the floating windows:
-- move/resize the floating windows, if there are any -- move/resize the floating windows, if there are any
@@ -165,12 +164,16 @@ windows f = do
whenJust (W.peek ws) $ \w -> io $ setWindowBorder d w fbc whenJust (W.peek ws) $ \w -> io $ setWindowBorder d w fbc
setTopFocus setTopFocus
asks (logHook . config) >>= userCode asks (logHook . config) >>= userCode
-- io performGC -- really helps, but seems to trigger GC bugs?
-- hide every window that was potentially visible before, but is not -- hide every window that was potentially visible before, but is not
-- given a position by a layout now. -- given a position by a layout now.
mapM_ hide (nub oldvisible \\ visible) mapM_ hide (nub oldvisible \\ visible)
-- all windows that are no longer in the windowset are marked as
-- withdrawn, it is important to do this after the above, otherwise 'hide'
-- will overwrite withdrawnState with iconicState
mapM_ (flip setWMState withdrawnState) (W.allWindows old \\ W.allWindows ws)
clearEvents enterWindowMask clearEvents enterWindowMask
-- | setWMState. set the WM_STATE property -- | setWMState. set the WM_STATE property
@@ -245,11 +248,31 @@ tileWindow w r = withDisplay $ \d -> do
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- | Returns True if the first rectangle is contained within, but not equal
-- to the second.
containedIn :: Rectangle -> Rectangle -> Bool
containedIn r1@(Rectangle x1 y1 w1 h1) r2@(Rectangle x2 y2 w2 h2)
= and [ r1 /= r2
, x1 >= x2
, y1 >= y2
, fromIntegral x1 + w1 <= fromIntegral x2 + w2
, fromIntegral y1 + h1 <= fromIntegral y2 + h2 ]
-- | Given a list of screens, remove all duplicated screens and screens that
-- are entirely contained within another.
nubScreens :: [Rectangle] -> [Rectangle]
nubScreens xs = nub . filter (\x -> not $ any (x `containedIn`) xs) $ xs
-- | Cleans the list of screens according to the rules documented for
-- nubScreens.
getCleanedScreenInfo :: MonadIO m => Display -> m [Rectangle]
getCleanedScreenInfo = io . fmap nubScreens . getScreenInfo
-- | rescreen. The screen configuration may have changed (due to -- | rescreen. The screen configuration may have changed (due to
-- xrandr), update the state and refresh the screen, and reset the gap. -- xrandr), update the state and refresh the screen, and reset the gap.
rescreen :: X () rescreen :: X ()
rescreen = do rescreen = do
xinesc <- withDisplay (io . getScreenInfo) xinesc <- withDisplay getCleanedScreenInfo
windows $ \ws@(W.StackSet { W.current = v, W.visible = vs, W.hidden = hs }) -> windows $ \ws@(W.StackSet { W.current = v, W.visible = vs, W.hidden = hs }) ->
let (xs, ys) = splitAt (length xinesc) $ map W.workspace (v:vs) ++ hs let (xs, ys) = splitAt (length xinesc) $ map W.workspace (v:vs) ++ hs
@@ -315,30 +338,24 @@ sendMessage a = do
{ W.workspace = (W.workspace $ W.current ws) { W.workspace = (W.workspace $ W.current ws)
{ W.layout = l' }}} { W.layout = l' }}}
-- | Send a message to a list of workspaces' layouts, without necessarily refreshing. -- | Send a message to all layouts, without refreshing.
sendMessageToWorkspaces :: Message a => a -> [WorkspaceId] -> X ()
sendMessageToWorkspaces a l = runOnWorkspaces $ \w ->
if W.tag w `elem` l
then do ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
return $ w { W.layout = maybe (W.layout w) id ml' }
else return w
-- | Send a message to all visible layouts, without necessarily refreshing.
-- This is how we implement the hooks, such as UnDoLayout.
broadcastMessage :: Message a => a -> X () broadcastMessage :: Message a => a -> X ()
broadcastMessage a = runOnWorkspaces $ \w -> do broadcastMessage a = withWindowSet $ \ws -> do
ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing let c = W.workspace . W.current $ ws
return $ w { W.layout = maybe (W.layout w) id ml' } v = map W.workspace . W.visible $ ws
h = W.hidden ws
mapM_ (sendMessageWithNoRefresh a) (c : v ++ h)
-- | This is basically a map function, running a function in the X monad on -- | Send a message to a layout, without refreshing.
-- each workspace with the output of that function being the modified workspace. sendMessageWithNoRefresh :: Message a => a -> W.Workspace WorkspaceId (Layout Window) Window -> X ()
runOnWorkspaces :: (WindowSpace -> X WindowSpace) -> X () sendMessageWithNoRefresh a w =
runOnWorkspaces job =do handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing >>=
ws <- gets windowset updateLayout (W.tag w)
h <- mapM job $ W.hidden ws
c:v <- mapM (\s -> (\w -> s { W.workspace = w}) <$> job (W.workspace s)) -- | Update the layout field of a workspace
$ W.current ws : W.visible ws updateLayout :: WorkspaceId -> Maybe (Layout Window) -> X ()
modify $ \s -> s { windowset = ws { W.current = c, W.visible = v, W.hidden = h } } updateLayout i ml = whenJust ml $ \l ->
runOnWorkspaces $ \ww -> return $ if W.tag ww == i then ww { W.layout = l} else ww
-- | Set the layout of the currently viewed workspace -- | Set the layout of the currently viewed workspace
setLayout :: Layout Window -> X () setLayout :: Layout Window -> X ()
@@ -376,10 +393,24 @@ cleanMask km = do
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 Pixel initColor :: Display -> String -> IO (Maybe Pixel)
initColor dpy c = (color_pixel . fst) <$> allocNamedColor dpy colormap c initColor dpy c = C.handle (\_ -> return Nothing) $
(Just . color_pixel . fst) <$> allocNamedColor dpy colormap c
where colormap = defaultColormap dpy (defaultScreen dpy) where colormap = defaultColormap dpy (defaultScreen dpy)
------------------------------------------------------------------------
-- | @restart name resume@. Attempt to restart xmonad by executing the program
-- @name@. If @resume@ is 'True', restart with the current window state.
-- When executing another window manager, @resume@ should be 'False'.
restart :: String -> Bool -> X ()
restart prog resume = do
broadcastMessage ReleaseResources
io . flush =<< asks display
args <- if resume then gets (("--resume":) . return . showWs . windowset) else return []
catchIO (executeFile prog True args Nothing)
where showWs = show . W.mapLayout show
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- | Floating layer support -- | Floating layer support
@@ -412,9 +443,9 @@ float :: Window -> X ()
float w = do float w = do
(sc, rr) <- floatLocation w (sc, rr) <- floatLocation w
windows $ \ws -> W.float w rr . fromMaybe ws $ do windows $ \ws -> W.float w rr . fromMaybe ws $ do
i <- W.findTag w ws i <- W.findTag w ws
guard $ i `elem` map (W.tag . W.workspace) (W.screens ws) guard $ i `elem` map (W.tag . W.workspace) (W.screens ws)
f <- W.peek ws f <- W.peek ws
sw <- W.lookupWorkspace sc ws sw <- W.lookupWorkspace sc ws
return (W.focusWindow f . W.shiftWin sw w $ ws) return (W.focusWindow f . W.shiftWin sw w $ ws)

View File

@@ -2,7 +2,7 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- | -- |
-- Module : StackSet -- Module : XMonad.StackSet
-- Copyright : (c) Don Stewart 2007 -- Copyright : (c) Don Stewart 2007
-- License : BSD3-style (see LICENSE) -- License : BSD3-style (see LICENSE)
-- --
@@ -112,7 +112,7 @@ import qualified Data.Map as M (Map,insert,delete,empty)
-- viewable. We thus need to track which virtual workspaces are -- viewable. We thus need to track which virtual workspaces are
-- associated (viewed) on which physical screens. To keep track of -- associated (viewed) on which physical screens. To keep track of
-- this, StackSet keeps separate lists of visible but non-focused -- this, StackSet keeps separate lists of visible but non-focused
-- workspaces, and non-visible workspaces. -- workspaces, and non-visible workspaces.
-- $focus -- $focus
-- --
@@ -122,38 +122,6 @@ import qualified Data.Map as M (Map,insert,delete,empty)
-- 'delete'. -- 'delete'.
-- --
-- |
-- API changes from xmonad 0.1:
-- StackSet constructor arguments changed. StackSet workspace window screen
--
-- * new, -- was: empty
--
-- * view,
--
-- * index,
--
-- * peek, -- was: peek\/peekStack
--
-- * focusUp, focusDown, -- was: rotate
--
-- * swapUp, swapDown
--
-- * focus -- was: raiseFocus
--
-- * insertUp, -- was: insert\/push
--
-- * delete,
--
-- * swapMaster, -- was: promote\/swap
--
-- * member,
--
-- * shift,
--
-- * lookupWorkspace, -- was: workspace
--
-- * visibleWorkspaces -- gone.
--
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- | -- |
-- A cursor into a non-empty list of workspaces. -- A cursor into a non-empty list of workspaces.
@@ -177,7 +145,7 @@ data Screen i l a sid sd = Screen { workspace :: !(Workspace i l a)
deriving (Show, Read, Eq) deriving (Show, Read, Eq)
-- | -- |
-- A workspace is just a tag - its index - and a stack -- A workspace is just a tag, a layout, and a stack.
-- --
data Workspace i l a = Workspace { tag :: !i, layout :: l, stack :: Maybe (Stack a) } data Workspace i l a = Workspace { tag :: !i, layout :: l, stack :: Maybe (Stack a) }
deriving (Show, Read, Eq) deriving (Show, Read, Eq)
@@ -242,8 +210,7 @@ new _ _ _ = abort "non-positive argument to StackSet.new"
view :: (Eq s, Eq i) => i -> StackSet i l a s sd -> StackSet i l a s sd view :: (Eq s, Eq i) => i -> StackSet i l a s sd -> StackSet i l a s sd
view i s view i s
| not (i `tagMember` s) | i == tag (workspace (current s)) = s -- current
|| i == tag (workspace (current s)) = s -- out of bounds or current
| Just x <- L.find ((i==).tag.workspace) (visible s) | Just x <- L.find ((i==).tag.workspace) (visible s)
-- if it is visible, it is just raised -- if it is visible, it is just raised
@@ -254,7 +221,7 @@ view i s
= s { current = (current s) { workspace = x } = s { current = (current s) { workspace = x }
, hidden = workspace (current s) : L.deleteBy (equating tag) x (hidden s) } , hidden = workspace (current s) : L.deleteBy (equating tag) x (hidden s) }
| otherwise = s -- can't happen: all workspaces are either invalid, current, visible, or hidden | otherwise = s -- not a member of the stackset
where equating f = \x y -> f x == f y where equating f = \x y -> f x == f y
@@ -333,7 +300,7 @@ integrate (Stack x l r) = reverse l ++ x : r
integrate' :: Maybe (Stack a) -> [a] integrate' :: Maybe (Stack a) -> [a]
integrate' = maybe [] integrate integrate' = maybe [] integrate
-- | -- |
-- /O(n)/. Turn a list into a possibly empty stack (i.e., a zipper): -- /O(n)/. Turn a list into a possibly empty stack (i.e., a zipper):
-- the first element of the list is current, and the rest of the list -- the first element of the list is current, and the rest of the list
-- is down. -- is down.
@@ -446,7 +413,7 @@ mapLayout f (StackSet v vs hs m) = StackSet (fScreen v) (map fScreen vs) (map fW
fScreen (Screen ws s sd) = Screen (fWorkspace ws) s sd fScreen (Screen ws s sd) = Screen (fWorkspace ws) s sd
fWorkspace (Workspace t l s) = Workspace t (f l) s fWorkspace (Workspace t l s) = Workspace t (f l) s
-- | /O(n)/. Is a window in the StackSet. -- | /O(n)/. Is a window in the StackSet?
member :: Eq a => a -> StackSet i l a s sd -> Bool member :: Eq a => a -> StackSet i l a s sd -> Bool
member a s = isJust (findTag a s) member a s = isJust (findTag a s)

View File

@@ -9,32 +9,23 @@ xmonad \- a tiling window manager
.PP .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. 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 .PP
By utilising the expressivity of a modern functional language with a rich static type system, \fBxmonad\fR provides a complete, featureful window manager in less than 500 lines of code, with an emphasis on correctness and robustness. Internal properties of the window manager are checked using a combination of static guarantees provided by the type system, and type-based automated testing. A benefit of this is that the code is simple to understand, and easy to modify. By utilising the expressivity of a modern functional language with a rich static type system, \fBxmonad\fR provides a complete, featureful window manager in less than 1200 lines of code, with an emphasis on correctness and robustness. Internal properties of the window manager are checked using a combination of static guarantees provided by the type system, and type-based automated testing. A benefit of this is that the code is simple to understand, and easy to modify.
.SH USAGE .SH USAGE
.PP .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. \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 .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. 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 .PP
When running with multiple monitors (Xinerama), each screen has exactly 1 workspace visible. When \fBxmonad\fR starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. If you switch to a workspace which is currently visible on another screen, \fBxmonad\fR simply switches focus to that screen. If you switch to a workspace which is *not* visible, \fBxmonad\fR replaces the workspace on the *current* screen with the workspace you selected. When running with multiple monitors (Xinerama), each screen has exactly 1 workspace visible. mod-{w,e,r} switch the focus between screens, while shift-mod-{w,e,r} move the current window to that screen. When \fBxmonad\fR starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. When switching workspaces to one that is already visible, the current and visible workspaces are swapped.
.PP .PP
For example, if you have the following configuration: .SS Flags
.RS \fBxmonad\fR has several flags which you may pass to the executable. These flags are:
.PP .TP
Screen 1: Workspace 2 \fB--recompile
.PP Recompiles your configuration in ~/.xmonad/xmonad.hs
Screen 2: Workspace 5 (current workspace) .TP
.RE \fB--version
.PP Display version of \fBxmonad\fR.
and you wanted to view workspace 7 on screen 1, you would press:
.RS
.PP
mod-2 (to select workspace 2, and make screen 1 the current screen)
.PP
mod-7 (to select workspace 7)
.RE
.PP
Since switching to the workspace currently visible on a given screen is such a common operation, shortcuts are provided: mod-{w,e,r} switch to the workspace currently visible on screens 1, 2, and 3 respectively. Likewise, shift-mod-{w,e,r} moves the current window to the workspace on that screen. Using these keys, the above example would become mod-w mod-7.
.SS Default keyboard bindings .SS Default keyboard bindings
___KEYBINDINGS___ ___KEYBINDINGS___
.SH EXAMPLES .SH EXAMPLES
@@ -44,6 +35,6 @@ xmonad
.RE .RE
to your \fI~/.xinitrc\fR file to your \fI~/.xinitrc\fR file
.SH CUSTOMIZATION .SH CUSTOMIZATION
\fBxmonad\fR is customized by creating a custom Config.hs and (re)compiling the source code. After recompiling, 'restart' is used to fork the new version, with changes reflected immediately. \fBxmonad\fR is customized in ~/.xmonad/xmonad.hs, and then restarting with mod-q.
.SH BUGS .SH BUGS
Probably. If you find any, please report them: http://code.google.com/p/xmonad/issues/list Probably. If you find any, please report them: http://code.google.com/p/xmonad/issues/list

View File

@@ -145,8 +145,7 @@ myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) , ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess))
-- Restart xmonad -- Restart xmonad
, ((modMask , xK_q ), , ((modMask , xK_q ), restart "xmonad" True)
broadcastMessage ReleaseResources >> restart (Just "xmonad") True)
] ]
++ ++
@@ -222,12 +221,19 @@ myLayout = tiled ||| Mirror tiled ||| Full
-- > xprop | grep WM_CLASS -- > xprop | grep WM_CLASS
-- and click on the client you're interested in. -- and click on the client you're interested in.
-- --
-- To match on the WM_NAME, you can use 'title' in the same way that
-- 'className' and 'resource' are used below.
--
myManageHook = composeAll myManageHook = composeAll
[ className =? "MPlayer" --> doFloat [ className =? "MPlayer" --> doFloat
, className =? "Gimp" --> doFloat , className =? "Gimp" --> doFloat
, resource =? "desktop_window" --> doIgnore , resource =? "desktop_window" --> doIgnore
, resource =? "kdesktop" --> doIgnore ] , resource =? "kdesktop" --> doIgnore ]
-- Whether focus follows the mouse pointer.
myFocusFollowsMouse :: Bool
myFocusFollowsMouse = True
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Status bars and logging -- Status bars and logging
@@ -241,6 +247,16 @@ myManageHook = composeAll
-- --
myLogHook = return () myLogHook = return ()
------------------------------------------------------------------------
-- Startup hook
-- Perform an arbitrary action each time xmonad starts or is restarted
-- with mod-q. Used by, e.g., XMonad.Layout.PerWorkspace to initialize
-- per-workspace layout choices.
--
-- By default, do nothing.
myStartupHook = return ()
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Now run xmonad with all the defaults we set up. -- Now run xmonad with all the defaults we set up.
@@ -257,6 +273,7 @@ main = xmonad defaults
defaults = defaultConfig { defaults = defaultConfig {
-- simple stuff -- simple stuff
terminal = myTerminal, terminal = myTerminal,
focusFollowsMouse = myFocusFollowsMouse,
borderWidth = myBorderWidth, borderWidth = myBorderWidth,
modMask = myModMask, modMask = myModMask,
numlockMask = myNumlockMask, numlockMask = myNumlockMask,
@@ -272,5 +289,6 @@ defaults = defaultConfig {
-- hooks, layouts -- hooks, layouts
layoutHook = myLayout, layoutHook = myLayout,
manageHook = myManageHook, manageHook = myManageHook,
logHook = myLogHook logHook = myLogHook,
startupHook = myStartupHook
} }

View File

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

View File

@@ -1,7 +1,10 @@
{-# OPTIONS -fglasgow-exts #-} {-# OPTIONS -fglasgow-exts -w #-}
module Properties where module Properties where
import XMonad.StackSet hiding (filter) import XMonad.StackSet hiding (filter)
import XMonad.Layout
import XMonad.Core hiding (workspaces,trace)
import XMonad.Operations ( applyResizeIncHint, applyMaxSizeHint )
import qualified XMonad.StackSet as S (filter) import qualified XMonad.StackSet as S (filter)
import Debug.Trace import Debug.Trace
@@ -52,7 +55,6 @@ instance (Integral i, Integral s, Eq a, Arbitrary a, Arbitrary l, Arbitrary sd)
| s <- ls ] | s <- ls ]
return $ fromList (fromIntegral n, sds,fs,ls,lay) return $ fromList (fromIntegral n, sds,fs,ls,lay)
coarbitrary = error "no coarbitrary for StackSet"
-- | fromList. Build a new StackSet from a list of list of elements, -- | fromList. Build a new StackSet from a list of list of elements,
@@ -137,10 +139,10 @@ prop_empty_I (n :: Positive Int) l = forAll (choose (1,fromIntegral n)) $ \m -
invariant $ new l [0..fromIntegral n-1] ms invariant $ new l [0..fromIntegral n-1] ms
prop_view_I (n :: NonNegative Int) (x :: T) = prop_view_I (n :: NonNegative Int) (x :: T) =
n `tagMember` x ==> invariant $ view (fromIntegral n) x invariant $ view (fromIntegral n) x
prop_greedyView_I (n :: NonNegative Int) (x :: T) = prop_greedyView_I (n :: NonNegative Int) (x :: T) =
n `tagMember` x ==> invariant $ view (fromIntegral n) x invariant $ greedyView (fromIntegral n) x
prop_focusUp_I (n :: NonNegative Int) (x :: T) = prop_focusUp_I (n :: NonNegative Int) (x :: T) =
invariant $ foldr (const focusUp) x [1..n] invariant $ foldr (const focusUp) x [1..n]
@@ -237,6 +239,13 @@ prop_greedyView_current (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
where where
i = fromIntegral n i = fromIntegral n
-- greedyView leaves things unchanged for invalid workspaces
prop_greedyView_current_id (x :: T) (n :: NonNegative Int) = not (i `tagMember` x) ==>
tag (workspace $ current (greedyView i x)) == j
where
i = fromIntegral n
j = tag (workspace (current x))
-- greedyView *only* sets the current workspace, and touches Xinerama. -- greedyView *only* sets the current workspace, and touches Xinerama.
-- no workspace contents will be changed. -- no workspace contents will be changed.
prop_greedyView_local (x :: T) (n :: NonNegative Int) = i `tagMember` x ==> prop_greedyView_local (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
@@ -349,6 +358,10 @@ prop_focusWindow_local (n :: NonNegative Int) (x::T ) =
i = fromIntegral n `mod` length s i = fromIntegral n `mod` length s
in hidden_spaces (focusWindow (s !! i) x) == hidden_spaces x in hidden_spaces (focusWindow (s !! i) x) == hidden_spaces x
-- On an invalid window, the stackset is unmodified
prop_focusWindow_identity (n :: Char) (x::T ) =
not (n `member` x) ==> focusWindow n x == x
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- member/findTag -- member/findTag
@@ -541,13 +554,19 @@ prop_float_reversible n (x :: T) =
where where
geom = RationalRect 100 100 100 100 geom = RationalRect 100 100 100 100
-- check rectanges were set prop_float_geometry n (x :: T) =
{- n `member` x ==> let s = float n geom x
prop_float_sets_geometry n (x :: T) = in M.lookup n (floating s) == Just geom
n `member` x ==> let y = float n geom x in M.lookup y (floating x) == Just geom
where where
geom = RationalRect 100 100 100 100 geom = RationalRect 100 100 100 100
-}
prop_float_delete n (x :: T) =
n `member` x ==> let s = float n geom x
t = delete n s
in not (n `member` t)
where
geom = RationalRect 100 100 100 100
------------------------------------------------------------------------ ------------------------------------------------------------------------
@@ -606,9 +625,26 @@ prop_rename1 (x::T) o n = o `tagMember` x && not (n `tagMember` x) ==>
let y = renameTag o n x let y = renameTag o n x
in n `tagMember` y in n `tagMember` y
-- |
-- Ensure that a given set of workspace tags is present by renaming
-- existing workspaces and\/or creating new hidden workspaces as
-- necessary.
--
prop_ensure (x :: T) l xs = let y = ensureTags l xs x prop_ensure (x :: T) l xs = let y = ensureTags l xs x
in and [ n `tagMember` y | n <- xs ] in and [ n `tagMember` y | n <- xs ]
-- adding a tag should create a new hidden workspace
prop_ensure_append (x :: T) l n =
not (n `tagMember` x)
==>
(hidden y /= hidden x -- doesn't append, renames
&&
and [ isNothing (stack z) && layout z == l | z <- hidden y, tag z == n ]
)
where
y = ensureTags l (n:ts) x
ts = [ tag z | z <- workspaces x ]
prop_mapWorkspaceId (x::T) = x == mapWorkspace id x prop_mapWorkspaceId (x::T) = x == mapWorkspace id x
prop_mapWorkspaceInverse (x::T) = x == mapWorkspace predTag (mapWorkspace succTag x) prop_mapWorkspaceInverse (x::T) = x == mapWorkspace predTag (mapWorkspace succTag x)
@@ -620,17 +656,145 @@ prop_mapLayoutId (x::T) = x == mapLayout id x
prop_mapLayoutInverse (x::T) = x == mapLayout pred (mapLayout succ x) prop_mapLayoutInverse (x::T) = x == mapLayout pred (mapLayout succ x)
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- some properties for layouts: -- The Tall layout
-- 1 window should always be tiled fullscreen -- 1 window should always be tiled fullscreen
{-
prop_tile_fullscreen rect = tile pct rect 1 1 == [rect] prop_tile_fullscreen rect = tile pct rect 1 1 == [rect]
where pct = 1/2
-- multiple windows -- multiple windows
prop_tile_non_overlap rect windows nmaster = noOverlaps (tile pct rect nmaster windows) prop_tile_non_overlap rect windows nmaster = noOverlaps (tile pct rect nmaster windows)
where _ = rect :: Rectangle where _ = rect :: Rectangle
pct = 3 % 100
pct = 3 % 100 -- splitting horizontally yields sensible results
prop_split_hoziontal (NonNegative n) x =
{-
trace (show (rect_x x
,rect_width x
,rect_x x + fromIntegral (rect_width x)
,map rect_x xs))
$
-}
sum (map rect_width xs) == rect_width x
&&
all (== rect_height x) (map rect_height xs)
&&
(map rect_x xs) == (sort $ map rect_x xs)
where
xs = splitHorizontally n x
-- splitting horizontally yields sensible results
prop_splitVertically (r :: Rational) x =
rect_x x == rect_x a && rect_x x == rect_x b
&&
rect_width x == rect_width a && rect_width x == rect_width b
{-
trace (show (rect_x x
,rect_width x
,rect_x x + fromIntegral (rect_width x)
,map rect_x xs))
$
-}
where
(a,b) = splitVerticallyBy r x
-- pureLayout works.
prop_purelayout_tall n r1 r2 rect (t :: T) =
isJust (peek t) ==>
length ts == length (index t)
&&
noOverlaps (map snd ts)
&&
description layoot == "Tall"
where layoot = Tall n r1 r2
st = fromJust . stack . workspace . current $ t
ts = pureLayout layoot rect st
-- Test message handling of Tall
-- what happens when we send a Shrink message to Tall
prop_shrink_tall (NonNegative n) (NonZero (NonNegative delta)) (NonNegative frac) =
n == n' && delta == delta' -- these state components are unchanged
&& frac' <= frac && (if frac' < frac then frac' == 0 || frac' == frac - delta
else frac == 0 )
-- remaining fraction should shrink
where
l1 = Tall n delta frac
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage Shrink)
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
-- what happens when we send a Shrink message to Tall
prop_expand_tall (NonNegative n)
(NonZero (NonNegative delta))
(NonNegative n1)
(NonZero (NonNegative d1)) =
n == n'
&& delta == delta' -- these state components are unchanged
&& frac' >= frac
&& (if frac' > frac
then frac' == 1 || frac' == frac + delta
else frac == 1 )
-- remaining fraction should shrink
where
frac = min 1 (n1 % d1)
l1 = Tall n delta frac
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage Expand)
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
-- what happens when we send an IncMaster message to Tall
prop_incmaster_tall (NonNegative n) (NonZero (NonNegative delta)) (NonNegative frac)
(NonNegative k) =
delta == delta' && frac == frac' && n' == n + k
where
l1 = Tall n delta frac
Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage (IncMasterN k))
-- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
-- toMessage LT = SomeMessage Shrink
-- toMessage EQ = SomeMessage Expand
-- toMessage GT = SomeMessage (IncMasterN 1)
------------------------------------------------------------------------
-- Full layout
-- pureLayout works for Full
prop_purelayout_full rect (t :: T) =
isJust (peek t) ==>
length ts == 1 -- only one window to view
&&
snd (head ts) == rect -- and sets fullscreen
&&
fst (head ts) == fromJust (peek t) -- and the focused window is shown
where layoot = Full
st = fromJust . stack . workspace . current $ t
ts = pureLayout layoot rect st
-- what happens when we send an IncMaster message to Full --- Nothing
prop_sendmsg_full (NonNegative k) =
isNothing (Full `pureMessage` (SomeMessage (IncMasterN k)))
prop_desc_full = description Full == show Full
------------------------------------------------------------------------
prop_desc_mirror n r1 r2 = description (Mirror $! t) == "Mirror Tall"
where t = Tall n r1 r2
------------------------------------------------------------------------
noOverlaps [] = True noOverlaps [] = True
noOverlaps [_] = True noOverlaps [_] = True
@@ -646,15 +810,36 @@ noOverlaps xs = and [ verts a `notOverlap` verts b
= (top1 < bottom2 || top2 < bottom1) = (top1 < bottom2 || top2 < bottom1)
|| (right1 < left2 || right2 < left1) || (right1 < left2 || right2 < left1)
-} ------------------------------------------------------------------------
-- Aspect ratios
prop_resize_inc (NonZero (NonNegative inc_w),NonZero (NonNegative inc_h)) b@(w,h) =
w' `mod` inc_w == 0 && h' `mod` inc_h == 0
where (w',h') = applyResizeIncHint a b
a = (inc_w,inc_h)
prop_resize_inc_extra ((NonNegative inc_w)) b@(w,h) =
(w,h) == (w',h')
where (w',h') = applyResizeIncHint a b
a = (-inc_w,0::Dimension)-- inc_h)
prop_resize_max (NonZero (NonNegative inc_w),NonZero (NonNegative inc_h)) b@(w,h) =
w' <= inc_w && h' <= inc_h
where (w',h') = applyMaxSizeHint a b
a = (inc_w,inc_h)
prop_resize_max_extra ((NonNegative inc_w)) b@(w,h) =
(w,h) == (w',h')
where (w',h') = applyMaxSizeHint a b
a = (-inc_w,0::Dimension)-- inc_h)
------------------------------------------------------------------------ ------------------------------------------------------------------------
main :: IO () main :: IO ()
main = do main = do
args <- getArgs args <- fmap (drop 1) getArgs
let n = if null args then 100 else read (head args) let n = if null args then 100 else read (head args)
(results, passed) <- liftM unzip $ mapM (\(s,a) -> printf "%-25s: " s >> a n) tests (results, passed) <- liftM unzip $ mapM (\(s,a) -> printf "%-40s: " s >> a n) tests
printf "Passed %d tests!\n" (sum passed) printf "Passed %d tests!\n" (sum passed)
when (not . and $ results) $ fail "Not all tests passed!" when (not . and $ results) $ fail "Not all tests passed!"
where where
@@ -676,6 +861,7 @@ main = do
,("greedyView : invariant" , mytest prop_greedyView_I) ,("greedyView : invariant" , mytest prop_greedyView_I)
,("greedyView sets current" , mytest prop_greedyView_current) ,("greedyView sets current" , mytest prop_greedyView_current)
,("greedyView is safe " , mytest prop_greedyView_current_id)
,("greedyView idempotent" , mytest prop_greedyView_idem) ,("greedyView idempotent" , mytest prop_greedyView_idem)
,("greedyView reversible" , mytest prop_greedyView_reversible) ,("greedyView reversible" , mytest prop_greedyView_reversible)
,("greedyView is local" , mytest prop_greedyView_local) ,("greedyView is local" , mytest prop_greedyView_local)
@@ -705,6 +891,7 @@ main = do
,("focusWindow is local", mytest prop_focusWindow_local) ,("focusWindow is local", mytest prop_focusWindow_local)
,("focusWindow works" , mytest prop_focusWindow_works) ,("focusWindow works" , mytest prop_focusWindow_works)
,("focusWindow identity", mytest prop_focusWindow_identity)
,("findTag" , mytest prop_findIndex) ,("findTag" , mytest prop_findIndex)
,("allWindows/member" , mytest prop_allWindowsMember) ,("allWindows/member" , mytest prop_allWindowsMember)
@@ -749,13 +936,17 @@ main = do
,("shiftWin fix current" , mytest prop_shift_win_fix_current) ,("shiftWin fix current" , mytest prop_shift_win_fix_current)
,("floating is reversible" , mytest prop_float_reversible) ,("floating is reversible" , mytest prop_float_reversible)
,("floating sets geometry" , mytest prop_float_geometry)
,("floats can be deleted", mytest prop_float_delete)
,("screens includes current", mytest prop_screens) ,("screens includes current", mytest prop_screens)
,("differentiate works", mytest prop_differentiate) ,("differentiate works", mytest prop_differentiate)
,("lookupTagOnScreen", mytest prop_lookup_current) ,("lookupTagOnScreen", mytest prop_lookup_current)
,("lookupTagOnVisbleScreen", mytest prop_lookup_visible) ,("lookupTagOnVisbleScreen", mytest prop_lookup_visible)
,("screens works", mytest prop_screens_works) ,("screens works", mytest prop_screens_works)
,("renaming works", mytest prop_rename1) ,("renaming works", mytest prop_rename1)
,("ensure works", mytest prop_ensure) ,("ensure works", mytest prop_ensure)
,("ensure hidden semantics", mytest prop_ensure_append)
,("mapWorkspace id", mytest prop_mapWorkspaceId) ,("mapWorkspace id", mytest prop_mapWorkspaceId)
,("mapWorkspace inverse", mytest prop_mapWorkspaceInverse) ,("mapWorkspace inverse", mytest prop_mapWorkspaceInverse)
@@ -767,12 +958,31 @@ main = do
,("new fails with abort", mytest prop_new_abort) ,("new fails with abort", mytest prop_new_abort)
,("shiftWin identity", mytest prop_shift_win_indentity) ,("shiftWin identity", mytest prop_shift_win_indentity)
-- renaming -- tall layout
{-
,("tile 1 window fullsize", mytest prop_tile_fullscreen) ,("tile 1 window fullsize", mytest prop_tile_fullscreen)
,("tiles never overlap", mytest prop_tile_non_overlap) ,("tiles never overlap", mytest prop_tile_non_overlap)
-} ,("split hozizontally", mytest prop_split_hoziontal)
,("split verticalBy", mytest prop_splitVertically)
,("pure layout tall", mytest prop_purelayout_tall)
,("send shrink tall", mytest prop_shrink_tall)
,("send expand tall", mytest prop_expand_tall)
,("send incmaster tall", mytest prop_incmaster_tall)
-- full layout
,("pure layout full", mytest prop_purelayout_full)
,("send message full", mytest prop_sendmsg_full)
,("describe full", mytest prop_desc_full)
,("describe mirror", mytest prop_desc_mirror)
-- resize hints
,("window hints: inc", mytest prop_resize_inc)
,("window hints: inc all", mytest prop_resize_inc_extra)
,("window hints: max", mytest prop_resize_max)
,("window hints: max all ", mytest prop_resize_max_extra)
] ]
@@ -941,6 +1151,7 @@ instance Arbitrary EmptyStackSet where
l <- arbitrary l <- arbitrary
-- there cannot be more screens than workspaces: -- there cannot be more screens than workspaces:
return . EmptyStackSet . new l ns $ take (min (length ns) (length sds)) sds return . EmptyStackSet . new l ns $ take (min (length ns) (length sds)) sds
coarbitrary = error "coarbitrary EmptyStackSet"
-- | Generates a value that satisfies a predicate. -- | Generates a value that satisfies a predicate.
suchThat :: Gen a -> (a -> Bool) -> Gen a suchThat :: Gen a -> (a -> Bool) -> Gen a

10
tests/coverage.hs Normal file
View File

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

View File

@@ -1,5 +1,5 @@
name: xmonad name: xmonad
version: 0.5 version: 0.7
homepage: http://xmonad.org homepage: http://xmonad.org
synopsis: A tiling window manager synopsis: A tiling window manager
description: description:
@@ -21,9 +21,14 @@ 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.in man/xmonad.1 man/xmonad.html man/xmonad.hs
util/GenerateManpage.hs util/GenerateManpage.hs
cabal-version: >= 1.2 cabal-version: >= 1.2
build-type: Simple
flag small_base flag small_base
description: Choose the new smaller, split-up base package. description: Choose the new smaller, split-up base package.
flag testing
description: Testing mode, only build minimal components
default: False
library library
exposed-modules: XMonad exposed-modules: XMonad
@@ -39,17 +44,34 @@ library
build-depends: base >= 3, containers, directory, process build-depends: base >= 3, containers, directory, process
else else
build-depends: base < 3 build-depends: base < 3
build-depends: X11>=1.4.0, mtl, unix build-depends: X11>=1.4.1, mtl, unix
ghc-options: -funbox-strict-fields -Wall -Werror -optl-Wl,-s ghc-options: -funbox-strict-fields -Wall -optl-Wl,-s
ghc-prof-options: -prof -auto-all ghc-prof-options: -prof -auto-all
extensions: CPP extensions: CPP
if flag(testing)
buildable: False
executable xmonad executable xmonad
main-is: Main.hs main-is: Main.hs
other-modules: XMonad.Core XMonad.Main XMonad.Layout other-modules: XMonad
XMonad.Operations XMonad.StackSet XMonad XMonad.Main
XMonad.Core
XMonad.Config
XMonad.Layout
XMonad.ManageHook
XMonad.Operations
XMonad.StackSet
ghc-options: -funbox-strict-fields -Wall -Werror -optl-Wl,-s ghc-options: -funbox-strict-fields -Wall -optl-Wl,-s
ghc-prof-options: -prof -auto-all ghc-prof-options: -prof -auto-all
extensions: CPP extensions: CPP
if flag(testing)
cpp-options: -DTESTING
hs-source-dirs: . tests/
build-depends: QuickCheck < 2
ghc-options: -Werror
if flag(testing) && flag(small_base)
build-depends: random