mirror of
https://github.com/xmonad/xmonad.git
synced 2025-07-31 12:11:53 -07:00
Compare commits
111 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4be3b39cd2 | ||
|
75889ab62e | ||
|
792add376e | ||
|
87c50a911f | ||
|
d16aa9975e | ||
|
f34642cbac | ||
|
008c3638a5 | ||
|
f5c40e9e12 | ||
|
bd82cc9150 | ||
|
a025912ab7 | ||
|
19c1759b35 | ||
|
92acd1eb74 | ||
|
db9f39d6af | ||
|
ebcd67efac | ||
|
387a253f62 | ||
|
4c83e8e097 | ||
|
ae59a5184f | ||
|
fa8fe9aca4 | ||
|
673c3e9ed9 | ||
|
6ba45cdb38 | ||
|
b995b430bc | ||
|
ba482a4611 | ||
|
684907bc77 | ||
|
ad4136df26 | ||
|
defe0c282e | ||
|
c7bdac1a7e | ||
|
17799f131a | ||
|
8cd66aa380 | ||
|
32ba0d4a0d | ||
|
77b3f62610 | ||
|
f3b07eb5dc | ||
|
4372c256ed | ||
|
34239a79de | ||
|
5866db4f0f | ||
|
46d039cde5 | ||
|
dd22717961 | ||
|
0beeb4164b | ||
|
0b435028ff | ||
|
84a988da82 | ||
|
dbd739e41e | ||
|
d5d8d551e6 | ||
|
a2ba4d8a6c | ||
|
557d3edb7d | ||
|
262db2367f | ||
|
eddb445307 | ||
|
d5aadf2538 | ||
|
a16bb44934 | ||
|
2b854ee47c | ||
|
02ed1cabdc | ||
|
02693d307c | ||
|
37dc284460 | ||
|
73e406f4a6 | ||
|
44bc9558d9 | ||
|
0eb84e4866 | ||
|
b4bf8de874 | ||
|
17c89e327e | ||
|
da71b6c8ac | ||
|
2621f3f6a8 | ||
|
8ec0bf3290 | ||
|
7e20d0d308 | ||
|
24d8de93d7 | ||
|
2dd6eeba7d | ||
|
72997cf982 | ||
|
7365d7bc11 | ||
|
36e20f689c | ||
|
cde261ed56 | ||
|
8d8cc8bcd8 | ||
|
ccb6ff92f2 | ||
|
e944a6c8d3 | ||
|
eb1e29c8bb | ||
|
66e7715ea6 | ||
|
d9d3e40112 | ||
|
7385793c65 | ||
|
72885e7e24 | ||
|
a931776e54 | ||
|
61568318d6 | ||
|
3caa989e20 | ||
|
09fd11d13b | ||
|
f33681de49 | ||
|
bf8bfc66a5 | ||
|
4075e2d9d3 | ||
|
78856e1a6f | ||
|
4222dd9ad3 | ||
|
34a547ce57 | ||
|
353e7cd681 | ||
|
72dece0769 | ||
|
6e1c5e9b49 | ||
|
bf8ba79090 | ||
|
5edfb1d262 | ||
|
0fecae0abc | ||
|
26f4f734f9 | ||
|
5e7df396b9 | ||
|
314ba78335 | ||
|
7aa78ecc75 | ||
|
ba8e26458e | ||
|
c627e8cc4d | ||
|
04f894275d | ||
|
edb752136f | ||
|
2b463a632f | ||
|
ca122dd2cb | ||
|
77657b65f9 | ||
|
28c57a837a | ||
|
afda20b56d | ||
|
0cc7b12fd0 | ||
|
15a78ae715 | ||
|
18444799e0 | ||
|
cc60fa73ad | ||
|
8881e2ac78 | ||
|
533031e3d6 | ||
|
76d4af15e4 | ||
|
74c6dd2721 |
4
CONFIG
4
CONFIG
@@ -51,9 +51,9 @@ Ok, looks good.
|
||||
|
||||
To have xmonad start using your settings, type 'mod-q'. xmonad will
|
||||
then load this new file, and run it. If it is unable to, the defaults
|
||||
are used.
|
||||
are used.
|
||||
|
||||
To load succesfully, both 'xmonad' and 'ghc' must be in your $PATH
|
||||
To load successfully, both 'xmonad' and 'ghc' must be in your $PATH
|
||||
environment variable. If GHC isn't in your path, for some reason, you
|
||||
can compile the xmonad.hs file yourself:
|
||||
|
||||
|
39
Main.hs
39
Main.hs
@@ -16,14 +16,17 @@ module Main (main) where
|
||||
|
||||
import XMonad
|
||||
|
||||
import System.IO
|
||||
import Control.Monad (unless)
|
||||
import System.Info
|
||||
import System.Environment
|
||||
import System.Posix.Process (executeFile)
|
||||
import System.Exit (exitFailure)
|
||||
|
||||
import Paths_xmonad (version)
|
||||
import Data.Version (showVersion)
|
||||
|
||||
import Graphics.X11.Xinerama (compiledWithXinerama)
|
||||
|
||||
#ifdef TESTING
|
||||
import qualified Properties
|
||||
#endif
|
||||
@@ -32,18 +35,27 @@ import qualified Properties
|
||||
-- for xmonad, and if it doesn't find one, just launches the default.
|
||||
main :: IO ()
|
||||
main = do
|
||||
installSignalHandlers -- important to ignore SIGCHLD to avoid zombies
|
||||
args <- getArgs
|
||||
let launch = catchIO buildLaunch >> xmonad defaultConfig
|
||||
case args of
|
||||
[] -> launch
|
||||
["--resume", _] -> launch
|
||||
("--resume":_) -> launch
|
||||
["--help"] -> usage
|
||||
["--recompile"] -> recompile True >> return ()
|
||||
["--version"] -> putStrLn ("xmonad " ++ showVersion version)
|
||||
["--recompile"] -> recompile True >>= flip unless exitFailure
|
||||
["--replace"] -> launch
|
||||
["--restart"] -> sendRestart >> return ()
|
||||
["--version"] -> putStrLn $ unwords shortVersion
|
||||
["--verbose-version"] -> putStrLn . unwords $ shortVersion ++ longVersion
|
||||
#ifdef TESTING
|
||||
("--run-tests":_) -> Properties.main
|
||||
#endif
|
||||
_ -> fail "unrecognized flags"
|
||||
where
|
||||
shortVersion = ["xmonad", showVersion version]
|
||||
longVersion = [ "compiled by", compilerName, showVersion compilerVersion
|
||||
, "for", arch ++ "-" ++ os
|
||||
, "\nXinerama:", show compiledWithXinerama ]
|
||||
|
||||
usage :: IO ()
|
||||
usage = do
|
||||
@@ -54,18 +66,20 @@ usage = do
|
||||
" --help Print this message" :
|
||||
" --version Print the version number" :
|
||||
" --recompile Recompile your ~/.xmonad/xmonad.hs" :
|
||||
" --replace Replace the running window manager with xmonad" :
|
||||
" --restart Request a running xmonad process to restart" :
|
||||
#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
|
||||
-- these cases:
|
||||
--
|
||||
-- * ghc missing
|
||||
--
|
||||
-- * ~/.xmonad/xmonad.hs missing
|
||||
-- * both "~\/.xmonad\/xmonad.hs" and "~\/.xmonad\/xmonad-$arch-$os" missing
|
||||
--
|
||||
-- * xmonad.hs fails to compile
|
||||
--
|
||||
@@ -73,7 +87,7 @@ usage = do
|
||||
--
|
||||
-- ** type error, syntax error, ..
|
||||
--
|
||||
-- * Missing xmonad/XMonadContrib modules due to ghc upgrade
|
||||
-- * Missing XMonad\/XMonadContrib modules due to ghc upgrade
|
||||
--
|
||||
buildLaunch :: IO ()
|
||||
buildLaunch = do
|
||||
@@ -82,3 +96,14 @@ buildLaunch = do
|
||||
args <- getArgs
|
||||
executeFile (dir ++ "/xmonad-"++arch++"-"++os) False args Nothing
|
||||
return ()
|
||||
|
||||
sendRestart :: IO ()
|
||||
sendRestart = do
|
||||
dpy <- openDisplay ""
|
||||
rw <- rootWindow dpy $ defaultScreen dpy
|
||||
xmonad_restart <- internAtom dpy "XMONAD_RESTART" False
|
||||
allocaXEvent $ \e -> do
|
||||
setEventType e clientMessage
|
||||
setClientMessageEvent e rw xmonad_restart 32 0 currentTime
|
||||
sendEvent dpy rw False structureNotifyMask e
|
||||
sync dpy False
|
||||
|
16
README
16
README
@@ -26,16 +26,16 @@ Building:
|
||||
|
||||
Building is quite straightforward, and requires a basic Haskell toolchain.
|
||||
On many systems xmonad is available as a binary package in your
|
||||
package system (e.g. on debian or gentoo). If at all possible, use this
|
||||
package system (e.g. on Debian or Gentoo). If at all possible, use this
|
||||
in preference to a source build, as the dependency resolution will be
|
||||
simpler.
|
||||
|
||||
We'll now walk through the complete list of toolchain dependencies.
|
||||
|
||||
* GHC: the Glasgow Haskell Compiler
|
||||
|
||||
|
||||
You first need a Haskell compiler. Your distribution's package
|
||||
system will have binaries of GHC (the Glasgow Haskell Compiler), the
|
||||
system will have binaries of GHC (the Glasgow Haskell Compiler), the
|
||||
compiler we use, so install that first. If your operating system's
|
||||
package system doesn't provide a binary version of GHC, you can find
|
||||
them here:
|
||||
@@ -46,7 +46,7 @@ Building:
|
||||
|
||||
apt-get install ghc6
|
||||
|
||||
It shouldn't be necessary to compile GHC from source -- every common
|
||||
It shouldn't be necessary to compile GHC from source -- every common
|
||||
system has a pre-build binary version.
|
||||
|
||||
* X11 libraries:
|
||||
@@ -60,7 +60,7 @@ Building:
|
||||
Typically you need: libXinerama libXext libX11
|
||||
|
||||
* Cabal
|
||||
|
||||
|
||||
xmonad requires a recent version of Cabal, >= 1.2.0. If you're using
|
||||
GHC 6.8, then it comes bundled with the right version. If you're
|
||||
using GHC 6.6.x, you'll need to build and install Cabal from hackage
|
||||
@@ -84,7 +84,7 @@ Building:
|
||||
unix http://hackage.haskell.org/cgi-bin/hackage-scripts/package/unix
|
||||
X11 http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11
|
||||
|
||||
* Build xmonad:
|
||||
* Build xmonad:
|
||||
|
||||
Once you've got all the dependencies in place (which should be
|
||||
straightforward), build xmonad:
|
||||
@@ -128,14 +128,14 @@ XMonadContrib
|
||||
|
||||
Other useful programs:
|
||||
|
||||
A nicer xterm replacment, that supports resizing better:
|
||||
A nicer xterm replacement, that supports resizing better:
|
||||
|
||||
urxvt http://software.schmorp.de/pkg/rxvt-unicode.html
|
||||
|
||||
For custom status bars:
|
||||
|
||||
dzen http://gotmor.googlepages.com/dzen
|
||||
xmobar http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmobar
|
||||
xmobar http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmobar
|
||||
|
||||
For a program dispatch menu:
|
||||
|
||||
|
4
STYLE
4
STYLE
@@ -2,7 +2,7 @@
|
||||
== Coding guidelines for contributing to
|
||||
== xmonad and the xmonad contributed extensions
|
||||
|
||||
* Comment every top level function (particularly exported funtions), and
|
||||
* Comment every top level function (particularly exported functions), and
|
||||
provide a type signature; use Haddock syntax in the comments.
|
||||
|
||||
* Follow the coding style of the other modules.
|
||||
@@ -15,7 +15,7 @@
|
||||
* Tabs are illegal. Use 4 spaces for indenting.
|
||||
|
||||
* Any pure function added to the core should have QuickCheck properties
|
||||
precisely defining its behaviour.
|
||||
precisely defining its behavior.
|
||||
|
||||
* New modules should identify the author, and be submitted under
|
||||
the same license as xmonad (BSD3 license or freer).
|
||||
|
6
TODO
6
TODO
@@ -1,7 +1,7 @@
|
||||
- Write down invariants for the window life cycle, especially:
|
||||
- When are borders set? Prove that the current handling is sufficient.
|
||||
|
||||
- current floating layer handling is unoptimal. FocusUp should raise,
|
||||
- current floating layer handling is nonoptimal. FocusUp should raise,
|
||||
for example
|
||||
|
||||
- Issues still with stacking order.
|
||||
@@ -15,7 +15,9 @@
|
||||
* double check README build instructions
|
||||
* test core with 6.6 and 6.8
|
||||
* bump xmonad.cabal version and X11 version
|
||||
* upload X11 and xmonad to hackage
|
||||
* upload X11 and xmonad to Hackage
|
||||
* update links to hackage in download.html
|
||||
* update #xmonad topic
|
||||
* check examples/text in user-facing Config.hs
|
||||
* check tour.html and intro.html are up to date, and mention all core bindings
|
||||
* confirm template config is type correct
|
||||
|
@@ -25,20 +25,24 @@ module XMonad.Config (defaultConfig) where
|
||||
-- Useful imports
|
||||
--
|
||||
import XMonad.Core as XMonad hiding
|
||||
(workspaces,manageHook,numlockMask,keys,logHook,startupHook,borderWidth,mouseBindings
|
||||
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse)
|
||||
(workspaces,manageHook,keys,logHook,startupHook,borderWidth,mouseBindings
|
||||
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse
|
||||
,handleEventHook)
|
||||
import qualified XMonad.Core as XMonad
|
||||
(workspaces,manageHook,numlockMask,keys,logHook,startupHook,borderWidth,mouseBindings
|
||||
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse)
|
||||
(workspaces,manageHook,keys,logHook,startupHook,borderWidth,mouseBindings
|
||||
,layoutHook,modMask,terminal,normalBorderColor,focusedBorderColor,focusFollowsMouse
|
||||
,handleEventHook)
|
||||
|
||||
import XMonad.Layout
|
||||
import XMonad.Operations
|
||||
import XMonad.ManageHook
|
||||
import qualified XMonad.StackSet as W
|
||||
import Data.Bits ((.|.))
|
||||
import Data.Monoid
|
||||
import qualified Data.Map as M
|
||||
import System.Exit
|
||||
import Graphics.X11.Xlib
|
||||
import Graphics.X11.Xlib.Extras
|
||||
|
||||
-- | The default number of workspaces (virtual screens) and their names.
|
||||
-- By default we use numeric strings, but any string may be used as a
|
||||
@@ -60,22 +64,6 @@ workspaces = map show [1 .. 9 :: Int]
|
||||
defaultModMask :: KeyMask
|
||||
defaultModMask = mod1Mask
|
||||
|
||||
-- | The mask for the numlock key. Numlock status is "masked" from the
|
||||
-- current modifier status, so the keybindings will work with numlock on or
|
||||
-- off. You may need to change this on some systems.
|
||||
--
|
||||
-- You can find the numlock modifier by running "xmodmap" and looking for a
|
||||
-- modifier with Num_Lock bound to it:
|
||||
--
|
||||
-- > $ xmodmap | grep Num
|
||||
-- > mod2 Num_Lock (0x4d)
|
||||
--
|
||||
-- Set numlockMask = 0 if you don't have a numlock key, or want to treat
|
||||
-- numlock status separately.
|
||||
--
|
||||
numlockMask :: KeyMask
|
||||
numlockMask = mod2Mask
|
||||
|
||||
-- | Width of the window border in pixels.
|
||||
--
|
||||
borderWidth :: Dimension
|
||||
@@ -119,6 +107,15 @@ manageHook = composeAll
|
||||
logHook :: X ()
|
||||
logHook = return ()
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Event handling
|
||||
|
||||
-- | Defines a custom handler function for X Events. The function should
|
||||
-- return (All True) if the default handler is to be run afterwards.
|
||||
-- To combine event hooks, use mappend or mconcat from Data.Monoid.
|
||||
handleEventHook :: Event -> X All
|
||||
handleEventHook _ = return (All True)
|
||||
|
||||
-- | Perform an arbitrary action at xmonad startup.
|
||||
startupHook :: X ()
|
||||
startupHook = return ()
|
||||
@@ -168,7 +165,7 @@ keys :: XConfig Layout -> M.Map (KeyMask, KeySym) (X ())
|
||||
keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
-- launching and killing programs
|
||||
[ ((modMask .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf) -- %! Launch terminal
|
||||
, ((modMask, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"") -- %! Launch dmenu
|
||||
, ((modMask, xK_p ), spawn "dmenu_run") -- %! Launch dmenu
|
||||
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun") -- %! Launch gmrun
|
||||
, ((modMask .|. shiftMask, xK_c ), kill) -- %! Close the focused window
|
||||
|
||||
@@ -205,7 +202,7 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
|
||||
-- quit, or restart
|
||||
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit xmonad
|
||||
, ((modMask , xK_q ), restart "xmonad" True) -- %! Restart xmonad
|
||||
, ((modMask , xK_q ), spawn "if type xmonad; then xmonad --recompile && xmonad --restart; else xmessage xmonad not in \\$PATH: \"$PATH\"; fi") -- %! Restart xmonad
|
||||
]
|
||||
++
|
||||
-- mod-[1..9] %! Switch to workspace N
|
||||
@@ -223,15 +220,15 @@ keys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
-- | Mouse bindings: default actions bound to mouse events
|
||||
--
|
||||
mouseBindings :: XConfig Layout -> M.Map (KeyMask, Button) (Window -> X ())
|
||||
mouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
mouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList
|
||||
-- mod-button1 %! Set the window to floating mode and move by dragging
|
||||
[ ((modMask, button1), (\w -> focus w >> mouseMoveWindow w
|
||||
>> windows W.swapMaster))
|
||||
[ ((modMask, button1), \w -> focus w >> mouseMoveWindow w
|
||||
>> windows W.shiftMaster)
|
||||
-- mod-button2 %! Raise the window to the top of the stack
|
||||
, ((modMask, button2), (\w -> focus w >> windows W.swapMaster))
|
||||
, ((modMask, button2), windows . (W.shiftMaster .) . W.focusWindow)
|
||||
-- mod-button3 %! Set the window to floating mode and resize by dragging
|
||||
, ((modMask, button3), (\w -> focus w >> mouseResizeWindow w
|
||||
>> windows W.swapMaster))
|
||||
, ((modMask, button3), \w -> focus w >> mouseResizeWindow w
|
||||
>> windows W.shiftMaster)
|
||||
-- you may also bind events to the mouse scroll wheel (button4 and button5)
|
||||
]
|
||||
|
||||
@@ -243,11 +240,12 @@ defaultConfig = XConfig
|
||||
, XMonad.terminal = terminal
|
||||
, XMonad.normalBorderColor = normalBorderColor
|
||||
, XMonad.focusedBorderColor = focusedBorderColor
|
||||
, XMonad.numlockMask = numlockMask
|
||||
, XMonad.modMask = defaultModMask
|
||||
, XMonad.keys = keys
|
||||
, XMonad.logHook = logHook
|
||||
, XMonad.startupHook = startupHook
|
||||
, XMonad.mouseBindings = mouseBindings
|
||||
, XMonad.manageHook = manageHook
|
||||
, XMonad.focusFollowsMouse = focusFollowsMouse }
|
||||
, XMonad.handleEventHook = handleEventHook
|
||||
, XMonad.focusFollowsMouse = focusFollowsMouse
|
||||
}
|
||||
|
178
XMonad/Core.hs
178
XMonad/Core.hs
@@ -1,7 +1,5 @@
|
||||
{-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving,
|
||||
MultiParamTypeClasses, TypeSynonymInstances, CPP #-}
|
||||
-- required for deriving Typeable
|
||||
{-# OPTIONS_GHC -fglasgow-exts #-}
|
||||
MultiParamTypeClasses, TypeSynonymInstances, CPP, DeriveDataTypeable #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
@@ -24,28 +22,36 @@ module XMonad.Core (
|
||||
XConf(..), XConfig(..), LayoutClass(..),
|
||||
Layout(..), readsLayout, Typeable, Message,
|
||||
SomeMessage(..), fromMessage, LayoutMessages(..),
|
||||
runX, catchX, userCode, io, catchIO, doubleFork,
|
||||
StateExtension(..), ExtensionClass(..),
|
||||
runX, catchX, userCode, userCodeDef, io, catchIO, installSignalHandlers, uninstallSignalHandlers,
|
||||
withDisplay, withWindowSet, isRoot, runOnWorkspaces,
|
||||
getAtom, spawn, getXMonadDir, recompile, trace, whenJust, whenX,
|
||||
getAtom, spawn, spawnPID, xfork, getXMonadDir, recompile, trace, whenJust, whenX,
|
||||
atom_WM_STATE, atom_WM_PROTOCOLS, atom_WM_DELETE_WINDOW, ManageHook, Query(..), runQuery
|
||||
) where
|
||||
|
||||
import XMonad.StackSet hiding (modify)
|
||||
|
||||
import Prelude hiding ( catch )
|
||||
import Control.Exception (catch, bracket, throw, Exception(ExitException))
|
||||
import Codec.Binary.UTF8.String (encodeString)
|
||||
import Control.Exception.Extensible (catch, fromException, try, bracket, throw, finally, SomeException(..))
|
||||
import Control.Applicative
|
||||
import Control.Monad.State
|
||||
import Control.Monad.Reader
|
||||
import System.FilePath
|
||||
import System.IO
|
||||
import System.Info
|
||||
import System.Posix.Process (executeFile, forkProcess, getProcessStatus, createSession)
|
||||
import System.Posix.Process (executeFile, forkProcess, getAnyProcessStatus, createSession)
|
||||
import System.Posix.Signals
|
||||
import System.Posix.IO
|
||||
import System.Posix.Types (ProcessID)
|
||||
import System.Process
|
||||
import System.Directory
|
||||
import System.Exit
|
||||
import Graphics.X11.Xlib
|
||||
import Graphics.X11.Xlib.Extras (Event)
|
||||
import Data.Typeable
|
||||
import Data.List ((\\))
|
||||
import Data.Maybe (isJust,fromMaybe)
|
||||
import Data.Monoid
|
||||
|
||||
import qualified Data.Map as M
|
||||
@@ -53,10 +59,17 @@ import qualified Data.Set as S
|
||||
|
||||
-- | XState, the (mutable) window manager state.
|
||||
data XState = XState
|
||||
{ windowset :: !WindowSet -- ^ workspace list
|
||||
, mapped :: !(S.Set Window) -- ^ the Set of mapped windows
|
||||
, waitingUnmap :: !(M.Map Window Int) -- ^ the number of expected UnmapEvents
|
||||
, dragging :: !(Maybe (Position -> Position -> X (), X ())) }
|
||||
{ windowset :: !WindowSet -- ^ workspace list
|
||||
, mapped :: !(S.Set Window) -- ^ the Set of mapped windows
|
||||
, waitingUnmap :: !(M.Map Window Int) -- ^ the number of expected UnmapEvents
|
||||
, dragging :: !(Maybe (Position -> Position -> X (), X ()))
|
||||
, numberlockMask :: !KeyMask -- ^ The numlock modifier
|
||||
, extensibleState :: !(M.Map String (Either String StateExtension))
|
||||
-- ^ stores custom state information.
|
||||
--
|
||||
-- The module "XMonad.Utils.ExtensibleState" in xmonad-contrib
|
||||
-- provides additional information and a simple interface for using this.
|
||||
}
|
||||
|
||||
-- | XConf, the (read-only) window manager configuration.
|
||||
data XConf = XConf
|
||||
@@ -70,6 +83,9 @@ data XConf = XConf
|
||||
, buttonActions :: !(M.Map (KeyMask, Button) (Window -> X ()))
|
||||
-- ^ a mapping of button presses to actions
|
||||
, mouseFocused :: !Bool -- ^ was refocus caused by mouse action?
|
||||
, mousePosition :: !(Maybe (Position, Position))
|
||||
-- ^ position of the mouse according to
|
||||
-- the event currently being processed
|
||||
}
|
||||
|
||||
-- todo, better name
|
||||
@@ -79,8 +95,10 @@ data XConfig l = XConfig
|
||||
, terminal :: !String -- ^ The preferred terminal application. Default: \"xterm\"
|
||||
, layoutHook :: !(l Window) -- ^ The available layouts
|
||||
, manageHook :: !ManageHook -- ^ The action to run when a new window is opened
|
||||
, handleEventHook :: !(Event -> X All) -- ^ Handle an X event, returns (All True) if the default handler
|
||||
-- should also be run afterwards. mappend should be used for combining
|
||||
-- event hooks in most cases.
|
||||
, workspaces :: ![String] -- ^ The list of workspaces' names
|
||||
, numlockMask :: !KeyMask -- ^ The numlock modifier
|
||||
, modMask :: !KeyMask -- ^ the mod modifier
|
||||
, keys :: !(XConfig Layout -> M.Map (ButtonMask,KeySym) (X ()))
|
||||
-- ^ The key binding: a map from key presses and actions
|
||||
@@ -116,9 +134,7 @@ data ScreenDetail = SD { screenRect :: !Rectangle } deriving (Eq,Show, Read)
|
||||
-- instantiated on 'XConf' and 'XState' automatically.
|
||||
--
|
||||
newtype X a = X (ReaderT XConf (StateT XState IO) a)
|
||||
#ifndef __HADDOCK__
|
||||
deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf)
|
||||
#endif
|
||||
deriving (Functor, Monad, MonadIO, MonadState XState, MonadReader XConf, Typeable)
|
||||
|
||||
instance Applicative X where
|
||||
pure = return
|
||||
@@ -130,9 +146,7 @@ instance (Monoid a) => Monoid (X a) where
|
||||
|
||||
type ManageHook = Query (Endo WindowSet)
|
||||
newtype Query a = Query (ReaderT Window X a)
|
||||
#ifndef __HADDOCK__
|
||||
deriving (Functor, Monad, MonadReader Window, MonadIO)
|
||||
#endif
|
||||
|
||||
runQuery :: Query a -> Window -> X a
|
||||
runQuery (Query m) w = runReaderT m w
|
||||
@@ -152,16 +166,21 @@ catchX :: X a -> X a -> X a
|
||||
catchX job errcase = do
|
||||
st <- get
|
||||
c <- ask
|
||||
(a, s') <- io $ runX c st job `catch` \e -> case e of
|
||||
ExitException {} -> throw e
|
||||
_ -> do hPrint stderr e; runX c st errcase
|
||||
(a, s') <- io $ runX c st job `catch` \e -> case fromException e of
|
||||
Just x -> throw e `const` (x `asTypeOf` ExitSuccess)
|
||||
_ -> do hPrint stderr e; runX c st errcase
|
||||
put s'
|
||||
return a
|
||||
|
||||
-- | Execute the argument, catching all exceptions. Either this function or
|
||||
-- 'catchX' should be used at all callsites of user customized code.
|
||||
userCode :: X () -> X ()
|
||||
userCode a = catchX (a >> return ()) (return ())
|
||||
userCode :: X a -> X (Maybe a)
|
||||
userCode a = catchX (Just `liftM` a) (return Nothing)
|
||||
|
||||
-- | Same as userCode but with a default argument to return instead of using
|
||||
-- Maybe, provided for convenience.
|
||||
userCodeDef :: a -> X a -> X a
|
||||
userCodeDef def a = fromMaybe def `liftM` userCode a
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Convenient wrappers to state
|
||||
@@ -325,6 +344,33 @@ data LayoutMessages = Hide -- ^ sent when a layout becomes non-visi
|
||||
|
||||
instance Message LayoutMessages
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Extensible state
|
||||
--
|
||||
|
||||
-- | Every module must make the data it wants to store
|
||||
-- an instance of this class.
|
||||
--
|
||||
-- Minimal complete definition: initialValue
|
||||
class Typeable a => ExtensionClass a where
|
||||
-- | Defines an initial value for the state extension
|
||||
initialValue :: a
|
||||
-- | Specifies whether the state extension should be
|
||||
-- persistent. Setting this method to 'PersistentExtension'
|
||||
-- will make the stored data survive restarts, but
|
||||
-- requires a to be an instance of Read and Show.
|
||||
--
|
||||
-- It defaults to 'StateExtension', i.e. no persistence.
|
||||
extensionType :: a -> StateExtension
|
||||
extensionType = StateExtension
|
||||
|
||||
-- | Existential type to store a state extension.
|
||||
data StateExtension =
|
||||
forall a. ExtensionClass a => StateExtension a
|
||||
-- ^ Non-persistent state extension
|
||||
| forall a. (Read a, Show a, ExtensionClass a) => PersistentExtension a
|
||||
-- ^ Persistent extension
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- | General utilities
|
||||
--
|
||||
@@ -335,21 +381,30 @@ io = liftIO
|
||||
-- | Lift an 'IO' action into the 'X' monad. If the action results in an 'IO'
|
||||
-- exception, log the exception to stderr and continue normal execution.
|
||||
catchIO :: MonadIO m => IO () -> m ()
|
||||
catchIO f = io (f `catch` \e -> hPrint stderr e >> hFlush stderr)
|
||||
catchIO f = io (f `catch` \(SomeException e) -> hPrint stderr e >> hFlush stderr)
|
||||
|
||||
-- | spawn. Launch an external application
|
||||
-- | spawn. Launch an external application. Specifically, it double-forks and
|
||||
-- runs the 'String' you pass as a command to \/bin\/sh.
|
||||
--
|
||||
-- Note this function assumes your locale uses utf8.
|
||||
spawn :: MonadIO m => String -> m ()
|
||||
spawn x = doubleFork $ executeFile "/bin/sh" False ["-c", x] Nothing
|
||||
spawn x = spawnPID x >> return ()
|
||||
|
||||
-- | Double fork and execute an 'IO' action (usually one of the exec family of
|
||||
-- functions)
|
||||
doubleFork :: MonadIO m => IO () -> m ()
|
||||
doubleFork m = io $ do
|
||||
pid <- forkProcess $ do
|
||||
forkProcess (createSession >> m)
|
||||
exitWith ExitSuccess
|
||||
getProcessStatus True False pid
|
||||
return ()
|
||||
-- | Like 'spawn', but returns the 'ProcessID' of the launched application
|
||||
spawnPID :: MonadIO m => String -> m ProcessID
|
||||
spawnPID x = xfork $ executeFile "/bin/sh" False ["-c", encodeString x] Nothing
|
||||
|
||||
-- | A replacement for 'forkProcess' which resets default signal handlers.
|
||||
xfork :: MonadIO m => IO () -> m ProcessID
|
||||
xfork x = io . forkProcess . finally nullStdin $ do
|
||||
uninstallSignalHandlers
|
||||
createSession
|
||||
x
|
||||
where
|
||||
nullStdin = do
|
||||
fd <- openFd "/dev/null" ReadOnly Nothing defaultFileFlags
|
||||
dupTo fd stdInput
|
||||
closeFd fd
|
||||
|
||||
-- | This is basically a map function, running a function in the 'X' monad on
|
||||
-- each workspace with the output of that function being the modified workspace.
|
||||
@@ -372,9 +427,11 @@ getXMonadDir = io $ getAppUserDataDirectory "xmonad"
|
||||
--
|
||||
-- * the xmonad executable does not exist
|
||||
--
|
||||
-- * the xmonad executable is older than xmonad.hs
|
||||
-- * the xmonad executable is older than xmonad.hs or any file in
|
||||
-- ~\/.xmonad\/lib
|
||||
--
|
||||
-- The -i flag is used to restrict recompilation to the xmonad.hs file only.
|
||||
-- The -i flag is used to restrict recompilation to the xmonad.hs file only,
|
||||
-- and any files in the ~\/.xmonad\/lib directory.
|
||||
--
|
||||
-- Compilation errors (if any) are logged to ~\/.xmonad\/xmonad.errors. If
|
||||
-- GHC indicates failure with a non-zero exit code, an xmessage displaying
|
||||
@@ -386,31 +443,46 @@ recompile :: MonadIO m => Bool -> m Bool
|
||||
recompile force = io $ do
|
||||
dir <- getXMonadDir
|
||||
let binn = "xmonad-"++arch++"-"++os
|
||||
bin = dir ++ "/" ++ binn
|
||||
base = dir ++ "/" ++ "xmonad"
|
||||
bin = dir </> binn
|
||||
base = dir </> "xmonad"
|
||||
err = base ++ ".errors"
|
||||
src = base ++ ".hs"
|
||||
lib = dir </> "lib"
|
||||
libTs <- mapM getModTime . Prelude.filter isSource =<< allFiles lib
|
||||
srcT <- getModTime src
|
||||
binT <- getModTime bin
|
||||
if (force || srcT > binT)
|
||||
if force || any (binT <) (srcT : libTs)
|
||||
then do
|
||||
status <- bracket (openFile err WriteMode) hClose $ \h -> do
|
||||
waitForProcess =<< runProcess "ghc" ["--make", "xmonad.hs", "-i", "-no-recomp", "-v0", "-o",binn] (Just dir)
|
||||
-- temporarily disable SIGCHLD ignoring:
|
||||
uninstallSignalHandlers
|
||||
status <- bracket (openFile err WriteMode) hClose $ \h ->
|
||||
waitForProcess =<< runProcess "ghc" ["--make", "xmonad.hs", "-i", "-ilib", "-fforce-recomp", "-v0", "-o",binn] (Just dir)
|
||||
Nothing Nothing Nothing (Just h)
|
||||
|
||||
-- re-enable SIGCHLD:
|
||||
installSignalHandlers
|
||||
|
||||
-- now, if it fails, run xmessage to let the user know:
|
||||
when (status /= ExitSuccess) $ do
|
||||
ghcErr <- readFile err
|
||||
let msg = unlines $
|
||||
["Error detected while loading xmonad configuration file: " ++ src]
|
||||
++ lines ghcErr ++ ["","Please check the file for errors."]
|
||||
++ lines (if null ghcErr then show status else ghcErr)
|
||||
++ ["","Please check the file for errors."]
|
||||
-- nb, the ordering of printing, then forking, is crucial due to
|
||||
-- lazy evaluation
|
||||
hPutStrLn stderr msg
|
||||
doubleFork $ executeFile "xmessage" True ["-default", "okay", msg] Nothing
|
||||
forkProcess $ executeFile "xmessage" True ["-default", "okay", msg] Nothing
|
||||
return ()
|
||||
return (status == ExitSuccess)
|
||||
else return True
|
||||
where getModTime f = catch (Just <$> getModificationTime f) (const $ return Nothing)
|
||||
where getModTime f = catch (Just <$> getModificationTime f) (\(SomeException _) -> return Nothing)
|
||||
isSource = flip elem [".hs",".lhs",".hsc"]
|
||||
allFiles t = do
|
||||
let prep = map (t</>) . Prelude.filter (`notElem` [".",".."])
|
||||
cs <- prep <$> catch (getDirectoryContents t) (\(SomeException _) -> return [])
|
||||
ds <- filterM doesDirectoryExist cs
|
||||
concat . ((cs \\ ds):) <$> mapM allFiles ds
|
||||
|
||||
-- | Conditionally run an action, using a @Maybe a@ to decide.
|
||||
whenJust :: Monad m => Maybe a -> (a -> m ()) -> m ()
|
||||
@@ -424,3 +496,21 @@ whenX a f = a >>= \b -> when b f
|
||||
-- be found in your .xsession-errors file
|
||||
trace :: MonadIO m => String -> m ()
|
||||
trace = io . hPutStrLn stderr
|
||||
|
||||
-- | Ignore SIGPIPE to avoid termination when a pipe is full, and SIGCHLD to
|
||||
-- avoid zombie processes, and clean up any extant zombie processes.
|
||||
installSignalHandlers :: MonadIO m => m ()
|
||||
installSignalHandlers = io $ do
|
||||
installHandler openEndedPipe Ignore Nothing
|
||||
installHandler sigCHLD Ignore Nothing
|
||||
(try :: IO a -> IO (Either SomeException a))
|
||||
$ fix $ \more -> do
|
||||
x <- getAnyProcessStatus False False
|
||||
when (isJust x) more
|
||||
return ()
|
||||
|
||||
uninstallSignalHandlers :: MonadIO m => m ()
|
||||
uninstallSignalHandlers = io $ do
|
||||
installHandler openEndedPipe Default Nothing
|
||||
installHandler sigCHLD Default Nothing
|
||||
return ()
|
||||
|
@@ -1,5 +1,4 @@
|
||||
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances, DeriveDataTypeable #-}
|
||||
|
||||
-- --------------------------------------------------------------------------
|
||||
-- |
|
||||
@@ -51,7 +50,11 @@ instance LayoutClass Full a
|
||||
|
||||
-- | The builtin tiling mode of xmonad. Supports 'Shrink', 'Expand' and
|
||||
-- 'IncMasterN'.
|
||||
data Tall a = Tall !Int !Rational !Rational deriving (Show, Read)
|
||||
data Tall a = Tall { tallNMaster :: !Int -- ^ The default number of windows in the master pane (default: 1)
|
||||
, tallRatioIncrement :: !Rational -- ^ Percent of screen to increment by when resizing panes (default: 3/100)
|
||||
, tallRatio :: !Rational -- ^ Default proportion of screen occupied by master pane (default: 1/2)
|
||||
}
|
||||
deriving (Show, Read)
|
||||
-- TODO should be capped [0..1] ..
|
||||
|
||||
-- a nice pure layout, lots of properties for the layout, and its messages, in Properties.hs
|
||||
@@ -122,7 +125,7 @@ instance LayoutClass l a => LayoutClass (Mirror l) a where
|
||||
|
||||
-- | Mirror a rectangle.
|
||||
mirrorRect :: Rectangle -> Rectangle
|
||||
mirrorRect (Rectangle rx ry rw rh) = (Rectangle ry rx rh rw)
|
||||
mirrorRect (Rectangle rx ry rw rh) = Rectangle ry rx rh rw
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- LayoutClass selection manager
|
||||
@@ -170,7 +173,7 @@ choose (Choose d l r) d' ml mr = f lr
|
||||
|
||||
instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
|
||||
runLayout (W.Workspace i (Choose L l r) ms) =
|
||||
fmap (second . fmap $ flip (Choose L) $ r) . runLayout (W.Workspace i l ms)
|
||||
fmap (second . fmap $ flip (Choose L) r) . runLayout (W.Workspace i l ms)
|
||||
runLayout (W.Workspace i (Choose R l r) ms) =
|
||||
fmap (second . fmap $ Choose R l) . runLayout (W.Workspace i r ms)
|
||||
|
||||
@@ -191,7 +194,7 @@ instance (LayoutClass l a, LayoutClass r a) => LayoutClass (Choose l r) a where
|
||||
|
||||
R -> choose c R Nothing =<< handle r NextNoWrap
|
||||
|
||||
handleMessage c@(Choose _ l _) m | Just FirstLayout <- fromMessage m = do
|
||||
handleMessage c@(Choose _ l _) m | Just FirstLayout <- fromMessage m =
|
||||
flip (choose c L) Nothing =<< handle l FirstLayout
|
||||
|
||||
handleMessage c@(Choose d l r) m | Just ReleaseResources <- fromMessage m =
|
||||
|
145
XMonad/Main.hsc
145
XMonad/Main.hsc
@@ -15,19 +15,21 @@
|
||||
|
||||
module XMonad.Main (xmonad) where
|
||||
|
||||
import Control.Arrow (second)
|
||||
import Data.Bits
|
||||
import Data.List ((\\))
|
||||
import Data.Function
|
||||
import qualified Data.Map as M
|
||||
import qualified Data.Set as S
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.State
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Monoid (getAll)
|
||||
|
||||
import Foreign.C
|
||||
import Foreign.Ptr
|
||||
|
||||
import System.Environment (getArgs)
|
||||
import System.Posix.Signals
|
||||
|
||||
import Graphics.X11.Xlib hiding (refreshKeyboardMapping)
|
||||
import Graphics.X11.Xlib.Extras
|
||||
@@ -57,14 +59,31 @@ xmonad :: (LayoutClass l Window, Read (l Window)) => XConfig l -> IO ()
|
||||
xmonad initxmc = do
|
||||
-- setup locale information from environment
|
||||
withCString "" $ c_setlocale (#const LC_ALL)
|
||||
-- ignore SIGPIPE
|
||||
installHandler openEndedPipe Ignore Nothing
|
||||
-- ignore SIGPIPE and SIGCHLD
|
||||
installSignalHandlers
|
||||
-- First, wrap the layout in an existential, to keep things pretty:
|
||||
let xmc = initxmc { layoutHook = Layout $ layoutHook initxmc }
|
||||
dpy <- openDisplay ""
|
||||
let dflt = defaultScreen dpy
|
||||
|
||||
rootw <- rootWindow dpy dflt
|
||||
|
||||
args <- getArgs
|
||||
|
||||
when ("--replace" `elem` args) $ replace dpy dflt rootw
|
||||
|
||||
-- If another WM is running, a BadAccess error will be returned. The
|
||||
-- default error handler will write the exception to stderr and exit with
|
||||
-- an error.
|
||||
selectInput dpy rootw $ substructureRedirectMask .|. substructureNotifyMask
|
||||
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
|
||||
.|. buttonPressMask
|
||||
sync dpy False -- sync to ensure all outstanding errors are delivered
|
||||
|
||||
-- turn off the default handler in favor of one that ignores all errors
|
||||
-- (ugly, I know)
|
||||
xSetErrorHandler -- in C, I'm too lazy to write the binding: dons
|
||||
|
||||
xinesc <- getCleanedScreenInfo dpy
|
||||
nbc <- do v <- initColor dpy $ normalBorderColor xmc
|
||||
~(Just nbc_) <- initColor dpy $ normalBorderColor Default.defaultConfig
|
||||
@@ -75,12 +94,10 @@ xmonad initxmc = do
|
||||
return (fromMaybe fbc_ v)
|
||||
|
||||
hSetBuffering stdout NoBuffering
|
||||
args <- getArgs
|
||||
|
||||
let layout = layoutHook xmc
|
||||
lreads = readsLayout layout
|
||||
initialWinset = new layout (workspaces xmc) $ map SD xinesc
|
||||
|
||||
maybeRead reads' s = case reads' s of
|
||||
[(x, "")] -> Just x
|
||||
_ -> Nothing
|
||||
@@ -90,6 +107,10 @@ xmonad initxmc = do
|
||||
ws <- maybeRead reads s
|
||||
return . W.ensureTags layout (workspaces xmc)
|
||||
$ W.mapLayout (fromMaybe layout . maybeRead lreads) ws
|
||||
extState = fromMaybe M.empty $ do
|
||||
("--resume" : _ : dyns : _) <- return args
|
||||
vals <- maybeRead reads dyns
|
||||
return . M.fromList . map (second Left) $ vals
|
||||
|
||||
cf = XConf
|
||||
{ display = dpy
|
||||
@@ -99,23 +120,21 @@ xmonad initxmc = do
|
||||
, focusedBorder = fbc
|
||||
, keyActions = keys xmc xmc
|
||||
, buttonActions = mouseBindings xmc xmc
|
||||
, mouseFocused = False }
|
||||
, mouseFocused = False
|
||||
, mousePosition = Nothing }
|
||||
|
||||
st = XState
|
||||
{ windowset = initialWinset
|
||||
, mapped = S.empty
|
||||
, waitingUnmap = M.empty
|
||||
, dragging = Nothing }
|
||||
|
||||
xSetErrorHandler -- in C, I'm too lazy to write the binding: dons
|
||||
|
||||
-- setup initial X environment
|
||||
sync dpy False
|
||||
selectInput dpy rootw $ substructureRedirectMask .|. substructureNotifyMask
|
||||
.|. enterWindowMask .|. leaveWindowMask .|. structureNotifyMask
|
||||
|
||||
{ windowset = initialWinset
|
||||
, numberlockMask = 0
|
||||
, mapped = S.empty
|
||||
, waitingUnmap = M.empty
|
||||
, dragging = Nothing
|
||||
, extensibleState = extState
|
||||
}
|
||||
allocaXEvent $ \e ->
|
||||
runX cf st $ do
|
||||
|
||||
setNumlockMask
|
||||
grabKeys
|
||||
grabButtons
|
||||
|
||||
@@ -136,12 +155,26 @@ xmonad initxmc = do
|
||||
userCode $ startupHook initxmc
|
||||
|
||||
-- main loop, for all you HOF/recursion fans out there.
|
||||
forever_ $ handle =<< io (nextEvent dpy e >> getEvent e)
|
||||
forever $ prehandle =<< io (nextEvent dpy e >> getEvent e)
|
||||
|
||||
return ()
|
||||
where forever_ a = a >> forever_ a
|
||||
where
|
||||
-- if the event gives us the position of the pointer, set mousePosition
|
||||
prehandle e = let mouse = do guard (ev_event_type e `elem` evs)
|
||||
return (fromIntegral (ev_x_root e)
|
||||
,fromIntegral (ev_y_root e))
|
||||
in local (\c -> c { mousePosition = mouse }) (handleWithHook e)
|
||||
evs = [ keyPress, keyRelease, enterNotify, leaveNotify
|
||||
, buttonPress, buttonRelease]
|
||||
|
||||
|
||||
-- | Runs handleEventHook from the configuration and runs the default handler
|
||||
-- function if it returned True.
|
||||
handleWithHook :: Event -> X ()
|
||||
handleWithHook e = do
|
||||
evHook <- asks (handleEventHook . config)
|
||||
whenX (userCodeDef True $ getAll `fmap` evHook e) (handle e)
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- | Event handler. Map X events onto calls into Operations.hs, which
|
||||
-- modify our internal model of the window manager state.
|
||||
@@ -160,7 +193,7 @@ handle (KeyEvent {ev_event_type = t, ev_state = m, ev_keycode = code})
|
||||
s <- io $ keycodeToKeysym dpy code 0
|
||||
mClean <- cleanMask m
|
||||
ks <- asks keyActions
|
||||
userCode $ whenJust (M.lookup (mClean, s) ks) id
|
||||
userCodeDef () $ whenJust (M.lookup (mClean, s) ks) id
|
||||
|
||||
-- manage a new window
|
||||
handle (MapRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
|
||||
@@ -189,7 +222,9 @@ handle (UnmapEvent {ev_window = w, ev_send_event = synthetic}) = whenX (isClient
|
||||
-- set keyboard mapping
|
||||
handle e@(MappingNotifyEvent {}) = do
|
||||
io $ refreshKeyboardMapping e
|
||||
when (ev_request e == mappingKeyboard) grabKeys
|
||||
when (ev_request e `elem` [mappingKeyboard, mappingModifier]) $ do
|
||||
setNumlockMask
|
||||
grabKeys
|
||||
|
||||
-- handle button release, which may finish dragging.
|
||||
handle e@(ButtonEvent {ev_event_type = t})
|
||||
@@ -214,16 +249,16 @@ handle e@(ButtonEvent {ev_window = w,ev_event_type = t,ev_button = b })
|
||||
-- grabbed in grabButtons. Otherwise, it's click-to-focus.
|
||||
isr <- isRoot w
|
||||
m <- cleanMask $ ev_state e
|
||||
ba <- asks buttonActions
|
||||
if isr then userCode $ whenJust (M.lookup (m, b) ba) ($ ev_subwindow e)
|
||||
else focus w
|
||||
mact <- asks (M.lookup (m, b) . buttonActions)
|
||||
case mact of
|
||||
(Just act) | isr -> act $ ev_subwindow e
|
||||
_ -> focus w
|
||||
broadcastMessage e -- Always send button events.
|
||||
|
||||
-- entered a normal window: focus it if focusFollowsMouse is set to
|
||||
-- True in the user's config.
|
||||
handle e@(CrossingEvent {ev_window = w, ev_event_type = t})
|
||||
| t == enterNotify && ev_mode e == notifyNormal
|
||||
&& ev_detail e /= notifyInferior
|
||||
= whenX (asks $ focusFollowsMouse . config) (focus w)
|
||||
|
||||
-- left a window, check if we need to focus root
|
||||
@@ -262,8 +297,15 @@ handle e@(ConfigureRequestEvent {ev_window = w}) = withDisplay $ \dpy -> do
|
||||
handle (ConfigureEvent {ev_window = w}) = whenX (isRoot w) rescreen
|
||||
|
||||
-- property notify
|
||||
handle PropertyEvent { ev_event_type = t, ev_atom = a }
|
||||
| t == propertyNotify && a == wM_NAME = userCode =<< asks (logHook . config)
|
||||
handle event@(PropertyEvent { ev_event_type = t, ev_atom = a })
|
||||
| t == propertyNotify && a == wM_NAME = asks (logHook . config) >>= userCodeDef () >>
|
||||
broadcastMessage event
|
||||
|
||||
handle e@ClientMessageEvent { ev_message_type = mt } = do
|
||||
a <- getAtom "XMONAD_RESTART"
|
||||
if (mt == a)
|
||||
then restart "xmonad" True
|
||||
else broadcastMessage e
|
||||
|
||||
handle e = broadcastMessage e -- trace (eventName e) -- ignoring
|
||||
|
||||
@@ -289,6 +331,18 @@ scan dpy rootw = do
|
||||
return $ not (wa_override_redirect wa)
|
||||
&& (wa_map_state wa == waIsViewable || ic)
|
||||
|
||||
setNumlockMask :: X ()
|
||||
setNumlockMask = do
|
||||
dpy <- asks display
|
||||
ms <- io $ getModifierMapping dpy
|
||||
xs <- sequence [ do
|
||||
ks <- io $ keycodeToKeysym dpy kc 0
|
||||
if ks == xK_Num_Lock
|
||||
then return (setBit 0 (fromIntegral m))
|
||||
else return (0 :: KeyMask)
|
||||
| (m, kcs) <- ms, kc <- kcs, kc /= 0]
|
||||
modify (\s -> s { numberlockMask = foldr (.|.) 0 xs })
|
||||
|
||||
-- | Grab the keys back
|
||||
grabKeys :: X ()
|
||||
grabKeys = do
|
||||
@@ -300,7 +354,7 @@ grabKeys = do
|
||||
kc <- io $ keysymToKeycode dpy sym
|
||||
-- "If the specified KeySym is not defined for any KeyCode,
|
||||
-- XKeysymToKeycode() returns zero."
|
||||
when (kc /= '\0') $ mapM_ (grab kc . (mask .|.)) =<< extraModifiers
|
||||
when (kc /= 0) $ mapM_ (grab kc . (mask .|.)) =<< extraModifiers
|
||||
|
||||
-- | XXX comment me
|
||||
grabButtons :: X ()
|
||||
@@ -312,3 +366,36 @@ grabButtons = do
|
||||
ems <- extraModifiers
|
||||
ba <- asks buttonActions
|
||||
mapM_ (\(m,b) -> mapM_ (grab b . (m .|.)) ems) (M.keys $ ba)
|
||||
|
||||
-- | @replace@ to signals compliant window managers to exit.
|
||||
replace :: Display -> ScreenNumber -> Window -> IO ()
|
||||
replace dpy dflt rootw = do
|
||||
-- check for other WM
|
||||
wmSnAtom <- internAtom dpy ("WM_S" ++ show dflt) False
|
||||
currentWmSnOwner <- xGetSelectionOwner dpy wmSnAtom
|
||||
when (currentWmSnOwner /= 0) $ do
|
||||
-- prepare to receive destroyNotify for old WM
|
||||
selectInput dpy currentWmSnOwner structureNotifyMask
|
||||
|
||||
-- create off-screen window
|
||||
netWmSnOwner <- allocaSetWindowAttributes $ \attributes -> do
|
||||
set_override_redirect attributes True
|
||||
set_event_mask attributes propertyChangeMask
|
||||
let screen = defaultScreenOfDisplay dpy
|
||||
visual = defaultVisualOfScreen screen
|
||||
attrmask = cWOverrideRedirect .|. cWEventMask
|
||||
createWindow dpy rootw (-100) (-100) 1 1 0 copyFromParent copyFromParent visual attrmask attributes
|
||||
|
||||
-- try to acquire wmSnAtom, this should signal the old WM to terminate
|
||||
xSetSelectionOwner dpy wmSnAtom netWmSnOwner currentTime
|
||||
|
||||
-- SKIPPED: check if we acquired the selection
|
||||
-- SKIPPED: send client message indicating that we are now the WM
|
||||
|
||||
-- wait for old WM to go away
|
||||
fix $ \again -> do
|
||||
evt <- allocaXEvent $ \event -> do
|
||||
windowEvent dpy currentWmSnOwner structureNotifyMask event
|
||||
get_EventType event
|
||||
|
||||
when (evt /= destroyNotify) again
|
||||
|
@@ -22,7 +22,7 @@ import Prelude hiding (catch)
|
||||
import XMonad.Core
|
||||
import Graphics.X11.Xlib.Extras
|
||||
import Graphics.X11.Xlib (Display, Window, internAtom, wM_NAME)
|
||||
import Control.Exception (bracket, catch)
|
||||
import Control.Exception.Extensible (bracket, catch, SomeException(..))
|
||||
import Control.Monad.Reader
|
||||
import Data.Maybe
|
||||
import Data.Monoid
|
||||
@@ -34,20 +34,24 @@ liftX :: X a -> Query a
|
||||
liftX = Query . lift
|
||||
|
||||
-- | The identity hook that returns the WindowSet unchanged.
|
||||
idHook :: ManageHook
|
||||
idHook = doF id
|
||||
idHook :: Monoid m => m
|
||||
idHook = mempty
|
||||
|
||||
-- | Compose two 'ManageHook's.
|
||||
(<+>) :: ManageHook -> ManageHook -> ManageHook
|
||||
-- | Infix 'mappend'. Compose two 'ManageHook' from right to left.
|
||||
(<+>) :: Monoid m => m -> m -> m
|
||||
(<+>) = mappend
|
||||
|
||||
-- | Compose the list of 'ManageHook's.
|
||||
composeAll :: [ManageHook] -> ManageHook
|
||||
composeAll :: Monoid m => [m] -> m
|
||||
composeAll = mconcat
|
||||
|
||||
infix 0 -->
|
||||
|
||||
-- | @p --> x@. If @p@ returns 'True', execute the 'ManageHook'.
|
||||
(-->) :: Query Bool -> ManageHook -> ManageHook
|
||||
p --> f = p >>= \b -> if b then f else mempty
|
||||
--
|
||||
-- > (-->) :: Monoid m => Query Bool -> Query m -> Query m -- a simpler type
|
||||
(-->) :: (Monad m, Monoid a) => m Bool -> m a -> m a
|
||||
p --> f = p >>= \b -> if b then f else return mempty
|
||||
|
||||
-- | @q =? x@. if the result of @q@ equals @x@, return 'True'.
|
||||
(=?) :: Eq a => Query a -> a -> Query Bool
|
||||
@@ -70,9 +74,10 @@ title = ask >>= \w -> liftX $ do
|
||||
let
|
||||
getProp =
|
||||
(internAtom d "_NET_WM_NAME" False >>= getTextProperty d w)
|
||||
`catch` \_ -> getTextProperty d w wM_NAME
|
||||
extract = fmap head . wcTextPropertyToTextList d
|
||||
io $ bracket getProp (xFree . tp_value) extract `catch` \_ -> return ""
|
||||
`catch` \(SomeException _) -> getTextProperty d w wM_NAME
|
||||
extract prop = do l <- wcTextPropertyToTextList d prop
|
||||
return $ if null l then "" else head l
|
||||
io $ bracket getProp (xFree . tp_value) extract `catch` \(SomeException _) -> return ""
|
||||
|
||||
-- | Return the application name.
|
||||
appName :: Query String
|
||||
@@ -98,7 +103,7 @@ getStringProperty d w p = do
|
||||
return $ fmap (map (toEnum . fromIntegral)) md
|
||||
|
||||
-- | Modify the 'WindowSet' with a pure function.
|
||||
doF :: (WindowSet -> WindowSet) -> ManageHook
|
||||
doF :: (s -> s) -> Query (Endo s)
|
||||
doF = return . Endo
|
||||
|
||||
-- | Move the window to the floating layer.
|
||||
@@ -111,4 +116,4 @@ doIgnore = ask >>= \w -> liftX (reveal w) >> doF (W.delete w)
|
||||
|
||||
-- | Move the window to a given workspace
|
||||
doShift :: WorkspaceId -> ManageHook
|
||||
doShift = doF . W.shift
|
||||
doShift i = doF . W.shiftWin i =<< ask
|
||||
|
@@ -1,5 +1,4 @@
|
||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||
{-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards, TypeSynonymInstances #-}
|
||||
|
||||
-- --------------------------------------------------------------------------
|
||||
@@ -23,7 +22,7 @@ import XMonad.Layout (Full(..))
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import Data.Maybe
|
||||
import Data.Monoid (appEndo)
|
||||
import Data.Monoid (Endo(..))
|
||||
import Data.List (nub, (\\), find)
|
||||
import Data.Bits ((.|.), (.&.), complement)
|
||||
import Data.Ratio
|
||||
@@ -33,9 +32,8 @@ import qualified Data.Set as S
|
||||
import Control.Applicative
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.State
|
||||
import qualified Control.Exception as C
|
||||
import qualified Control.Exception.Extensible as C
|
||||
|
||||
import System.IO
|
||||
import System.Posix.Process (executeFile)
|
||||
import Graphics.X11.Xlib
|
||||
import Graphics.X11.Xinerama (getScreenInfo)
|
||||
@@ -68,7 +66,7 @@ manage w = whenX (not <$> isClient w) $ withDisplay $ \d -> do
|
||||
where i = W.tag $ W.workspace $ W.current ws
|
||||
|
||||
mh <- asks (manageHook . config)
|
||||
g <- fmap appEndo (runQuery mh w) `catchX` return id
|
||||
g <- appEndo <$> userCodeDef (Endo id) (runQuery mh w)
|
||||
windows (g . f)
|
||||
|
||||
-- | unmanage. A window no longer exists, remove it from the window
|
||||
@@ -77,14 +75,14 @@ manage w = whenX (not <$> isClient w) $ withDisplay $ \d -> do
|
||||
unmanage :: Window -> X ()
|
||||
unmanage = windows . W.delete
|
||||
|
||||
-- | Kill the currently focused client. If we do kill it, we'll get a
|
||||
-- | Kill the specified window. If we do kill it, we'll get a
|
||||
-- delete notify back from X.
|
||||
--
|
||||
-- There are two ways to delete a window. Either just kill it, or if it
|
||||
-- supports the delete protocol, send a delete event (e.g. firefox)
|
||||
--
|
||||
kill :: X ()
|
||||
kill = withDisplay $ \d -> withFocused $ \w -> do
|
||||
killWindow :: Window -> X ()
|
||||
killWindow w = withDisplay $ \d -> do
|
||||
wmdelt <- atom_WM_DELETE_WINDOW ; wmprot <- atom_WM_PROTOCOLS
|
||||
|
||||
protocols <- io $ getWMProtocols d w
|
||||
@@ -95,6 +93,10 @@ kill = withDisplay $ \d -> withFocused $ \w -> do
|
||||
sendEvent d w False noEventMask ev
|
||||
else killClient d w >> return ()
|
||||
|
||||
-- | Kill the currently focused client.
|
||||
kill :: X ()
|
||||
kill = withFocused killWindow
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Managing windows
|
||||
|
||||
@@ -120,38 +122,36 @@ windows f = do
|
||||
-- for each workspace, layout the currently visible workspaces
|
||||
let allscreens = W.screens ws
|
||||
summed_visible = scanl (++) [] $ map (W.integrate' . W.stack . W.workspace) allscreens
|
||||
visible <- fmap concat $ forM (zip allscreens summed_visible) $ \ (w, vis) -> do
|
||||
rects <- fmap concat $ forM (zip allscreens summed_visible) $ \ (w, vis) -> do
|
||||
let wsp = W.workspace w
|
||||
this = W.view n ws
|
||||
n = W.tag wsp
|
||||
flt = filter (flip M.member (W.floating ws)) (W.index this)
|
||||
tiled = (W.stack . W.workspace . W.current $ this)
|
||||
>>= W.filter (`M.notMember` W.floating ws)
|
||||
>>= W.filter (`notElem` vis)
|
||||
viewrect@(Rectangle sx sy sw sh) = screenRect $ W.screenDetail w
|
||||
viewrect = screenRect $ W.screenDetail w
|
||||
|
||||
-- just the tiled windows:
|
||||
-- now tile the windows on this workspace, modified by the gap
|
||||
(rs, ml') <- runLayout wsp { W.stack = tiled } viewrect `catchX`
|
||||
runLayout wsp { W.stack = tiled, W.layout = Layout Full } viewrect
|
||||
mapM_ (uncurry tileWindow) rs
|
||||
updateLayout n ml'
|
||||
|
||||
-- now the floating windows:
|
||||
-- move/resize the floating windows, if there are any
|
||||
forM_ flt $ \fw -> whenJust (M.lookup fw (W.floating ws)) $
|
||||
\(W.RationalRect rx ry rw rh) -> do
|
||||
tileWindow fw $ Rectangle
|
||||
(sx + floor (toRational sw*rx)) (sy + floor (toRational sh*ry))
|
||||
(floor (toRational sw*rw)) (floor (toRational sh*rh))
|
||||
let m = W.floating ws
|
||||
flt = [(fw, scaleRationalRect viewrect r)
|
||||
| fw <- filter (flip M.member m) (W.index this)
|
||||
, Just r <- [M.lookup fw m]]
|
||||
vs = flt ++ rs
|
||||
|
||||
let vs = flt ++ map fst rs
|
||||
io $ restackWindows d vs
|
||||
io $ restackWindows d (map fst vs)
|
||||
-- return the visible windows for this workspace:
|
||||
return vs
|
||||
|
||||
let visible = map fst rects
|
||||
|
||||
mapM_ (uncurry tileWindow) rects
|
||||
|
||||
whenJust (W.peek ws) $ \w -> io $ setWindowBorder d w fbc
|
||||
asks (logHook . config) >>= userCode
|
||||
|
||||
mapM_ reveal visible
|
||||
setTopFocus
|
||||
@@ -167,6 +167,13 @@ windows f = do
|
||||
|
||||
isMouseFocused <- asks mouseFocused
|
||||
unless isMouseFocused $ clearEvents enterWindowMask
|
||||
asks (logHook . config) >>= userCodeDef ()
|
||||
|
||||
-- | Produce the actual rectangle from a screen and a ratio on that screen.
|
||||
scaleRationalRect :: Rectangle -> W.RationalRect -> Rectangle
|
||||
scaleRationalRect (Rectangle sx sy sw sh) (W.RationalRect rx ry rw rh)
|
||||
= Rectangle (sx + scale sw rx) (sy + scale sh ry) (scale sw rw) (scale sh rh)
|
||||
where scale s r = floor (toRational s * r)
|
||||
|
||||
-- | setWMState. set the WM_STATE property
|
||||
setWMState :: Window -> Int -> X ()
|
||||
@@ -192,7 +199,7 @@ reveal :: Window -> X ()
|
||||
reveal w = withDisplay $ \d -> do
|
||||
setWMState w normalState
|
||||
io $ mapWindow d w
|
||||
modify (\s -> s { mapped = S.insert w (mapped s) })
|
||||
whenX (isClient w) $ modify (\s -> s { mapped = S.insert w (mapped s) })
|
||||
|
||||
-- | The client events that xmonad is interested in
|
||||
clientMask :: EventMask
|
||||
@@ -202,7 +209,7 @@ clientMask = structureNotifyMask .|. enterWindowMask .|. propertyChangeMask
|
||||
setInitialProperties :: Window -> X ()
|
||||
setInitialProperties w = asks normalBorder >>= \nb -> withDisplay $ \d -> do
|
||||
setWMState w iconicState
|
||||
io $ selectInput d w $ clientMask
|
||||
io $ selectInput d w clientMask
|
||||
bw <- asks (borderWidth . config)
|
||||
io $ setWindowBorderWidth d w bw
|
||||
-- we must initially set the color of new windows, to maintain invariants
|
||||
@@ -294,11 +301,17 @@ setTopFocus = withWindowSet $ maybe (setFocusX =<< asks theRoot) setFocusX . W.p
|
||||
-- This happens if X notices we've moved the mouse (and perhaps moved
|
||||
-- the mouse to a new screen).
|
||||
focus :: Window -> X ()
|
||||
focus w = withWindowSet $ \s -> do
|
||||
if W.member w s then when (W.peek s /= Just w) $ do
|
||||
local (\c -> c { mouseFocused = True }) $ do
|
||||
windows (W.focusWindow w)
|
||||
else whenX (isRoot w) $ setFocusX w
|
||||
focus w = local (\c -> c { mouseFocused = True }) $ withWindowSet $ \s -> do
|
||||
let stag = W.tag . W.workspace
|
||||
curr = stag $ W.current s
|
||||
mnew <- maybe (return Nothing) (fmap (fmap stag) . uncurry pointScreen)
|
||||
=<< asks mousePosition
|
||||
root <- asks theRoot
|
||||
case () of
|
||||
_ | W.member w s && W.peek s /= Just w -> windows (W.focusWindow w)
|
||||
| Just new <- mnew, w == root && curr /= new
|
||||
-> windows (W.view new)
|
||||
| otherwise -> return ()
|
||||
|
||||
-- | Call X to set the keyboard focus details.
|
||||
setFocusX :: Window -> X ()
|
||||
@@ -306,14 +319,13 @@ setFocusX w = withWindowSet $ \ws -> do
|
||||
dpy <- asks display
|
||||
|
||||
-- clear mouse button grab and border on other windows
|
||||
forM_ (W.current ws : W.visible ws) $ \wk -> do
|
||||
forM_ (W.index (W.view (W.tag (W.workspace wk)) ws)) $ \otherw -> do
|
||||
forM_ (W.current ws : W.visible ws) $ \wk ->
|
||||
forM_ (W.index (W.view (W.tag (W.workspace wk)) ws)) $ \otherw ->
|
||||
setButtonGrab True otherw
|
||||
|
||||
-- If we ungrab buttons on the root window, we lose our mouse bindings.
|
||||
whenX (not <$> isRoot w) $ setButtonGrab False w
|
||||
io $ do setInputFocus dpy w revertToPointerRoot 0
|
||||
-- raiseWindow dpy w
|
||||
io $ setInputFocus dpy w revertToPointerRoot 0
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Message handling
|
||||
@@ -324,7 +336,7 @@ sendMessage :: Message a => a -> X ()
|
||||
sendMessage a = do
|
||||
w <- W.workspace . W.current <$> gets windowset
|
||||
ml' <- handleMessage (W.layout w) (SomeMessage a) `catchX` return Nothing
|
||||
whenJust ml' $ \l' -> do
|
||||
whenJust ml' $ \l' ->
|
||||
windows $ \ws -> ws { W.current = (W.current ws)
|
||||
{ W.workspace = (W.workspace $ W.current ws)
|
||||
{ W.layout = l' }}}
|
||||
@@ -374,18 +386,18 @@ isClient w = withWindowSet $ return . W.member w
|
||||
-- (numlock and capslock)
|
||||
extraModifiers :: X [KeyMask]
|
||||
extraModifiers = do
|
||||
nlm <- asks (numlockMask . config)
|
||||
nlm <- gets numberlockMask
|
||||
return [0, nlm, lockMask, nlm .|. lockMask ]
|
||||
|
||||
-- | Strip numlock\/capslock from a mask
|
||||
cleanMask :: KeyMask -> X KeyMask
|
||||
cleanMask km = do
|
||||
nlm <- asks (numlockMask . config)
|
||||
nlm <- gets numberlockMask
|
||||
return (complement (nlm .|. lockMask) .&. km)
|
||||
|
||||
-- | Get the 'Pixel' value for a named color
|
||||
initColor :: Display -> String -> IO (Maybe Pixel)
|
||||
initColor dpy c = C.handle (\_ -> return Nothing) $
|
||||
initColor dpy c = C.handle (\(C.SomeException _) -> return Nothing) $
|
||||
(Just . color_pixel . fst) <$> allocNamedColor dpy colormap c
|
||||
where colormap = defaultColormap dpy (defaultScreen dpy)
|
||||
|
||||
@@ -398,9 +410,13 @@ 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 []
|
||||
let wsData = show . W.mapLayout show . windowset
|
||||
maybeShow (t, Right (PersistentExtension ext)) = Just (t, show ext)
|
||||
maybeShow (t, Left str) = Just (t, str)
|
||||
maybeShow _ = Nothing
|
||||
extState = return . show . catMaybes . map maybeShow . M.toList . extensibleState
|
||||
args <- if resume then gets (\s -> "--resume":wsData s:extState s) else return []
|
||||
catchIO (executeFile prog True args Nothing)
|
||||
where showWs = show . W.mapLayout show
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- | Floating layer support
|
||||
@@ -412,22 +428,30 @@ floatLocation w = withDisplay $ \d -> do
|
||||
ws <- gets windowset
|
||||
wa <- io $ getWindowAttributes d w
|
||||
bw <- fi <$> asks (borderWidth . config)
|
||||
sc <- fromMaybe (W.current ws) <$> pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
|
||||
|
||||
-- XXX horrible
|
||||
let sc = fromMaybe (W.current ws) $ find (pointWithin (fi $ wa_x wa) (fi $ wa_y wa) . screenRect . W.screenDetail) $ W.screens ws
|
||||
sr = screenRect . W.screenDetail $ sc
|
||||
let sr = screenRect . W.screenDetail $ sc
|
||||
rr = W.RationalRect ((fi (wa_x wa) - fi (rect_x sr)) % fi (rect_width sr))
|
||||
((fi (wa_y wa) - fi (rect_y sr)) % fi (rect_height sr))
|
||||
(fi (wa_width wa + bw*2) % fi (rect_width sr))
|
||||
(fi (wa_height wa + bw*2) % fi (rect_height sr))
|
||||
|
||||
return (W.screen $ sc, rr)
|
||||
return (W.screen sc, rr)
|
||||
where fi x = fromIntegral x
|
||||
pointWithin :: Integer -> Integer -> Rectangle -> Bool
|
||||
pointWithin x y r = x >= fi (rect_x r) &&
|
||||
x < fi (rect_x r) + fi (rect_width r) &&
|
||||
y >= fi (rect_y r) &&
|
||||
y < fi (rect_y r) + fi (rect_height r)
|
||||
|
||||
-- | Given a point, determine the screen (if any) that contains it.
|
||||
pointScreen :: Position -> Position
|
||||
-> X (Maybe (W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail))
|
||||
pointScreen x y = withWindowSet $ return . find p . W.screens
|
||||
where p = pointWithin x y . screenRect . W.screenDetail
|
||||
|
||||
-- | @pointWithin x y r@ returns 'True' if the @(x, y)@ co-ordinate is within
|
||||
-- @r@.
|
||||
pointWithin :: Position -> Position -> Rectangle -> Bool
|
||||
pointWithin x y r = x >= rect_x r &&
|
||||
x < rect_x r + fromIntegral (rect_width r) &&
|
||||
y >= rect_y r &&
|
||||
y < rect_y r + fromIntegral (rect_height r)
|
||||
|
||||
-- | Make a tiled window floating, using its suggested rectangle
|
||||
float :: Window -> X ()
|
||||
@@ -482,7 +506,7 @@ mouseResizeWindow w = whenX (isClient w) $ withDisplay $ \d -> do
|
||||
wa <- io $ getWindowAttributes d w
|
||||
sh <- io $ getWMNormalHints d w
|
||||
io $ warpPointer d none w 0 0 0 0 (fromIntegral (wa_width wa)) (fromIntegral (wa_height wa))
|
||||
mouseDrag (\ex ey -> do
|
||||
mouseDrag (\ex ey ->
|
||||
io $ resizeWindow d w `uncurry`
|
||||
applySizeHintsContents sh (ex - fromIntegral (wa_x wa),
|
||||
ey - fromIntegral (wa_y wa)))
|
||||
|
@@ -35,14 +35,14 @@ module XMonad.StackSet (
|
||||
-- * Operations on the current stack
|
||||
-- $stackOperations
|
||||
peek, index, integrate, integrate', differentiate,
|
||||
focusUp, focusDown, focusMaster, focusWindow,
|
||||
focusUp, focusDown, focusUp', focusDown', focusMaster, focusWindow,
|
||||
tagMember, renameTag, ensureTags, member, findTag, mapWorkspace, mapLayout,
|
||||
-- * Modifying the stackset
|
||||
-- $modifyStackset
|
||||
insertUp, delete, delete', filter,
|
||||
-- * Setting the master window
|
||||
-- $settingMW
|
||||
swapUp, swapDown, swapMaster, modify, modify', float, sink, -- needed by users
|
||||
swapUp, swapDown, swapMaster, shiftMaster, modify, modify', float, sink, -- needed by users
|
||||
-- * Composite operations
|
||||
-- $composite
|
||||
shift, shiftWin,
|
||||
@@ -52,7 +52,7 @@ module XMonad.StackSet (
|
||||
) where
|
||||
|
||||
import Prelude hiding (filter)
|
||||
import Data.Maybe (listToMaybe,fromJust,isJust)
|
||||
import Data.Maybe (listToMaybe,isJust,fromMaybe)
|
||||
import qualified Data.List as L (deleteBy,find,splitAt,filter,nub)
|
||||
import Data.List ( (\\) )
|
||||
import qualified Data.Map as M (Map,insert,delete,empty)
|
||||
@@ -155,7 +155,7 @@ data RationalRect = RationalRect Rational Rational Rational Rational
|
||||
deriving (Show, Read, Eq)
|
||||
|
||||
-- |
|
||||
-- A stack is a cursor onto a (possibly empty) window list.
|
||||
-- A stack is a cursor onto a window list.
|
||||
-- The data structure tracks focus by construction, and
|
||||
-- the master window is by convention the top-most item.
|
||||
-- Focus operations will not reorder the list that results from
|
||||
@@ -194,7 +194,8 @@ abort x = error $ "xmonad: StackSet: " ++ x
|
||||
-- Xinerama: Virtual workspaces are assigned to physical screens, starting at 0.
|
||||
--
|
||||
new :: (Integral s) => l -> [i] -> [sd] -> StackSet i l a s sd
|
||||
new l wids m | not (null wids) && length m <= length wids = StackSet cur visi unseen M.empty
|
||||
new l wids m | not (null wids) && length m <= length wids && not (null m)
|
||||
= StackSet cur visi unseen M.empty
|
||||
where (seen,unseen) = L.splitAt (length m) $ map (\i -> Workspace i l Nothing) wids
|
||||
(cur:visi) = [ Screen i s sd | (i, s, sd) <- zip3 seen [0..] m ]
|
||||
-- now zip up visibles with their screen id
|
||||
@@ -342,15 +343,19 @@ index = with [] integrate
|
||||
--
|
||||
focusUp, focusDown, swapUp, swapDown :: StackSet i l a s sd -> StackSet i l a s sd
|
||||
focusUp = modify' focusUp'
|
||||
focusDown = modify' (reverseStack . focusUp' . reverseStack)
|
||||
focusDown = modify' focusDown'
|
||||
|
||||
swapUp = modify' swapUp'
|
||||
swapDown = modify' (reverseStack . swapUp' . reverseStack)
|
||||
|
||||
focusUp', swapUp' :: Stack a -> Stack a
|
||||
-- | Variants of 'focusUp' and 'focusDown' that work on a
|
||||
-- 'Stack' rather than an entire 'StackSet'.
|
||||
focusUp', focusDown' :: Stack a -> Stack a
|
||||
focusUp' (Stack t (l:ls) rs) = Stack l ls (t:rs)
|
||||
focusUp' (Stack t [] rs) = Stack x xs [] where (x:xs) = reverse (t:rs)
|
||||
focusDown' = reverseStack . focusUp' . reverseStack
|
||||
|
||||
swapUp' :: Stack a -> Stack a
|
||||
swapUp' (Stack t (l:ls) rs) = Stack t ls (l:rs)
|
||||
swapUp' (Stack t [] rs) = Stack t (reverse rs) []
|
||||
|
||||
@@ -364,7 +369,7 @@ reverseStack (Stack t ls rs) = Stack t rs ls
|
||||
--
|
||||
focusWindow :: (Eq s, Eq a, Eq i) => a -> StackSet i l a s sd -> StackSet i l a s sd
|
||||
focusWindow w s | Just w == peek s = s
|
||||
| otherwise = maybe s id $ do
|
||||
| otherwise = fromMaybe s $ do
|
||||
n <- findTag w s
|
||||
return $ until ((Just w ==) . peek) focusUp (view n s)
|
||||
|
||||
@@ -508,6 +513,15 @@ swapMaster = modify' $ \c -> case c of
|
||||
|
||||
-- natural! keep focus, move current to the top, move top to current.
|
||||
|
||||
-- | /O(s)/. Set the master window to the focused window.
|
||||
-- The other windows are kept in order and shifted down on the stack, as if you
|
||||
-- just hit mod-shift-k a bunch of times.
|
||||
-- Focus stays with the item moved.
|
||||
shiftMaster :: StackSet i l a s sd -> StackSet i l a s sd
|
||||
shiftMaster = modify' $ \c -> case c of
|
||||
Stack _ [] _ -> c -- already master.
|
||||
Stack t ls rs -> Stack t [] (reverse ls ++ rs)
|
||||
|
||||
-- | /O(s)/. Set focus to the master window.
|
||||
focusMaster :: StackSet i l a s sd -> StackSet i l a s sd
|
||||
focusMaster = modify' $ \c -> case c of
|
||||
@@ -525,10 +539,7 @@ focusMaster = modify' $ \c -> case c of
|
||||
-- element on the current stack, the original stackSet is returned.
|
||||
--
|
||||
shift :: (Ord a, Eq s, Eq i) => i -> StackSet i l a s sd -> StackSet i l a s sd
|
||||
shift n s | n `tagMember` s && n /= curtag = maybe s go (peek s)
|
||||
| otherwise = s
|
||||
where go w = view curtag . insertUp w . view n . delete' w $ s
|
||||
curtag = currentTag s
|
||||
shift n s = maybe s (\w -> shiftWin n w s) (peek s)
|
||||
|
||||
-- | /O(n)/. shiftWin. Searches for the specified window 'w' on all workspaces
|
||||
-- of the stackSet and moves it to stack 'n', leaving it as the focused
|
||||
@@ -536,13 +547,12 @@ shift n s | n `tagMember` s && n /= curtag = maybe s go (peek s)
|
||||
-- focused element on that workspace.
|
||||
-- The actual focused workspace doesn't change. If the window is not
|
||||
-- found in the stackSet, the original stackSet is returned.
|
||||
-- TODO how does this duplicate 'shift's behaviour?
|
||||
shiftWin :: (Ord a, Eq a, Eq s, Eq i) => i -> a -> StackSet i l a s sd -> StackSet i l a s sd
|
||||
shiftWin n w s | from == Nothing = s -- not found
|
||||
| n `tagMember` s && (Just n) /= from = go
|
||||
| otherwise = s
|
||||
where from = findTag w s
|
||||
|
||||
go = on n (insertUp w) . on (fromJust from) (delete' w) $ s
|
||||
on i f = view (currentTag s) . f . view i
|
||||
shiftWin n w s = case findTag w s of
|
||||
Just from | n `tagMember` s && n /= from -> go from s
|
||||
_ -> s
|
||||
where go from = onWorkspace n (insertUp w) . onWorkspace from (delete' w)
|
||||
|
||||
onWorkspace :: (Eq i, Eq s) => i -> (StackSet i l a s sd -> StackSet i l a s sd)
|
||||
-> (StackSet i l a s sd -> StackSet i l a s sd)
|
||||
onWorkspace n f s = view (currentTag s) . f . view n $ s
|
||||
|
68
man/HCAR.tex
Normal file
68
man/HCAR.tex
Normal file
@@ -0,0 +1,68 @@
|
||||
% xmonad-Gx.tex
|
||||
\begin{hcarentry}{xmonad}
|
||||
\label{xmonad}
|
||||
\report{Gwern Branwen}%05/10
|
||||
\status{active development}
|
||||
\makeheader
|
||||
|
||||
XMonad is a tiling window manager for X. Windows are arranged
|
||||
automatically to tile the screen without gaps or overlap, maximizing
|
||||
screen use. Window manager features are accessible from the keyboard; a
|
||||
mouse is optional. XMonad is written, configured, and extensible in
|
||||
Haskell. Custom layout algorithms, key bindings, and other extensions may
|
||||
be written by the user in config files. Layouts are applied
|
||||
dynamically, and different layouts may be used on each workspace.
|
||||
Xinerama is fully supported, allowing windows to be tiled on several
|
||||
physical screens.
|
||||
|
||||
Development since the last report has continued apace, with versions
|
||||
0.8, 0.8.1, 0.9 and 0.9.1 released, with simultaneous releases of the
|
||||
XMonadContrib library of customizations and extensions, which has now
|
||||
grown to no less than 205 modules encompassing a dizzying array of features.
|
||||
|
||||
Details of changes between releases can be found in the release notes:
|
||||
\begin{compactitem}
|
||||
\item \url{http://haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.7}
|
||||
\item \url{http://haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.8}
|
||||
\item \url{http://haskell.org/haskellwiki/Xmonad/Notable_changes_since_0.9}
|
||||
\item XMonad.Config.PlainConfig allows writing configs in a more 'normal' style, and not raw Haskell
|
||||
\item Supports using local modules in xmonad.hs; for example: to use definitions from \~/.xmonad/lib/XMonad/Stack/MyAdditions.hs
|
||||
\item xmonad --restart CLI option
|
||||
\item xmonad --replace CLI option
|
||||
\item XMonad.Prompt now has customizable keymaps
|
||||
\item Actions.GridSelect - a GUI menu for selecting windows or workspaces \& substring search on window names
|
||||
\item Actions.OnScreen
|
||||
\item Extensions now can have state
|
||||
\item Actions.SpawnOn - uses state to spawn applications on the workspace the user was originally on,
|
||||
and not where the user happens to be
|
||||
\item Markdown manpages and not man/troff
|
||||
\item XMonad.Layout.ImageButtonDecoration \&\\ XMonad.Util.Image
|
||||
\item XMonad.Layout.Groups
|
||||
\item XMonad.Layout.ZoomRow
|
||||
\item XMonad.Layout.Renamed
|
||||
\item XMonad.Layout.Drawer
|
||||
\item XMonad.Layout.FullScreen
|
||||
\item XMonad.Hooks.ScreenCorners
|
||||
\item XMonad.Actions.DynamicWorkspaceOrder
|
||||
\item XMonad.Actions.WorkspaceNames
|
||||
\item XMonad.Actions.DynamicWorkspaceGroups
|
||||
\end{compactitem}
|
||||
|
||||
Binary packages of XMonad and XMonadContrib are available for all major Linux distributions.
|
||||
|
||||
\FurtherReading
|
||||
\begin{compactitem}
|
||||
\item Homepage:
|
||||
\url{http://xmonad.org/}
|
||||
|
||||
\item Darcs source:
|
||||
|
||||
\texttt{darcs get} \url{http://code.haskell.org/xmonad}
|
||||
|
||||
\item IRC channel:
|
||||
\verb+#xmonad @@ irc.freenode.org+
|
||||
|
||||
\item Mailing list:
|
||||
\email{xmonad@@haskell.org}
|
||||
\end{compactitem}
|
||||
\end{hcarentry}
|
@@ -1,40 +0,0 @@
|
||||
./" man page created by David Lazar on April 24, 2007
|
||||
./" uses ``tmac.an'' macro set
|
||||
.TH xmonad 1 "18 April 07" xmonad\-1.0 "xmonad manual"
|
||||
.SH NAME
|
||||
xmonad \- a tiling window manager
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
\fBxmonad\fR is a minimalist tiling window manager for X, written in Haskell. Windows are managed using automatic layout algorithms, which can be dynamically reconfigured. At any time windows are arranged so as to maximise the use of screen real estate. All features of the window manager are accessible purely from the keyboard: a mouse is entirely optional. \fBxmonad\fR is configured in Haskell, and custom layout algorithms may be implemented by the user in config files. A principle of \fBxmonad\fR is predictability: the user should know in advance precisely the window arrangement that will result from any action.
|
||||
.PP
|
||||
By default, \fBxmonad\fR provides three layout algorithms: tall, wide and fullscreen. In tall or wide mode, windows are tiled and arranged to prevent overlap and maximise screen use. Sets of windows are grouped together on virtual screens, and each screen retains its own layout, which may be reconfigured dynamically. Multiple physical monitors are supported via Xinerama, allowing simultaneous display of a number of screens.
|
||||
.PP
|
||||
By utilising the expressivity of a modern functional language with a rich static type system, \fBxmonad\fR provides a complete, featureful window manager in less than 1200 lines of code, with an emphasis on correctness and robustness. Internal properties of the window manager are checked using a combination of static guarantees provided by the type system, and type-based automated testing. A benefit of this is that the code is simple to understand, and easy to modify.
|
||||
.SH USAGE
|
||||
.PP
|
||||
\fBxmonad\fR places each window into a "workspace". Each workspace can have any number of windows, which you can cycle though with mod-j and mod-k. Windows are either displayed full screen, tiled horizontally, or tiled vertically. You can toggle the layout mode with mod-space, which will cycle through the available modes.
|
||||
.PP
|
||||
You can switch to workspace N with mod-N. For example, to switch to workspace 5, you would press mod-5. Similarly, you can move the current window to another workspace with mod-shift-N.
|
||||
.PP
|
||||
When running with multiple monitors (Xinerama), each screen has exactly 1 workspace visible. mod-{w,e,r} switch the focus between screens, while shift-mod-{w,e,r} move the current window to that screen. When \fBxmonad\fR starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. When switching workspaces to one that is already visible, the current and visible workspaces are swapped.
|
||||
.PP
|
||||
.SS Flags
|
||||
\fBxmonad\fR has several flags which you may pass to the executable. These flags are:
|
||||
.TP
|
||||
\fB--recompile
|
||||
Recompiles your configuration in ~/.xmonad/xmonad.hs
|
||||
.TP
|
||||
\fB--version
|
||||
Display version of \fBxmonad\fR.
|
||||
.SS Default keyboard bindings
|
||||
___KEYBINDINGS___
|
||||
.SH EXAMPLES
|
||||
To use \fBxmonad\fR as your window manager add:
|
||||
.RS
|
||||
xmonad
|
||||
.RE
|
||||
to your \fI~/.xinitrc\fR file
|
||||
.SH CUSTOMIZATION
|
||||
\fBxmonad\fR is customized in ~/.xmonad/xmonad.hs, and then restarting with mod-q.
|
||||
.SH BUGS
|
||||
Probably. If you find any, please report them: http://code.google.com/p/xmonad/issues/list
|
102
man/xmonad.1.markdown
Normal file
102
man/xmonad.1.markdown
Normal file
@@ -0,0 +1,102 @@
|
||||
#Name
|
||||
xmonad - a tiling window manager
|
||||
|
||||
#Description
|
||||
|
||||
_xmonad_ is a minimalist tiling window manager for X, written in Haskell.
|
||||
Windows are managed using automatic layout algorithms, which can be
|
||||
dynamically reconfigured. At any time windows are arranged so as to
|
||||
maximize the use of screen real estate. All features of the window manager
|
||||
are accessible purely from the keyboard: a mouse is entirely optional.
|
||||
_xmonad_ is configured in Haskell, and custom layout algorithms may be
|
||||
implemented by the user in config files. A principle of _xmonad_ is
|
||||
predictability: the user should know in advance precisely the window
|
||||
arrangement that will result from any action.
|
||||
|
||||
By default, _xmonad_ provides three layout algorithms: tall, wide and
|
||||
fullscreen. In tall or wide mode, windows are tiled and arranged to prevent
|
||||
overlap and maximize screen use. Sets of windows are grouped together on
|
||||
virtual screens, and each screen retains its own layout, which may be
|
||||
reconfigured dynamically. Multiple physical monitors are supported via
|
||||
Xinerama, allowing simultaneous display of a number of screens.
|
||||
|
||||
By utilizing the expressivity of a modern functional language with a rich
|
||||
static type system, _xmonad_ provides a complete, featureful window manager
|
||||
in less than 1200 lines of code, with an emphasis on correctness and
|
||||
robustness. Internal properties of the window manager are checked using a
|
||||
combination of static guarantees provided by the type system, and
|
||||
type-based automated testing. A benefit of this is that the code is simple
|
||||
to understand, and easy to modify.
|
||||
|
||||
#Usage
|
||||
|
||||
_xmonad_ places each window into a "workspace". Each workspace can have
|
||||
any number of windows, which you can cycle though with mod-j and mod-k.
|
||||
Windows are either displayed full screen, tiled horizontally, or tiled
|
||||
vertically. You can toggle the layout mode with mod-space, which will cycle
|
||||
through the available modes.
|
||||
|
||||
You can switch to workspace N with mod-N. For example, to switch to
|
||||
workspace 5, you would press mod-5. Similarly, you can move the current
|
||||
window to another workspace with mod-shift-N.
|
||||
|
||||
When running with multiple monitors (Xinerama), each screen has exactly 1
|
||||
workspace visible. mod-{w,e,r} switch the focus between screens, while
|
||||
shift-mod-{w,e,r} move the current window to that screen. When _xmonad_
|
||||
starts, workspace 1 is on screen 1, workspace 2 is on screen 2, etc. When
|
||||
switching workspaces to one that is already visible, the current and
|
||||
visible workspaces are swapped.
|
||||
|
||||
##Flags
|
||||
xmonad has several flags which you may pass to the executable.
|
||||
These flags are:
|
||||
|
||||
--recompile
|
||||
: Recompiles your configuration in _~/.xmonad/xmonad.hs_
|
||||
|
||||
--restart
|
||||
: Causes the currently running _xmonad_ process to restart
|
||||
|
||||
--replace
|
||||
: Replace the current window manager with xmonad
|
||||
|
||||
--version
|
||||
: Display version of _xmonad_
|
||||
|
||||
--verbose-version
|
||||
: Display detailed version of _xmonad_
|
||||
|
||||
##Default keyboard bindings
|
||||
|
||||
___KEYBINDINGS___
|
||||
|
||||
#Examples
|
||||
To use xmonad as your window manager add to your _~/.xinitrc_ file:
|
||||
|
||||
> exec xmonad
|
||||
|
||||
#Customization
|
||||
xmonad is customized in ~/.xmonad/xmonad.hs, and then restarting
|
||||
with mod-q.
|
||||
|
||||
You can find many extensions to the core feature set in the xmonad-
|
||||
contrib package, available through your package manager or from
|
||||
[xmonad.org].
|
||||
|
||||
##Modular Configuration
|
||||
As of _xmonad-0.9_, any additional Haskell modules may be placed in
|
||||
_~/.xmonad/lib/_ are available in GHC's searchpath. Hierarchical modules
|
||||
are supported: for example, the file
|
||||
_~/.xmonad/lib/XMonad/Stack/MyAdditions.hs_ could contain:
|
||||
|
||||
> module XMonad.Stack.MyAdditions (function1) where
|
||||
> function1 = error "function1: Not implemented yet!"
|
||||
|
||||
Your xmonad.hs may then import XMonad.Stack.MyAdditions as if that
|
||||
module was contained within xmonad or xmonad-contrib.
|
||||
|
||||
#Bugs
|
||||
Probably. If you find any, please report them to the [bugtracker]
|
||||
|
||||
[xmonad.org]: http://xmonad.org
|
||||
[bugtracker]: http://code.google.com/p/xmonad/issues/list
|
111
man/xmonad.hs
111
man/xmonad.hs
@@ -8,6 +8,7 @@
|
||||
--
|
||||
|
||||
import XMonad
|
||||
import Data.Monoid
|
||||
import System.Exit
|
||||
|
||||
import qualified XMonad.StackSet as W
|
||||
@@ -18,6 +19,10 @@ import qualified Data.Map as M
|
||||
--
|
||||
myTerminal = "xterm"
|
||||
|
||||
-- Whether focus follows the mouse pointer.
|
||||
myFocusFollowsMouse :: Bool
|
||||
myFocusFollowsMouse = True
|
||||
|
||||
-- Width of the window border in pixels.
|
||||
--
|
||||
myBorderWidth = 1
|
||||
@@ -29,21 +34,6 @@ myBorderWidth = 1
|
||||
--
|
||||
myModMask = mod1Mask
|
||||
|
||||
-- The mask for the numlock key. Numlock status is "masked" from the
|
||||
-- current modifier status, so the keybindings will work with numlock on or
|
||||
-- off. You may need to change this on some systems.
|
||||
--
|
||||
-- You can find the numlock modifier by running "xmodmap" and looking for a
|
||||
-- modifier with Num_Lock bound to it:
|
||||
--
|
||||
-- > $ xmodmap | grep Num
|
||||
-- > mod2 Num_Lock (0x4d)
|
||||
--
|
||||
-- Set numlockMask = 0 if you don't have a numlock key, or want to treat
|
||||
-- numlock status separately.
|
||||
--
|
||||
myNumlockMask = mod2Mask
|
||||
|
||||
-- The default number of workspaces (virtual screens) and their names.
|
||||
-- By default we use numeric strings, but any string may be used as a
|
||||
-- workspace name. The number of workspaces is determined by the length
|
||||
@@ -63,73 +53,76 @@ myFocusedBorderColor = "#ff0000"
|
||||
------------------------------------------------------------------------
|
||||
-- Key bindings. Add, modify or remove key bindings here.
|
||||
--
|
||||
myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $
|
||||
|
||||
-- launch a terminal
|
||||
[ ((modMask .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
|
||||
[ ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
|
||||
|
||||
-- launch dmenu
|
||||
, ((modMask, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
|
||||
, ((modm, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
|
||||
|
||||
-- launch gmrun
|
||||
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun")
|
||||
, ((modm .|. shiftMask, xK_p ), spawn "gmrun")
|
||||
|
||||
-- close focused window
|
||||
, ((modMask .|. shiftMask, xK_c ), kill)
|
||||
-- close focused window
|
||||
, ((modm .|. shiftMask, xK_c ), kill)
|
||||
|
||||
-- Rotate through the available layout algorithms
|
||||
, ((modMask, xK_space ), sendMessage NextLayout)
|
||||
, ((modm, xK_space ), sendMessage NextLayout)
|
||||
|
||||
-- Reset the layouts on the current workspace to default
|
||||
, ((modMask .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf)
|
||||
, ((modm .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf)
|
||||
|
||||
-- Resize viewed windows to the correct size
|
||||
, ((modMask, xK_n ), refresh)
|
||||
, ((modm, xK_n ), refresh)
|
||||
|
||||
-- Move focus to the next window
|
||||
, ((modMask, xK_Tab ), windows W.focusDown)
|
||||
, ((modm, xK_Tab ), windows W.focusDown)
|
||||
|
||||
-- Move focus to the next window
|
||||
, ((modMask, xK_j ), windows W.focusDown)
|
||||
, ((modm, xK_j ), windows W.focusDown)
|
||||
|
||||
-- Move focus to the previous window
|
||||
, ((modMask, xK_k ), windows W.focusUp )
|
||||
, ((modm, xK_k ), windows W.focusUp )
|
||||
|
||||
-- Move focus to the master window
|
||||
, ((modMask, xK_m ), windows W.focusMaster )
|
||||
, ((modm, xK_m ), windows W.focusMaster )
|
||||
|
||||
-- Swap the focused window and the master window
|
||||
, ((modMask, xK_Return), windows W.swapMaster)
|
||||
, ((modm, xK_Return), windows W.swapMaster)
|
||||
|
||||
-- Swap the focused window with the next window
|
||||
, ((modMask .|. shiftMask, xK_j ), windows W.swapDown )
|
||||
, ((modm .|. shiftMask, xK_j ), windows W.swapDown )
|
||||
|
||||
-- Swap the focused window with the previous window
|
||||
, ((modMask .|. shiftMask, xK_k ), windows W.swapUp )
|
||||
, ((modm .|. shiftMask, xK_k ), windows W.swapUp )
|
||||
|
||||
-- Shrink the master area
|
||||
, ((modMask, xK_h ), sendMessage Shrink)
|
||||
, ((modm, xK_h ), sendMessage Shrink)
|
||||
|
||||
-- Expand the master area
|
||||
, ((modMask, xK_l ), sendMessage Expand)
|
||||
, ((modm, xK_l ), sendMessage Expand)
|
||||
|
||||
-- Push window back into tiling
|
||||
, ((modMask, xK_t ), withFocused $ windows . W.sink)
|
||||
, ((modm, xK_t ), withFocused $ windows . W.sink)
|
||||
|
||||
-- Increment the number of windows in the master area
|
||||
, ((modMask , xK_comma ), sendMessage (IncMasterN 1))
|
||||
, ((modm , xK_comma ), sendMessage (IncMasterN 1))
|
||||
|
||||
-- Deincrement the number of windows in the master area
|
||||
, ((modMask , xK_period), sendMessage (IncMasterN (-1)))
|
||||
, ((modm , xK_period), sendMessage (IncMasterN (-1)))
|
||||
|
||||
-- toggle the status bar gap
|
||||
-- TODO, update this binding with avoidStruts , ((modMask , xK_b ),
|
||||
-- Toggle the status bar gap
|
||||
-- Use this binding with avoidStruts from Hooks.ManageDocks.
|
||||
-- See also the statusBar function from Hooks.DynamicLog.
|
||||
--
|
||||
-- , ((modm , xK_b ), sendMessage ToggleStruts)
|
||||
|
||||
-- Quit xmonad
|
||||
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess))
|
||||
, ((modm .|. shiftMask, xK_q ), io (exitWith ExitSuccess))
|
||||
|
||||
-- Restart xmonad
|
||||
, ((modMask , xK_q ), restart "xmonad" True)
|
||||
, ((modm , xK_q ), spawn "xmonad --recompile; xmonad --restart")
|
||||
]
|
||||
++
|
||||
|
||||
@@ -137,7 +130,7 @@ myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
-- mod-[1..9], Switch to workspace N
|
||||
-- mod-shift-[1..9], Move client to workspace N
|
||||
--
|
||||
[((m .|. modMask, k), windows $ f i)
|
||||
[((m .|. modm, k), windows $ f i)
|
||||
| (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
|
||||
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
|
||||
++
|
||||
@@ -146,7 +139,7 @@ myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
-- mod-{w,e,r}, Switch to physical/Xinerama screens 1, 2, or 3
|
||||
-- mod-shift-{w,e,r}, Move client to screen 1, 2, or 3
|
||||
--
|
||||
[((m .|. modMask, key), screenWorkspace sc >>= flip whenJust (windows . f))
|
||||
[((m .|. modm, key), screenWorkspace sc >>= flip whenJust (windows . f))
|
||||
| (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
|
||||
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
|
||||
|
||||
@@ -154,16 +147,18 @@ myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
------------------------------------------------------------------------
|
||||
-- Mouse bindings: default actions bound to mouse events
|
||||
--
|
||||
myMouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList $
|
||||
myMouseBindings (XConfig {XMonad.modMask = modm}) = M.fromList $
|
||||
|
||||
-- mod-button1, Set the window to floating mode and move by dragging
|
||||
[ ((modMask, button1), (\w -> focus w >> mouseMoveWindow w))
|
||||
[ ((modm, button1), (\w -> focus w >> mouseMoveWindow w
|
||||
>> windows W.shiftMaster))
|
||||
|
||||
-- mod-button2, Raise the window to the top of the stack
|
||||
, ((modMask, button2), (\w -> focus w >> windows W.swapMaster))
|
||||
, ((modm, button2), (\w -> focus w >> windows W.shiftMaster))
|
||||
|
||||
-- mod-button3, Set the window to floating mode and resize by dragging
|
||||
, ((modMask, button3), (\w -> focus w >> mouseResizeWindow w))
|
||||
, ((modm, button3), (\w -> focus w >> mouseResizeWindow w
|
||||
>> windows W.shiftMaster))
|
||||
|
||||
-- you may also bind events to the mouse scroll wheel (button4 and button5)
|
||||
]
|
||||
@@ -214,20 +209,22 @@ myManageHook = composeAll
|
||||
, resource =? "desktop_window" --> doIgnore
|
||||
, resource =? "kdesktop" --> doIgnore ]
|
||||
|
||||
-- Whether focus follows the mouse pointer.
|
||||
myFocusFollowsMouse :: Bool
|
||||
myFocusFollowsMouse = True
|
||||
------------------------------------------------------------------------
|
||||
-- Event handling
|
||||
|
||||
-- * EwmhDesktops users should change this to ewmhDesktopsEventHook
|
||||
--
|
||||
-- Defines a custom handler function for X Events. The function should
|
||||
-- return (All True) if the default handler is to be run afterwards. To
|
||||
-- combine event hooks use mappend or mconcat from Data.Monoid.
|
||||
--
|
||||
myEventHook = mempty
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Status bars and logging
|
||||
|
||||
-- Perform an arbitrary action on each internal state change or X event.
|
||||
-- See the 'DynamicLog' extension for examples.
|
||||
--
|
||||
-- To emulate dwm's status bar
|
||||
--
|
||||
-- > logHook = dynamicLogDzen
|
||||
-- See the 'XMonad.Hooks.DynamicLog' extension for examples.
|
||||
--
|
||||
myLogHook = return ()
|
||||
|
||||
@@ -249,9 +246,9 @@ myStartupHook = return ()
|
||||
main = xmonad defaults
|
||||
|
||||
-- A structure containing your configuration settings, overriding
|
||||
-- fields in the default config. Any you don't override, will
|
||||
-- fields in the default config. Any you don't override, will
|
||||
-- use the defaults defined in xmonad/XMonad/Config.hs
|
||||
--
|
||||
--
|
||||
-- No need to modify this.
|
||||
--
|
||||
defaults = defaultConfig {
|
||||
@@ -260,7 +257,6 @@ defaults = defaultConfig {
|
||||
focusFollowsMouse = myFocusFollowsMouse,
|
||||
borderWidth = myBorderWidth,
|
||||
modMask = myModMask,
|
||||
numlockMask = myNumlockMask,
|
||||
workspaces = myWorkspaces,
|
||||
normalBorderColor = myNormalBorderColor,
|
||||
focusedBorderColor = myFocusedBorderColor,
|
||||
@@ -272,6 +268,7 @@ defaults = defaultConfig {
|
||||
-- hooks, layouts
|
||||
layoutHook = myLayout,
|
||||
manageHook = myManageHook,
|
||||
handleEventHook = myEventHook,
|
||||
logHook = myLogHook,
|
||||
startupHook = myStartupHook
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ import Data.Ratio
|
||||
import Data.Maybe
|
||||
import System.Environment
|
||||
import Control.Exception (assert)
|
||||
import qualified Control.Exception as C
|
||||
import qualified Control.Exception.Extensible as C
|
||||
import Control.Monad
|
||||
import Test.QuickCheck hiding (promote)
|
||||
import System.IO.Unsafe
|
||||
@@ -528,6 +528,18 @@ prop_shift_reversible i (x :: T) =
|
||||
y = swapMaster x
|
||||
n = tag (workspace $ current y)
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- shiftMaster
|
||||
|
||||
-- focus/local/idempotent same as swapMaster:
|
||||
prop_shift_master_focus (x :: T) = peek x == (peek $ shiftMaster x)
|
||||
prop_shift_master_local (x :: T) = hidden_spaces x == hidden_spaces (shiftMaster x)
|
||||
prop_shift_master_idempotent (x :: T) = shiftMaster (shiftMaster x) == shiftMaster x
|
||||
-- ordering is constant modulo the focused window:
|
||||
prop_shift_master_ordering (x :: T) = case peek x of
|
||||
Nothing -> True
|
||||
Just m -> L.delete m (index x) == L.delete m (index $ shiftMaster x)
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- shiftWin
|
||||
|
||||
@@ -601,13 +613,13 @@ prop_lookup_visible (x :: T) =
|
||||
|
||||
-- and help out hpc
|
||||
prop_abort x = unsafePerformIO $ C.catch (abort "fail")
|
||||
(\e -> return $ show e == "xmonad: StackSet: fail" )
|
||||
(\(C.SomeException e) -> return $ show e == "xmonad: StackSet: fail" )
|
||||
where
|
||||
_ = x :: Int
|
||||
|
||||
-- new should fail with an abort
|
||||
prop_new_abort x = unsafePerformIO $ C.catch f
|
||||
(\e -> return $ show e == "xmonad: StackSet: non-positive argument to StackSet.new" )
|
||||
(\(C.SomeException e) -> return $ show e == "xmonad: StackSet: non-positive argument to StackSet.new" )
|
||||
where
|
||||
f = new undefined{-layout-} [] [] `seq` return False
|
||||
|
||||
@@ -933,6 +945,11 @@ main = do
|
||||
,("swapUp is local" , mytest prop_swap_left_local)
|
||||
,("swapDown is local" , mytest prop_swap_right_local)
|
||||
|
||||
,("shiftMaster id on focus", mytest prop_shift_master_focus)
|
||||
,("shiftMaster is local", mytest prop_shift_master_local)
|
||||
,("shiftMaster is idempotent", mytest prop_shift_master_idempotent)
|
||||
,("shiftMaster preserves ordering", mytest prop_shift_master_ordering)
|
||||
|
||||
,("shift: invariant" , mytest prop_shift_I)
|
||||
,("shift is reversible" , mytest prop_shift_reversible)
|
||||
,("shiftWin: invariant" , mytest prop_shift_win_I)
|
||||
|
@@ -5,9 +5,9 @@ main = do foo <- getContents
|
||||
let actual_loc = filter (not.null) $ filter isntcomment $
|
||||
map (dropWhile (==' ')) $ lines foo
|
||||
loc = length actual_loc
|
||||
putStrLn $ show loc
|
||||
print loc
|
||||
-- uncomment the following to check for mistakes in isntcomment
|
||||
-- putStr $ unlines $ actual_loc
|
||||
-- print actual_loc
|
||||
|
||||
isntcomment ('-':'-':_) = False
|
||||
isntcomment ('{':'-':_) = False -- pragmas
|
||||
|
@@ -1,7 +1,14 @@
|
||||
-- Unlike the rest of xmonad, this file is copyright under the terms of the
|
||||
-- GPL.
|
||||
|
||||
--
|
||||
-- Generates man/xmonad.1 from man/xmonad.1.in by filling the list of
|
||||
-- keybindings with values scraped from Config.hs
|
||||
--
|
||||
-- Uses cabal to grab the xmonad version from xmonad.cabal
|
||||
--
|
||||
-- Uses pandoc to convert the "xmonad.1.markdown" to "xmonad.1"
|
||||
--
|
||||
-- Format for the docstrings in Config.hs takes the following form:
|
||||
--
|
||||
-- -- mod-x %! Frob the whatsit
|
||||
@@ -14,12 +21,23 @@
|
||||
-- [ ((modMask .|. shiftMask, xK_Return), spawn "xterm") -- %! Launch an xterm
|
||||
--
|
||||
-- Here, mod-shift-return will be used as the keybinding name.
|
||||
--
|
||||
import Control.Monad
|
||||
import Control.Applicative
|
||||
import Text.Regex.Posix
|
||||
import Data.Char
|
||||
import Data.List
|
||||
|
||||
import Distribution.PackageDescription.Parse
|
||||
import Distribution.Verbosity
|
||||
import Distribution.Package
|
||||
import Distribution.PackageDescription
|
||||
import Text.PrettyPrint.HughesPJ
|
||||
import Distribution.Text
|
||||
|
||||
import Text.Pandoc -- works with 1.6
|
||||
|
||||
releaseDate = "25 October 09"
|
||||
|
||||
trim :: String -> String
|
||||
trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace
|
||||
|
||||
@@ -35,13 +53,47 @@ allBindings :: String -> [(String, String)]
|
||||
allBindings xs = map (binding . map trim) (xs =~ "(.*)--(.*)%!(.*)")
|
||||
|
||||
-- FIXME: What escaping should we be doing on these strings?
|
||||
troff :: (String, String) -> String
|
||||
troff (key, desc) = ".IP\n \\fB" ++ key ++ "\\fR\n" ++ desc ++ "\n"
|
||||
markdownDefn :: (String, String) -> String
|
||||
markdownDefn (key, desc) = key ++ "\n: " ++ desc
|
||||
|
||||
replace :: Eq a => a -> a -> [a] -> [a]
|
||||
replace x y = map (\a -> if a == x then y else a)
|
||||
|
||||
-- rawSystem "pandoc" ["--read=markdown","--write=man","man/xmonad.1.markdown"]
|
||||
|
||||
main = do
|
||||
troffBindings <- (concatMap troff . allBindings) `liftM` readFile "./XMonad/Config.hs"
|
||||
let sed = unlines . replace "___KEYBINDINGS___" troffBindings . lines
|
||||
readFile "./man/xmonad.1.in" >>= return . sed >>= writeFile "./man/xmonad.1"
|
||||
releaseName <- (show . disp . package . packageDescription)
|
||||
`liftM`readPackageDescription normal "xmonad.cabal"
|
||||
keybindings <- (intercalate "\n\n" . map markdownDefn . allBindings)
|
||||
`liftM` readFile "./XMonad/Config.hs"
|
||||
|
||||
let manHeader = unwords [".TH xmonad 1","\""++releaseDate++"\"",releaseName,"\"xmonad manual\""]
|
||||
writeOpts = defaultWriterOptions -- { writerLiterateHaskell = True }
|
||||
|
||||
parsed <- readMarkdown defaultParserState { stateLiterateHaskell = True }
|
||||
. unlines
|
||||
. replace "___KEYBINDINGS___" keybindings
|
||||
. lines
|
||||
<$> readFile "./man/xmonad.1.markdown"
|
||||
|
||||
Right template <- getDefaultTemplate Nothing "man"
|
||||
writeFile "./man/xmonad.1"
|
||||
. (manHeader ++)
|
||||
. writeMan writeOpts{ writerStandalone = True, writerTemplate = template }
|
||||
$ parsed
|
||||
putStrLn "Documentation created: man/xmonad.1"
|
||||
|
||||
Right template <- getDefaultTemplate Nothing "html"
|
||||
writeFile "./man/xmonad.1.html"
|
||||
. writeHtmlString writeOpts
|
||||
{ writerVariables =
|
||||
[("include-before"
|
||||
,"<h1>"++releaseName++"</h1>"++
|
||||
"<p>Section: xmonad manual (1)<br/>"++
|
||||
"Updated: "++releaseDate++"</p>"++
|
||||
"<hr/>")]
|
||||
, writerStandalone = True
|
||||
, writerTemplate = template
|
||||
, writerTableOfContents = True }
|
||||
$ parsed
|
||||
putStrLn "Documentation created: man/xmonad.1.html"
|
||||
|
27
xmonad.cabal
27
xmonad.cabal
@@ -1,5 +1,5 @@
|
||||
name: xmonad
|
||||
version: 0.8
|
||||
version: 0.10
|
||||
homepage: http://xmonad.org
|
||||
synopsis: A tiling window manager
|
||||
description:
|
||||
@@ -18,11 +18,13 @@ license-file: LICENSE
|
||||
author: Spencer Janssen
|
||||
maintainer: xmonad@haskell.org
|
||||
extra-source-files: README TODO CONFIG STYLE tests/loc.hs tests/Properties.hs
|
||||
man/xmonad.1.in man/xmonad.1 man/xmonad.html man/xmonad.hs
|
||||
man/xmonad.1.markdown man/xmonad.1 man/xmonad.1.html
|
||||
util/GenerateManpage.hs
|
||||
cabal-version: >= 1.2
|
||||
build-type: Simple
|
||||
|
||||
data-files: man/xmonad.hs
|
||||
|
||||
flag small_base
|
||||
description: Choose the new smaller, split-up base package.
|
||||
|
||||
@@ -41,12 +43,18 @@ library
|
||||
XMonad.StackSet
|
||||
|
||||
if flag(small_base)
|
||||
build-depends: base >= 3, containers, directory, process
|
||||
build-depends: base < 5 && >=3, containers, directory, process, filepath, extensible-exceptions
|
||||
else
|
||||
build-depends: base < 3
|
||||
build-depends: X11>=1.4.1, mtl, unix
|
||||
build-depends: X11>=1.5.0.0 && < 1.6, mtl, unix,
|
||||
utf8-string >= 0.3 && < 0.4
|
||||
|
||||
if true
|
||||
ghc-options: -funbox-strict-fields -Wall
|
||||
|
||||
if impl(ghc >= 6.12.1)
|
||||
ghc-options: -fno-warn-unused-do-bind
|
||||
|
||||
ghc-options: -funbox-strict-fields -Wall
|
||||
ghc-prof-options: -prof -auto-all
|
||||
extensions: CPP
|
||||
|
||||
@@ -64,7 +72,12 @@ executable xmonad
|
||||
XMonad.Operations
|
||||
XMonad.StackSet
|
||||
|
||||
ghc-options: -funbox-strict-fields -Wall
|
||||
if true
|
||||
ghc-options: -funbox-strict-fields -Wall
|
||||
|
||||
if impl(ghc >= 6.12.1)
|
||||
ghc-options: -fno-warn-unused-do-bind
|
||||
|
||||
ghc-prof-options: -prof -auto-all
|
||||
extensions: CPP
|
||||
|
||||
@@ -74,4 +87,4 @@ executable xmonad
|
||||
build-depends: QuickCheck < 2
|
||||
ghc-options: -Werror
|
||||
if flag(testing) && flag(small_base)
|
||||
build-depends: random
|
||||
build-depends: filepath, process, directory, mtl, unix, X11, base, containers, random, extensible-exceptions
|
||||
|
Reference in New Issue
Block a user