mirror of
https://github.com/xmonad/xmonad-contrib.git
synced 2025-07-26 09:41:52 -07:00
Compare commits
122 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b456b87e0e | ||
|
da2a08ec7e | ||
|
c51f64476a | ||
|
d1b2eb4bbb | ||
|
03513bb9b4 | ||
|
0272b8e468 | ||
|
b1984eef30 | ||
|
deacde29a0 | ||
|
22ea09d747 | ||
|
2786791ff5 | ||
|
021298cb34 | ||
|
f885e942e9 | ||
|
b5d77062b9 | ||
|
bcec549103 | ||
|
d889c531d0 | ||
|
44b9610906 | ||
|
f3617e75c5 | ||
|
56850074df | ||
|
3ac1205411 | ||
|
ed240c6972 | ||
|
d44ca42551 | ||
|
fc581c9e4a | ||
|
c5af703cb8 | ||
|
42692986e6 | ||
|
88e524f480 | ||
|
4c7ebafcfe | ||
|
fe253a602c | ||
|
52379a3736 | ||
|
a11a42b2a5 | ||
|
afa80ad2a2 | ||
|
ef310e1792 | ||
|
8afb72a48e | ||
|
a521838fac | ||
|
8698e58f12 | ||
|
99f04b7504 | ||
|
f365c082ba | ||
|
601c3c06db | ||
|
9d0f34852c | ||
|
293b8152aa | ||
|
1d78c1fd60 | ||
|
96786e0abd | ||
|
78a9495c03 | ||
|
0462f00f42 | ||
|
3b4473f121 | ||
|
6962d8f216 | ||
|
0a935aff63 | ||
|
642cbdcad6 | ||
|
1a6c11e8e6 | ||
|
8cc3556448 | ||
|
d043dfbaf9 | ||
|
565dd89ebe | ||
|
20119ffa7a | ||
|
7337ce50c2 | ||
|
cbc978936e | ||
|
80618c53c1 | ||
|
4908cc5efb | ||
|
a5ffb70fc6 | ||
|
dcde384f1a | ||
|
e3503bc3f2 | ||
|
1415787fa3 | ||
|
de64bf60b4 | ||
|
7749dc92d5 | ||
|
9d2a5d4acc | ||
|
b6164c6ddc | ||
|
c40d8c2f3d | ||
|
b6c951a30c | ||
|
2520104b1e | ||
|
b849ccb29e | ||
|
1e30ffe2c6 | ||
|
f0259987b1 | ||
|
c27eb22b39 | ||
|
a0ac6331df | ||
|
806c1f4b5f | ||
|
a4cbf496e7 | ||
|
6d17e66bb3 | ||
|
a2cf9d4d97 | ||
|
9d409b6b3d | ||
|
5f361b02af | ||
|
5514c2ddca | ||
|
2480ba1f02 | ||
|
2102a565fd | ||
|
2051b80b25 | ||
|
e8edf860f7 | ||
|
95c8fa2d1d | ||
|
a667fa5720 | ||
|
31fd3135cf | ||
|
300d9cf2b7 | ||
|
b663075990 | ||
|
b0c3dcc192 | ||
|
07e9192f6f | ||
|
205032840b | ||
|
ae57d452be | ||
|
bf51c0f64c | ||
|
8971328f06 | ||
|
38a21daefe | ||
|
9476610ee0 | ||
|
2d5b9475b9 | ||
|
005f4ef7fb | ||
|
1c1205daed | ||
|
505cbb2430 | ||
|
2477985387 | ||
|
8d670902e5 | ||
|
c75b058c5b | ||
|
f3b6b2707a | ||
|
bd2b5379ab | ||
|
4ae4a7ec07 | ||
|
9dd5fff540 | ||
|
026fdf71be | ||
|
10be813bd7 | ||
|
63a0177187 | ||
|
2d1ccbe643 | ||
|
03caedc589 | ||
|
e677bb3cc1 | ||
|
644b85ab36 | ||
|
587078d456 | ||
|
25033caf6e | ||
|
a908ff760b | ||
|
07a5355edc | ||
|
289b994646 | ||
|
297e626fc7 | ||
|
c3d5c09e84 | ||
|
27efc7a626 |
@@ -77,7 +77,7 @@ copyToAll s = foldr copy s $ map tag (workspaces s)
|
||||
copyWindow :: (Eq a, Eq i, Eq s) => a -> i -> StackSet i l a s sd -> StackSet i l a s sd
|
||||
copyWindow w n = copy'
|
||||
where copy' s = if n `tagMember` s
|
||||
then view (tag (workspace (current s))) $ insertUp' w $ view n s
|
||||
then view (currentTag s) $ insertUp' w $ view n s
|
||||
else s
|
||||
insertUp' a s = modify (Just $ Stack a [] [])
|
||||
(\(Stack t l r) -> if a `elem` t:l++r
|
||||
@@ -107,7 +107,7 @@ kill1 = do ss <- gets windowset
|
||||
killAllOtherCopies :: X ()
|
||||
killAllOtherCopies = do ss <- gets windowset
|
||||
whenJust (peek ss) $ \w -> windows $
|
||||
view (tag (workspace (current ss))) .
|
||||
view (currentTag ss) .
|
||||
delFromAllButCurrent w
|
||||
where
|
||||
delFromAllButCurrent w ss = foldr ($) ss $
|
||||
|
@@ -172,6 +172,7 @@ data WSDirection = Next | Prev
|
||||
-- | What type of workspaces should be included in the cycle?
|
||||
data WSType = EmptyWS -- ^ cycle through empty workspaces
|
||||
| NonEmptyWS -- ^ cycle through non-empty workspaces
|
||||
| HiddenWS -- ^ cycle through non-visible workspaces
|
||||
| HiddenNonEmptyWS -- ^ cycle through non-empty non-visible workspaces
|
||||
| AnyWS -- ^ cycle through all workspaces
|
||||
| WSIs (X (WindowSpace -> Bool))
|
||||
@@ -182,8 +183,11 @@ data WSType = EmptyWS -- ^ cycle through empty workspaces
|
||||
wsTypeToPred :: WSType -> X (WindowSpace -> Bool)
|
||||
wsTypeToPred EmptyWS = return (isNothing . stack)
|
||||
wsTypeToPred NonEmptyWS = return (isJust . stack)
|
||||
wsTypeToPred HiddenNonEmptyWS = do hs <- gets (map tag . hidden . windowset)
|
||||
return (\w -> isJust (stack w) && tag w `elem` hs)
|
||||
wsTypeToPred HiddenWS = do hs <- gets (map tag . hidden . windowset)
|
||||
return (\w -> tag w `elem` hs)
|
||||
wsTypeToPred HiddenNonEmptyWS = do ne <- wsTypeToPred NonEmptyWS
|
||||
hi <- wsTypeToPred HiddenWS
|
||||
return (\w -> hi w && ne w)
|
||||
wsTypeToPred AnyWS = return (const True)
|
||||
wsTypeToPred (WSIs p) = p
|
||||
|
||||
@@ -217,7 +221,7 @@ findWorkspace s dir t n = findWorkspaceGen s (wsTypeToPred t) (maybeNegate dir n
|
||||
maybeNegate Prev d = (-d)
|
||||
|
||||
findWorkspaceGen :: X WorkspaceSort -> X (WindowSpace -> Bool) -> Int -> X WorkspaceId
|
||||
findWorkspaceGen _ _ 0 = (tag . workspace . current) `fmap` gets windowset
|
||||
findWorkspaceGen _ _ 0 = gets (currentTag . windowset)
|
||||
findWorkspaceGen sortX wsPredX d = do
|
||||
wsPred <- wsPredX
|
||||
sort <- sortX
|
||||
|
302
XMonad/Actions/GridSelect.hs
Normal file
302
XMonad/Actions/GridSelect.hs
Normal file
@@ -0,0 +1,302 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.GridSelect
|
||||
-- Copyright : Clemens Fruhwirth <clemens@endorphin.org>
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Clemens Fruhwirth <clemens@endorphin.org>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- GridSelect displays a 2D grid of windows to navigate with cursor
|
||||
-- keys and to select with return.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.GridSelect (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
GSConfig(..),
|
||||
defaultGSConfig,
|
||||
gridselect,
|
||||
withSelectedWindow,
|
||||
bringSelected,
|
||||
goToSelected,
|
||||
default_colorizer
|
||||
) where
|
||||
import Data.Maybe
|
||||
import Data.Bits
|
||||
import Control.Monad.State
|
||||
import Control.Arrow
|
||||
import Data.List as L
|
||||
import XMonad
|
||||
import XMonad.Util.Font
|
||||
import XMonad.Prompt (mkUnmanagedWindow)
|
||||
import XMonad.StackSet as W
|
||||
import XMonad.Layout.Decoration
|
||||
import XMonad.Util.NamedWindows
|
||||
import XMonad.Actions.WindowBringer (bringWindow)
|
||||
import Text.Printf
|
||||
|
||||
-- $usage
|
||||
--
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Actions.GridSelect
|
||||
--
|
||||
-- Then add a keybinding, e.g.
|
||||
--
|
||||
-- > , ((modMask x, xK_g), goToSelected defaultGSConfig)
|
||||
--
|
||||
-- Screenshot: <http://clemens.endorphin.org/gridselect.png>
|
||||
|
||||
data GSConfig = GSConfig {
|
||||
gs_cellheight :: Integer,
|
||||
gs_cellwidth :: Integer,
|
||||
gs_cellpadding :: Integer,
|
||||
gs_colorizer :: Window -> Bool -> X (String, String),
|
||||
gs_font :: String
|
||||
}
|
||||
|
||||
type TwoDPosition = (Integer, Integer)
|
||||
|
||||
type TwoDWindowMap = [(TwoDPosition,(String,Window))]
|
||||
|
||||
data TwoDState = TwoDState { td_curpos :: TwoDPosition,
|
||||
td_windowmap :: [(TwoDPosition,(String,Window))],
|
||||
td_gsconfig :: GSConfig,
|
||||
td_font :: XMonadFont,
|
||||
td_paneX :: Integer,
|
||||
td_paneY :: Integer,
|
||||
td_drawingWin :: Window
|
||||
}
|
||||
|
||||
|
||||
type TwoD a = StateT TwoDState X a
|
||||
|
||||
diamondLayer :: (Enum b', Num b') => b' -> [(b', b')]
|
||||
-- FIXME remove nub
|
||||
diamondLayer n = let ul = [ (x,n-x) | x <- [0..n] ]
|
||||
in nub $ ul ++ (map (negate *** id) ul) ++
|
||||
(map (negate *** negate) ul) ++
|
||||
(map (id *** negate) ul)
|
||||
|
||||
diamond :: (Enum a, Num a) => [(a, a)]
|
||||
diamond = concatMap diamondLayer [0..]
|
||||
|
||||
diamondRestrict :: (Enum t, Num t, Ord t) => t -> t -> [(t, t)]
|
||||
diamondRestrict x y = L.filter (\(x',y') -> abs x' <= x && abs y' <= y) .
|
||||
L.takeWhile (\(x',y') -> abs x' + abs y' <= x+y) $ diamond
|
||||
|
||||
tupadd :: (Num t1, Num t) => (t, t1) -> (t, t1) -> (t, t1)
|
||||
tupadd (a,b) (c,d) = (a+c,b+d)
|
||||
|
||||
findInWindowMap :: (Eq a) => a -> [(a, b)] -> Maybe (a, b)
|
||||
findInWindowMap pos = find ((== pos) . fst)
|
||||
|
||||
drawWinBox :: Window -> XMonadFont -> (String, String) -> Integer -> Integer -> String -> Integer -> Integer -> Integer -> X ()
|
||||
drawWinBox win font (fg,bg) ch cw text x y cp =
|
||||
withDisplay $ \dpy -> do
|
||||
gc <- liftIO $ createGC dpy win
|
||||
bordergc <- liftIO $ createGC dpy win
|
||||
liftIO $ do
|
||||
Just fgcolor <- initColor dpy fg
|
||||
Just bgcolor <- initColor dpy bg
|
||||
Just bordercolor <- initColor dpy borderColor
|
||||
setForeground dpy gc fgcolor
|
||||
setBackground dpy gc bgcolor
|
||||
setForeground dpy bordergc bordercolor
|
||||
fillRectangle dpy win gc (fromInteger x) (fromInteger y) (fromInteger cw) (fromInteger ch)
|
||||
drawRectangle dpy win bordergc (fromInteger x) (fromInteger y) (fromInteger cw) (fromInteger ch)
|
||||
stext <- shrinkWhile (shrinkIt shrinkText)
|
||||
(\n -> do size <- liftIO $ textWidthXMF dpy font n
|
||||
return $ size > (fromInteger (cw-(2*cp))))
|
||||
text
|
||||
printStringXMF dpy win font gc bg fg (fromInteger (x+cp)) (fromInteger (y+(div ch 2))) stext
|
||||
liftIO $ freeGC dpy gc
|
||||
liftIO $ freeGC dpy bordergc
|
||||
|
||||
updateAllWindows :: TwoD ()
|
||||
updateAllWindows =
|
||||
do
|
||||
TwoDState { td_windowmap = wins } <- get
|
||||
updateWindows wins
|
||||
|
||||
updateWindows :: TwoDWindowMap -> TwoD ()
|
||||
updateWindows windowmap = do
|
||||
TwoDState { td_curpos = curpos,
|
||||
td_drawingWin = win,
|
||||
td_gsconfig = gsconfig,
|
||||
td_font = font,
|
||||
td_paneX = paneX,
|
||||
td_paneY = paneY} <- get
|
||||
let cellwidth = gs_cellwidth gsconfig
|
||||
cellheight = gs_cellheight gsconfig
|
||||
paneX' = div (paneX-cellwidth) 2
|
||||
paneY' = div (paneY-cellheight) 2
|
||||
updateWindow (pos@(x,y),(text, clientwindow)) = lift $ do
|
||||
colors <- (gs_colorizer gsconfig) clientwindow (pos == curpos)
|
||||
drawWinBox win font
|
||||
colors
|
||||
cellheight
|
||||
cellwidth
|
||||
text
|
||||
(paneX'+x*cellwidth)
|
||||
(paneY'+y*cellheight)
|
||||
(gs_cellpadding gsconfig)
|
||||
mapM updateWindow windowmap
|
||||
return ()
|
||||
|
||||
eventLoop :: TwoD (Maybe Window)
|
||||
eventLoop = do
|
||||
(keysym,string,event) <- lift $ withDisplay $ \d -> liftIO $ allocaXEvent $ \e -> do
|
||||
nextEvent d e
|
||||
ev <- getEvent e
|
||||
(ks,s) <- if ev_event_type ev == keyPress
|
||||
then lookupString $ asKeyEvent e
|
||||
else return (Nothing, "")
|
||||
return (ks,s,ev)
|
||||
handle (fromMaybe xK_VoidSymbol keysym,string) event
|
||||
|
||||
handle :: (KeySym, String)
|
||||
-> Event
|
||||
-> StateT TwoDState X (Maybe Window)
|
||||
handle (ks,_) (KeyEvent {ev_event_type = t})
|
||||
| t == keyPress && ks == xK_Escape = return Nothing
|
||||
| t == keyPress && (ks == xK_Left || ks == xK_h) = diffAndRefresh (-1,0)
|
||||
| t == keyPress && (ks == xK_Right || ks == xK_l) = diffAndRefresh (1,0)
|
||||
| t == keyPress && (ks == xK_Down || ks == xK_j) = diffAndRefresh (0,1)
|
||||
| t == keyPress && (ks == xK_Up || ks == xK_k) = diffAndRefresh (0,-1)
|
||||
| t == keyPress && ks == xK_Return = do
|
||||
(TwoDState { td_curpos = pos, td_windowmap = winmap }) <- get
|
||||
return $ fmap (snd . snd) $ findInWindowMap pos winmap
|
||||
where diffAndRefresh diff = do
|
||||
state <- get
|
||||
let windowmap = td_windowmap state
|
||||
oldPos = td_curpos state
|
||||
newPos = oldPos `tupadd` diff
|
||||
newSelectedWin = findInWindowMap newPos windowmap
|
||||
when (isJust newSelectedWin) $ do
|
||||
put state { td_curpos = newPos }
|
||||
updateWindows (catMaybes [(findInWindowMap oldPos windowmap), newSelectedWin])
|
||||
eventLoop
|
||||
|
||||
handle _ (ExposeEvent { }) = do
|
||||
updateAllWindows
|
||||
eventLoop
|
||||
|
||||
handle _ _ = do
|
||||
eventLoop
|
||||
|
||||
-- FIXME probably move that into Utils?
|
||||
-- Conversion scheme as in http://en.wikipedia.org/wiki/HSV_color_space
|
||||
hsv2rgb :: Fractional a => (Integer,a,a) -> (a,a,a)
|
||||
hsv2rgb (h,s,v) =
|
||||
let hi = (div h 60) `mod` 6 :: Integer
|
||||
f = (((fromInteger h)/60) - (fromInteger hi)) :: Fractional a => a
|
||||
q = v * (1-f)
|
||||
p = v * (1-s)
|
||||
t = v * (1-(1-f)*s)
|
||||
in case hi of
|
||||
0 -> (v,t,p)
|
||||
1 -> (q,v,p)
|
||||
2 -> (p,v,t)
|
||||
3 -> (p,q,v)
|
||||
4 -> (t,p,v)
|
||||
5 -> (v,p,q)
|
||||
_ -> error "The world is ending. x mod a >= a."
|
||||
|
||||
default_colorizer :: Window -> Bool -> X (String, String)
|
||||
default_colorizer w active = do
|
||||
classname <- runQuery className w
|
||||
let seed x = toInteger (sum $ map ((*x).fromEnum) classname) :: Integer
|
||||
(r,g,b) = hsv2rgb ((seed 83) `mod` 360,
|
||||
(fromInteger ((seed 191) `mod` 1000))/2500+0.4,
|
||||
(fromInteger ((seed 121) `mod` 1000))/2500+0.4)
|
||||
if active
|
||||
then return ("#faff69", "black")
|
||||
else return ("#" ++ concat (map (twodigitHex.(round :: Double -> Integer).(*256)) [r, g, b] ), "white")
|
||||
where
|
||||
twodigitHex :: Integer -> String
|
||||
twodigitHex a = printf "%02x" a
|
||||
|
||||
-- | Brings up a 2D grid of windows in the center of the screen, and one can
|
||||
-- select a window with cursors keys. The selected window is returned.
|
||||
gridselect :: GSConfig -> X (Maybe Window)
|
||||
gridselect gsconfig =
|
||||
withDisplay $ \dpy -> do
|
||||
rootw <- liftIO $ rootWindow dpy (defaultScreen dpy)
|
||||
s <- gets $ screenRect . W.screenDetail . W.current . windowset
|
||||
windowList <- windowMap
|
||||
win <- liftIO $ mkUnmanagedWindow dpy (defaultScreenOfDisplay dpy) rootw
|
||||
(rect_x s) (rect_y s) (rect_width s) (rect_height s)
|
||||
liftIO $ mapWindow dpy win
|
||||
liftIO $ selectInput dpy win (exposureMask .|. keyPressMask)
|
||||
status <- io $ grabKeyboard dpy win True grabModeAsync grabModeAsync currentTime
|
||||
font <- initXMF (gs_font gsconfig)
|
||||
let screenWidth = toInteger $ rect_width s;
|
||||
screenHeight = toInteger $ rect_height s;
|
||||
selectedWindow <- if (status == grabSuccess) then
|
||||
do
|
||||
let restriction :: Integer -> (GSConfig -> Integer) -> Double
|
||||
restriction ss cs = ((fromInteger ss)/(fromInteger $ cs gsconfig)-1)/2
|
||||
restrictX = floor $ restriction screenWidth gs_cellwidth
|
||||
restrictY = floor $ restriction screenHeight gs_cellheight
|
||||
winmap = zipWith (,) (diamondRestrict restrictX restrictY) windowList
|
||||
selectedWindow <- evalStateT (do updateAllWindows; eventLoop)
|
||||
(TwoDState (0,0)
|
||||
winmap
|
||||
gsconfig
|
||||
font
|
||||
screenWidth
|
||||
screenHeight
|
||||
win)
|
||||
return selectedWindow
|
||||
else
|
||||
return Nothing
|
||||
liftIO $ do
|
||||
unmapWindow dpy win
|
||||
destroyWindow dpy win
|
||||
sync dpy False
|
||||
releaseXMF font
|
||||
return selectedWindow
|
||||
|
||||
-- | Brings up a 2D grid of windows in the center of the screen, and one can
|
||||
-- select a window with cursors keys. The selected window is then passed to
|
||||
-- a callback function.
|
||||
withSelectedWindow :: (Window -> X ()) -> GSConfig -> X ()
|
||||
withSelectedWindow callback conf = do
|
||||
mbWindow <- gridselect conf
|
||||
case mbWindow of
|
||||
Just w -> callback w
|
||||
Nothing -> return ()
|
||||
|
||||
|
||||
windowMap :: X [(String,Window)]
|
||||
windowMap = do
|
||||
ws <- gets windowset
|
||||
wins <- mapM keyValuePair (W.allWindows ws)
|
||||
return wins
|
||||
where keyValuePair w = flip (,) w `fmap` decorateName' w
|
||||
|
||||
decorateName' :: Window -> X String
|
||||
decorateName' w = do
|
||||
fmap show $ getName w
|
||||
|
||||
defaultGSConfig :: GSConfig
|
||||
defaultGSConfig = GSConfig 50 130 10 default_colorizer "xft:Sans-8"
|
||||
|
||||
borderColor :: String
|
||||
borderColor = "white"
|
||||
|
||||
-- | Brings selected window to the current workspace.
|
||||
bringSelected :: GSConfig -> X ()
|
||||
bringSelected = withSelectedWindow $ \w -> do
|
||||
windows (bringWindow w)
|
||||
XMonad.focus w
|
||||
windows W.shiftMaster
|
||||
|
||||
-- | Switches to selected window's workspace and focuses that window.
|
||||
goToSelected :: GSConfig -> X ()
|
||||
goToSelected = withSelectedWindow $ windows . W.focusWindow
|
||||
|
@@ -65,7 +65,8 @@ instance Show (MouseResize a) where show _ = ""
|
||||
instance Read (MouseResize a) where readsPrec _ s = [(MR [], s)]
|
||||
|
||||
instance LayoutModifier MouseResize Window where
|
||||
redoLayout (MR st) _ s wrs
|
||||
redoLayout _ _ Nothing wrs = return (wrs, Nothing)
|
||||
redoLayout (MR st) _ (Just s) wrs
|
||||
| [] <- st = initState >>= \nst -> return (wrs, Just $ MR nst)
|
||||
| otherwise = processState >>= \nst -> return (wrs, Just $ MR nst)
|
||||
where
|
||||
|
@@ -36,7 +36,7 @@ import Data.List (find)
|
||||
|
||||
-- | Uses supplied function to decide which action to run depending on current workspace name.
|
||||
chooseAction :: (String->X()) -> X()
|
||||
chooseAction f = withWindowSet (f . S.tag . S.workspace . S.current)
|
||||
chooseAction f = withWindowSet (f . S.currentTag)
|
||||
|
||||
-- | If current workspace is listed, run appropriate action (only the first match counts!)
|
||||
-- If it isn't listed, then run default action (marked with empty string, \"\"), or do nothing if default isn't supplied.
|
||||
|
@@ -29,6 +29,9 @@ module XMonad.Actions.Plane
|
||||
, Limits (..)
|
||||
, Lines (..)
|
||||
|
||||
-- * Key bindings
|
||||
, planeKeys
|
||||
|
||||
-- * Navigating through workspaces
|
||||
, planeShift
|
||||
, planeMove
|
||||
@@ -36,7 +39,8 @@ module XMonad.Actions.Plane
|
||||
where
|
||||
|
||||
import Control.Monad
|
||||
import Data.List hiding (union)
|
||||
import Data.List
|
||||
import Data.Map hiding (split)
|
||||
import Data.Maybe
|
||||
|
||||
import XMonad
|
||||
@@ -52,12 +56,7 @@ import XMonad.Util.Run
|
||||
-- >
|
||||
-- > myKeys conf = union (keys defaultConfig conf) $ myNewKeys conf
|
||||
-- >
|
||||
-- > myNewkeys (XConfig {modMask = m}) =
|
||||
-- > fromList
|
||||
-- > [ ((keyMask .|. m, keySym), function (Lines 3) Finite direction)
|
||||
-- > | (keySym, direction) <- zip [xK_Left .. xK_Down] $ enumFrom ToLeft
|
||||
-- > , (keyMask, function) <- [(0, planeMove), (shiftMask, planeShift)]
|
||||
-- > ]
|
||||
-- > myNewkeys (XConfig {modMask = modm}) = planeKeys modm (Lines 3) Finite
|
||||
--
|
||||
-- For detailed instructions on editing your key bindings, see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
@@ -65,13 +64,12 @@ import XMonad.Util.Run
|
||||
-- | Direction to go in the plane.
|
||||
data Direction = ToLeft | ToUp | ToRight | ToDown deriving Enum
|
||||
|
||||
-- | Defines whether it's a finite or a circular organization of workspaces.
|
||||
-- | Defines the behaviour when you're trying to move out of the limits.
|
||||
data Limits
|
||||
= Finite -- ^ When you're at a edge of the plane, there's no way to move
|
||||
-- to the next region.
|
||||
| Circular -- ^ If you try to move, you'll get to the other edge, on the
|
||||
-- other side.
|
||||
| Linear -- ^ The plan comes as a row.
|
||||
= Finite -- ^ Ignore the function call, and keep in the same workspace.
|
||||
| Circular -- ^ Get on the other side, like in the Snake game.
|
||||
| Linear -- ^ The plan comes as a row, so it goes to the next or prev if
|
||||
-- the workspaces were numbered.
|
||||
deriving Eq
|
||||
|
||||
-- | The number of lines in which the workspaces will be arranged. It's
|
||||
@@ -82,26 +80,22 @@ data Lines
|
||||
= GConf -- ^ Use @gconftool-2@ to find out the number of lines.
|
||||
| Lines Int -- ^ Specify the number of lines explicity.
|
||||
|
||||
-- $navigating
|
||||
--
|
||||
-- There're two parameters that must be provided to navigate, and it's a good
|
||||
-- idea to use them with the same values in each keybinding.
|
||||
--
|
||||
-- The first is the number of lines in which the workspaces are going to be
|
||||
-- organized. It's possible to use a number of lines that is not a divisor
|
||||
-- of the number of workspaces, but the results are better when using a
|
||||
-- divisor. If it's not a divisor, the last line will have the remaining
|
||||
-- workspaces.
|
||||
--
|
||||
-- The other one is 'Limits'.
|
||||
-- | This is the way most people would like to use this module. It ataches the
|
||||
-- 'KeyMask' passed as a parameter with 'xK_Left', 'xK_Up', 'xK_Right' and
|
||||
-- 'xK_Down', associating it with 'planeMove' to the corresponding 'Direction'.
|
||||
-- It also associates these bindings with 'shiftMask' to 'planeShift'.
|
||||
planeKeys :: KeyMask -> Lines -> Limits -> Map (KeyMask, KeySym) (X ())
|
||||
planeKeys modm ln limits =
|
||||
fromList $
|
||||
[ ((keyMask, keySym), function ln limits direction)
|
||||
| (keySym, direction) <- zip [xK_Left .. xK_Down] $ enumFrom ToLeft
|
||||
, (keyMask, function) <- [(modm, planeMove), (shiftMask .|. modm, planeShift)]
|
||||
]
|
||||
|
||||
-- | Shift a window to the next workspace in 'Direction'. Note that this will
|
||||
-- also move to the next workspace.
|
||||
planeShift
|
||||
:: Lines
|
||||
-> Limits
|
||||
-> Direction
|
||||
-> X ()
|
||||
-- also move to the next workspace. It's a good idea to use the same 'Lines'
|
||||
-- and 'Limits' for all the bindings.
|
||||
planeShift :: Lines -> Limits -> Direction -> X ()
|
||||
planeShift = plane shift'
|
||||
|
||||
shift' ::
|
||||
|
@@ -22,10 +22,16 @@ module XMonad.Actions.Search ( -- * Usage
|
||||
|
||||
amazon,
|
||||
codesearch,
|
||||
deb,
|
||||
debbts,
|
||||
debpts,
|
||||
dictionary,
|
||||
google,
|
||||
hackage,
|
||||
hoogle,
|
||||
images,
|
||||
imdb,
|
||||
isohunt,
|
||||
maps,
|
||||
mathworld,
|
||||
scholar,
|
||||
@@ -75,14 +81,26 @@ import XMonad.Util.XSelection (getSelection)
|
||||
|
||||
* 'codesearch' -- Google Labs Code Search search.
|
||||
|
||||
* 'deb' -- Debian package search.
|
||||
|
||||
* 'debbts' -- Debian Bug Tracking System.
|
||||
|
||||
* 'debpts -- Debian Package Tracking System.
|
||||
|
||||
* 'dictionary' -- dictionary.reference.com search.
|
||||
|
||||
* 'google' -- basic Google search.
|
||||
|
||||
* 'hoogle' -- Hoogle, the Haskell libraries search engine.
|
||||
* 'hackage' -- Hackage, the Haskell package database.
|
||||
|
||||
* 'hoogle' -- Hoogle, the Haskell libraries API search engine.
|
||||
|
||||
* 'images' -- Google images.
|
||||
|
||||
* 'imdb' -- the Internet Movie Database.
|
||||
|
||||
* 'isohunt' -- isoHunt search.
|
||||
|
||||
* 'maps' -- Google maps.
|
||||
|
||||
* 'mathworld' -- Wolfram MathWorld search.
|
||||
@@ -125,6 +143,22 @@ Then add the following to your key bindings:
|
||||
> , ((0, xK_w), method S.wikipedia)
|
||||
> ]
|
||||
|
||||
Or in combination with XMonad.Util.EZConfig:
|
||||
|
||||
> ...
|
||||
> ] -- end of regular keybindings
|
||||
> -- Search commands
|
||||
> ++ [("M-s " ++ k, S.promptSearch P.defaultXPConfig f) | (k,f) <- searchList ]
|
||||
> ++ [("M-S-s " ++ k, S.selectSearch f) | (k,f) <- searchList ]
|
||||
>
|
||||
> ...
|
||||
>
|
||||
> searchList :: [([Char], S.SearchEngine)]
|
||||
> searchList = [ ("g", S.google)
|
||||
> , ("h", S.hoohle)
|
||||
> , ("w", S.wikipedia)
|
||||
> ]
|
||||
|
||||
Make sure to set firefox to open new pages in a new window instead of
|
||||
in a new tab: @Firefox -> Edit -> Preferences -> Tabs -> New pages
|
||||
should be opened in...@
|
||||
@@ -154,14 +188,14 @@ escape = escapeURIString (\c -> isAlpha c || isDigit c || isMark c)
|
||||
(Char -> Bool) -- a predicate which returns 'False' if should escape
|
||||
-> String -- the string to process
|
||||
-> String -- the resulting URI string
|
||||
escapeURIString p s = concatMap (escapeURIChar p) s
|
||||
escapeURIString = concatMap . escapeURIChar
|
||||
escapeURIChar :: (Char->Bool) -> Char -> String
|
||||
escapeURIChar p c
|
||||
| p c = [c]
|
||||
| otherwise = '%' : myShowHex (ord c) ""
|
||||
where
|
||||
myShowHex :: Int -> ShowS
|
||||
myShowHex n r = case showIntAtBase 16 (toChrHex) n r of
|
||||
myShowHex n r = case showIntAtBase 16 toChrHex n r of
|
||||
[] -> "00"
|
||||
[ch] -> ['0',ch]
|
||||
cs -> cs
|
||||
@@ -178,7 +212,7 @@ data SearchEngine = SearchEngine Name Site
|
||||
-- | Given a browser, a search engine, and a search term, perform the
|
||||
-- requested search in the browser.
|
||||
search :: Browser -> Site -> Query -> X ()
|
||||
search browser site query = safeSpawn browser (site ++ (escape query))
|
||||
search browser site query = safeSpawn browser $ site ++ escape query
|
||||
|
||||
{- | Given a base URL, create the 'SearchEngine' that escapes the query and
|
||||
appends it to the base. You can easily define a new engine locally using
|
||||
@@ -193,17 +227,24 @@ search browser site query = safeSpawn browser (site ++ (escape query))
|
||||
Generally, examining the resultant URL of a search will allow you to reverse-engineer
|
||||
it if you can't find the necessary URL already described in other projects such as Surfraw. -}
|
||||
searchEngine :: Name -> Site -> SearchEngine
|
||||
searchEngine name site = SearchEngine name site
|
||||
searchEngine = SearchEngine
|
||||
|
||||
-- The engines.
|
||||
amazon, codesearch, dictionary, google, hoogle, imdb, maps, mathworld,
|
||||
scholar, thesaurus, wayback, wikipedia, youtube :: SearchEngine
|
||||
amazon, codesearch, deb, debbts, debpts, dictionary, google, hackage, hoogle, images,
|
||||
imdb, isohunt, maps, mathworld, scholar, thesaurus, wayback, wikipedia,
|
||||
youtube :: SearchEngine
|
||||
amazon = searchEngine "amazon" "http://www.amazon.com/exec/obidos/external-search?index=all&keyword="
|
||||
codesearch = searchEngine "codesearch" "http://www.google.com/codesearch?q="
|
||||
deb = searchEngine "deb" "http://packages.debian.org/"
|
||||
debbts = searchEngine "debbts" "http://bugs.debian.org/"
|
||||
debpts = searchEngine "debpts" "http://packages.qa.debian.org/"
|
||||
dictionary = searchEngine "dictionary" "http://dictionary.reference.com/browse/"
|
||||
google = searchEngine "google" "http://www.google.com/search?num=100&q="
|
||||
hackage = searchEngine "hackage" "http://hackage.haskell.org/cgi-bin/hackage-scripts/package/"
|
||||
hoogle = searchEngine "hoogle" "http://www.haskell.org/hoogle/?q="
|
||||
images = searchEngine "images" "http://images.google.fr/images?q="
|
||||
imdb = searchEngine "imdb" "http://www.imdb.com/Find?select=all&for="
|
||||
isohunt = searchEngine "isohunt" "http://isohunt.com/torrents/?ihq="
|
||||
maps = searchEngine "maps" "http://maps.google.com/maps?q="
|
||||
mathworld = searchEngine "mathworld" "http://mathworld.wolfram.com/search/?query="
|
||||
scholar = searchEngine "scholar" "http://scholar.google.com/scholar?q="
|
||||
@@ -219,7 +260,7 @@ wayback = searchEngine "wayback" "http://web.archive.org/"
|
||||
Prompt's result, passes it to a given searchEngine and opens it in a given
|
||||
browser. -}
|
||||
promptSearchBrowser :: XPConfig -> Browser -> SearchEngine -> X ()
|
||||
promptSearchBrowser config browser (SearchEngine name site) = mkXPrompt (Search name) config (historyCompletion) $ search browser site
|
||||
promptSearchBrowser config browser (SearchEngine name site) = mkXPrompt (Search name) config historyCompletion $ search browser site
|
||||
|
||||
{- | Like 'search', but in this case, the string is not specified but grabbed
|
||||
from the user's response to a prompt. Example:
|
||||
|
77
XMonad/Actions/SpawnOn.hs
Normal file
77
XMonad/Actions/SpawnOn.hs
Normal file
@@ -0,0 +1,77 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Actions.SpawnOn
|
||||
-- Copyright : (c) Spencer Janssen
|
||||
-- License : BSD
|
||||
--
|
||||
-- Maintainer : Spencer Janssen <spencerjanssen@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- This module provides helper functions to be used in @manageHook@. Here's
|
||||
-- how you might use this:
|
||||
--
|
||||
-- > import XMonad.Hooks.ManageHelpers
|
||||
-- > main = do
|
||||
-- > sp <- mkSpawner
|
||||
-- > xmonad defaultConfig {
|
||||
-- > ...
|
||||
-- > manageHook = spawnHook sp <+> manageHook defaultConfig
|
||||
-- > ...
|
||||
-- > }
|
||||
|
||||
module XMonad.Actions.SpawnOn (
|
||||
Spawner,
|
||||
mkSpawner,
|
||||
manageSpawn,
|
||||
spawnHere,
|
||||
spawnOn,
|
||||
shellPromptHere,
|
||||
shellPromptOn
|
||||
) where
|
||||
|
||||
import Data.IORef
|
||||
import System.Posix.Types (ProcessID)
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import XMonad.Hooks.ManageHelpers
|
||||
import XMonad.Prompt
|
||||
import XMonad.Prompt.Shell
|
||||
|
||||
newtype Spawner = Spawner {pidsRef :: IORef [(ProcessID, WorkspaceId)]}
|
||||
|
||||
maxPids :: Int
|
||||
maxPids = 5
|
||||
|
||||
mkSpawner :: (Functor m, MonadIO m) => m Spawner
|
||||
mkSpawner = io . fmap Spawner $ newIORef []
|
||||
|
||||
manageSpawn :: Spawner -> ManageHook
|
||||
manageSpawn sp = do
|
||||
pids <- io . readIORef $ pidsRef sp
|
||||
mp <- pid
|
||||
case flip lookup pids =<< mp of
|
||||
Just w -> doF (W.shift w)
|
||||
Nothing -> doF id
|
||||
|
||||
mkPrompt :: (String -> X ()) -> XPConfig -> X ()
|
||||
mkPrompt cb c = do
|
||||
cmds <- io $ getCommands
|
||||
mkXPrompt Shell c (getShellCompl cmds) cb
|
||||
|
||||
shellPromptHere :: Spawner -> XPConfig -> X ()
|
||||
shellPromptHere sp = mkPrompt (spawnHere sp)
|
||||
|
||||
shellPromptOn :: Spawner -> WorkspaceId -> XPConfig -> X ()
|
||||
shellPromptOn sp ws = mkPrompt (spawnOn sp ws)
|
||||
|
||||
spawnHere :: Spawner -> String -> X ()
|
||||
spawnHere sp cmd = withWindowSet $ \ws -> spawnOn sp (currTag ws) cmd
|
||||
where currTag = W.tag . W.workspace . W.current
|
||||
|
||||
spawnOn :: Spawner -> WorkspaceId -> String -> X ()
|
||||
spawnOn sp ws cmd = do
|
||||
p <- spawnPID cmd
|
||||
io $ modifyIORef (pidsRef sp) (take maxPids . ((p, ws) :))
|
@@ -48,7 +48,7 @@ import XMonad.Util.WorkspaceCompare
|
||||
-- | Swaps the currently focused workspace with the given workspace tag, via
|
||||
-- @swapWorkspaces@.
|
||||
swapWithCurrent :: Eq i => i -> StackSet i l a s sd -> StackSet i l a s sd
|
||||
swapWithCurrent t s = swapWorkspaces t (tag $ workspace $ current s) s
|
||||
swapWithCurrent t s = swapWorkspaces t (currentTag s) s
|
||||
|
||||
-- | Say @swapTo Next@ or @swapTo Prev@ to move your current workspace.
|
||||
-- This is an @X ()@ so can be hooked up to your keybindings directly.
|
||||
|
@@ -120,7 +120,7 @@ wsToList ws = crs ++ cls
|
||||
wsToListGlobal :: (Ord i) => StackSet i l a s sd -> [a]
|
||||
wsToListGlobal ws = concat ([crs] ++ rws ++ lws ++ [cls])
|
||||
where
|
||||
curtag = tag . workspace . current $ ws
|
||||
curtag = currentTag ws
|
||||
(crs, cls) = (cms down, cms (reverse . up))
|
||||
cms f = maybe [] f (stack . workspace . current $ ws)
|
||||
(lws, rws) = (mws (<), mws (>))
|
||||
@@ -149,8 +149,7 @@ withTagged t f = withTagged' t (mapM_ f)
|
||||
withTaggedGlobal t f = withTaggedGlobal' t (mapM_ f)
|
||||
|
||||
withTagged' :: String -> ([Window] -> X ()) -> X ()
|
||||
withTagged' t m = gets windowset >>=
|
||||
filterM (hasTag t) . integrate' . stack . workspace . current >>= m
|
||||
withTagged' t m = gets windowset >>= filterM (hasTag t) . index >>= m
|
||||
|
||||
withTaggedGlobal' :: String -> ([Window] -> X ()) -> X ()
|
||||
withTaggedGlobal' t m = gets windowset >>=
|
||||
@@ -160,7 +159,7 @@ withFocusedP :: (Window -> WindowSet -> WindowSet) -> X ()
|
||||
withFocusedP f = withFocused $ windows . f
|
||||
|
||||
shiftHere :: (Ord a, Eq s, Eq i) => a -> StackSet i l a s sd -> StackSet i l a s sd
|
||||
shiftHere w s = shiftWin (tag . workspace . current $ s) w s
|
||||
shiftHere w s = shiftWin (currentTag s) w s
|
||||
|
||||
shiftToScreen :: (Ord a, Eq s, Eq i) => s -> a -> StackSet i l a s sd -> StackSet i l a s sd
|
||||
shiftToScreen sid w s = case filter (\m -> sid /= screen m) ((current s):(visible s)) of
|
||||
|
@@ -16,6 +16,7 @@ module XMonad.Actions.Warp (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
banish,
|
||||
banishScreen,
|
||||
Corner(..),
|
||||
warpToScreen,
|
||||
warpToWindow
|
||||
@@ -49,7 +50,7 @@ Note that warping to a particular screen may change the focus.
|
||||
|
||||
data Corner = UpperLeft | UpperRight | LowerLeft | LowerRight
|
||||
|
||||
{- | Move the mouse cursor to a corner of the screen. Useful for
|
||||
{- | Move the mouse cursor to a corner of the focused window. Useful for
|
||||
uncluttering things.
|
||||
|
||||
Internally, this uses numerical parameters. We parametrize on the 'Corner'
|
||||
@@ -64,7 +65,22 @@ banish direction = case direction of
|
||||
LowerRight -> warpToWindow 1 1
|
||||
LowerLeft -> warpToWindow 0 1
|
||||
UpperLeft -> warpToWindow 0 0
|
||||
UpperRight -> warpToWindow 1 0
|
||||
UpperRight -> warpToWindow 1 0
|
||||
|
||||
{- | Same as 'banish' but moves the mouse to the corner of the
|
||||
currently focused screen -}
|
||||
banishScreen :: Corner -> X ()
|
||||
banishScreen direction = case direction of
|
||||
LowerRight -> warpToCurrentScreen 1 1
|
||||
LowerLeft -> warpToCurrentScreen 0 1
|
||||
UpperLeft -> warpToCurrentScreen 0 0
|
||||
UpperRight -> warpToCurrentScreen 1 0
|
||||
where
|
||||
warpToCurrentScreen h v =
|
||||
do ws <- gets windowset
|
||||
warpToScreen (W.screen $ current ws) h v
|
||||
windows (const ws)
|
||||
|
||||
|
||||
fraction :: (Integral a, Integral b) => Rational -> a -> b
|
||||
fraction f x = floor (f * fromIntegral x)
|
||||
|
@@ -15,11 +15,11 @@
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Actions.WindowBringer (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
gotoMenu, bringMenu, windowMap,
|
||||
bringWindow
|
||||
) where
|
||||
-- * Usage
|
||||
-- $usage
|
||||
gotoMenu, gotoMenu', bringMenu, windowMap,
|
||||
bringWindow
|
||||
) where
|
||||
|
||||
import Data.Char (toLower)
|
||||
import qualified Data.Map as M
|
||||
@@ -27,7 +27,7 @@ import qualified Data.Map as M
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad
|
||||
import qualified XMonad as X
|
||||
import XMonad.Util.Dmenu (dmenuMap)
|
||||
import XMonad.Util.Dmenu (menuMap)
|
||||
import XMonad.Util.NamedWindows (getName)
|
||||
|
||||
-- $usage
|
||||
@@ -50,6 +50,9 @@ import XMonad.Util.NamedWindows (getName)
|
||||
gotoMenu :: X ()
|
||||
gotoMenu = actionMenu W.focusWindow
|
||||
|
||||
gotoMenu' :: String -> X ()
|
||||
gotoMenu' menuCmd = actionMenu' menuCmd W.focusWindow
|
||||
|
||||
-- | Pops open a dmenu with window titles. Choose one, and it will be
|
||||
-- dragged, kicking and screaming, into your current workspace.
|
||||
bringMenu :: X ()
|
||||
@@ -57,12 +60,18 @@ bringMenu = actionMenu bringWindow
|
||||
|
||||
-- | Brings the specified window into the current workspace.
|
||||
bringWindow :: Window -> X.WindowSet -> X.WindowSet
|
||||
bringWindow w ws = W.shiftWin (W.tag . W.workspace . W.current $ ws) w ws
|
||||
bringWindow w ws = W.shiftWin (W.currentTag ws) w ws
|
||||
|
||||
-- | Calls dmenuMap to grab the appropriate Window, and hands it off to action
|
||||
-- if found.
|
||||
actionMenu :: (Window -> X.WindowSet -> X.WindowSet) -> X()
|
||||
actionMenu action = windowMap >>= dmenuMap >>= flip X.whenJust (windows . action)
|
||||
actionMenu action = actionMenu' "dmenu" action
|
||||
|
||||
actionMenu' :: String -> (Window -> X.WindowSet -> X.WindowSet) -> X()
|
||||
actionMenu' menuCmd action = windowMap >>= menuMapFunction >>= flip X.whenJust (windows . action)
|
||||
where
|
||||
menuMapFunction :: M.Map String a -> X (Maybe a)
|
||||
menuMapFunction selectionMap = menuMap menuCmd selectionMap
|
||||
|
||||
-- | A map from window names to Windows.
|
||||
windowMap :: X (M.Map String Window)
|
||||
|
@@ -24,17 +24,22 @@ module XMonad.Actions.WindowGo (
|
||||
|
||||
raiseBrowser,
|
||||
raiseEditor,
|
||||
runOrRaiseAndDo,
|
||||
runOrRaiseMaster,
|
||||
raiseAndDo,
|
||||
raiseMaster,
|
||||
module XMonad.ManageHook
|
||||
) where
|
||||
|
||||
import Control.Monad (filterM)
|
||||
import Data.Char (toLower)
|
||||
|
||||
import XMonad (Query(), X(), withWindowSet, spawn, runQuery, liftIO, focus)
|
||||
import XMonad (Query(), X(), withWindowSet, spawn, runQuery, liftIO)
|
||||
import Graphics.X11 (Window)
|
||||
import XMonad.ManageHook
|
||||
import XMonad.Operations (windows)
|
||||
import XMonad.Prompt.Shell (getBrowser, getEditor)
|
||||
import qualified XMonad.StackSet as W (allWindows, peek)
|
||||
|
||||
import qualified XMonad.StackSet as W (allWindows, peek, swapMaster, focusWindow)
|
||||
{- $usage
|
||||
|
||||
Import the module into your @~\/.xmonad\/xmonad.hs@:
|
||||
@@ -57,7 +62,7 @@ For detailed instructions on editing your key bindings, see
|
||||
-- | 'action' is an executable to be run via 'spawn' (of "XMonad.Core") if the Window cannot be found.
|
||||
-- Presumably this executable is the same one that you were looking for.
|
||||
runOrRaise :: String -> Query Bool -> X ()
|
||||
runOrRaise action = raiseMaybe $ spawn action
|
||||
runOrRaise = raiseMaybe . spawn
|
||||
|
||||
-- | See 'raiseMaybe'. If the Window can't be found, quietly give up and do nothing.
|
||||
raise :: Query Bool -> X ()
|
||||
@@ -94,11 +99,11 @@ raiseMaybe f thatUserQuery = withWindowSet $ \s -> do
|
||||
maybeResult <- filterM (runQuery thatUserQuery) (W.allWindows s)
|
||||
case maybeResult of
|
||||
[] -> f
|
||||
(x:_) -> focus x
|
||||
(x:_) -> windows $ W.focusWindow x
|
||||
|
||||
-- | See 'runOrRaise' and 'raiseNextMaybe'. Version that allows cycling through matches.
|
||||
runOrRaiseNext :: String -> Query Bool -> X ()
|
||||
runOrRaiseNext action = raiseNextMaybe $ spawn action
|
||||
runOrRaiseNext = raiseNextMaybe . spawn
|
||||
|
||||
-- | See 'raise' and 'raiseNextMaybe'. Version that allows cycling through matches.
|
||||
raiseNext :: Query Bool -> X ()
|
||||
@@ -116,10 +121,10 @@ raiseNextMaybe f thatUserQuery = withWindowSet $ \s -> do
|
||||
case ws of
|
||||
[] -> f
|
||||
(x:_) -> let go (Just w) | (w `elem` ws) = next w $ cycle ws
|
||||
go _ = focus x
|
||||
go _ = windows $ W.focusWindow x
|
||||
in go $ W.peek s
|
||||
where
|
||||
next w (x:y:_) | x==w = focus y
|
||||
next w (x:y:_) | x==w = windows $ W.focusWindow y
|
||||
next w (_:xs) = next w xs
|
||||
next _ _ = error "raiseNextMaybe: empty list"
|
||||
|
||||
@@ -134,3 +139,34 @@ raiseVar getvar = liftIO getvar >>= \var -> runOrRaise var (fmap (map toLower) c
|
||||
raiseBrowser, raiseEditor :: X ()
|
||||
raiseBrowser = raiseVar getBrowser
|
||||
raiseEditor = raiseVar getEditor
|
||||
|
||||
{- | if the window is found the window is focused and the third argument is called
|
||||
otherwise, the first argument is called
|
||||
See 'raiseMaster' for an example -}
|
||||
raiseAndDo :: X () -> Query Bool -> (Window -> X ())-> X ()
|
||||
raiseAndDo raisef thatUserQuery afterRaise = withWindowSet $ \s -> do
|
||||
maybeResult <- filterM (runQuery thatUserQuery) (W.allWindows s)
|
||||
case maybeResult of
|
||||
[] -> raisef
|
||||
(x:_) -> do windows $ W.focusWindow x
|
||||
afterRaise x
|
||||
|
||||
{- | if the window is found the window is focused and the third argument is called
|
||||
otherwise, raisef is called -}
|
||||
runOrRaiseAndDo :: String -> Query Bool -> (Window -> X ()) -> X ()
|
||||
runOrRaiseAndDo = raiseAndDo . spawn
|
||||
|
||||
{- | if the window is found the window is focused and set to master
|
||||
otherwise, the first argument is called
|
||||
|
||||
raiseMaster (runInTerm \"-title ghci\" \"zsh -c \'ghci\'\") (title =? \"ghci\") -}
|
||||
raiseMaster :: X () -> Query Bool -> X ()
|
||||
raiseMaster raisef thatUserQuery = raiseAndDo raisef thatUserQuery (\_ -> windows W.swapMaster)
|
||||
|
||||
{- | if the window is found the window is focused and set to master
|
||||
otherwise, action is run
|
||||
|
||||
runOrRaiseMaster \"firefox\" (className =? \"Firefox\"))
|
||||
-}
|
||||
runOrRaiseMaster :: String -> Query Bool -> X ()
|
||||
runOrRaiseMaster run query = runOrRaiseAndDo run query (\_ -> windows W.swapMaster)
|
||||
|
@@ -153,7 +153,7 @@ currentPosition posRef = do
|
||||
currentWindow <- gets (W.peek . windowset)
|
||||
currentRect <- maybe (Rectangle 0 0 0 0) snd <$> windowRect (fromMaybe root currentWindow)
|
||||
|
||||
wsid <- gets (W.tag . W.workspace . W.current . windowset)
|
||||
wsid <- gets (W.currentTag . windowset)
|
||||
mp <- M.lookup wsid <$> io (readIORef posRef)
|
||||
|
||||
return $ maybe (middleOf currentRect) (`inside` currentRect) mp
|
||||
@@ -162,7 +162,7 @@ currentPosition posRef = do
|
||||
|
||||
setPosition :: IORef WNState -> Point -> Rectangle -> X ()
|
||||
setPosition posRef oldPos newRect = do
|
||||
wsid <- gets (W.tag . W.workspace . W.current . windowset)
|
||||
wsid <- gets (W.currentTag . windowset)
|
||||
io $ modifyIORef posRef $ M.insert wsid (oldPos `inside` newRect)
|
||||
|
||||
inside :: Point -> Rectangle -> Point
|
||||
|
46
XMonad/Config/Azerty.hs
Normal file
46
XMonad/Config/Azerty.hs
Normal file
@@ -0,0 +1,46 @@
|
||||
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.Azerty
|
||||
-- Copyright : (c) Devin Mullins <me@twifkak.com>
|
||||
-- License : BSD
|
||||
--
|
||||
-- Maintainer : Devin Mullins <me@twifkak.com>
|
||||
--
|
||||
-- This module fixes some of the keybindings for the francophone among you who
|
||||
-- use an AZERTY keyboard layout. Config stolen from TeXitoi's config on the
|
||||
-- wiki.
|
||||
|
||||
module XMonad.Config.Azerty (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
azertyConfig, azertyKeys
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import qualified Data.Map as M
|
||||
|
||||
-- $usage
|
||||
-- To use this module, start with the following @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad
|
||||
-- > import XMonad.Config.Azerty
|
||||
-- >
|
||||
-- > main = xmonad azertyConfig
|
||||
--
|
||||
-- If you prefer, an azertyKeys function is provided which you can use as so:
|
||||
--
|
||||
-- > import qualified Data.Map as M
|
||||
-- > main = xmonad someConfig { keys = \c -> azertyKeys c `M.union` keys someConfig c }
|
||||
|
||||
azertyConfig = defaultConfig { keys = \c -> azertyKeys c `M.union` keys defaultConfig c }
|
||||
|
||||
azertyKeys conf@(XConfig {modMask = modm}) = M.fromList $
|
||||
[((modm, xK_semicolon), sendMessage (IncMasterN (-1)))]
|
||||
++
|
||||
[((m .|. modm, k), windows $ f i)
|
||||
| (i, k) <- zip (workspaces conf) [0x26,0xe9,0x22,0x27,0x28,0x2d,0xe8,0x5f,0xe7,0xe0],
|
||||
(f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
|
@@ -12,8 +12,6 @@
|
||||
-- environment such as KDE or GNOME.
|
||||
|
||||
module XMonad.Config.Desktop (
|
||||
-- * Usage
|
||||
-- -- $usage
|
||||
desktopConfig,
|
||||
desktopLayoutModifiers
|
||||
) where
|
||||
|
@@ -176,7 +176,7 @@ instance UrgencyHook FocusUrgencyHook Window where
|
||||
s { windowset = until ((Just w ==) . W.peek)
|
||||
W.focusUp $ windowset s }
|
||||
| otherwise =
|
||||
let t = W.tag $ W.workspace $ W.current $ windowset s
|
||||
let t = W.currentTag $ windowset s
|
||||
in s { windowset = until ((Just w ==) . W.peek)
|
||||
W.focusUp $ copyWindow w t $ windowset s }
|
||||
has _ Nothing = False
|
||||
|
@@ -13,7 +13,7 @@
|
||||
|
||||
module XMonad.Config.Gnome (
|
||||
-- * Usage
|
||||
-- -- $usage
|
||||
-- $usage
|
||||
gnomeConfig,
|
||||
gnomeRun
|
||||
) where
|
||||
|
@@ -13,8 +13,9 @@
|
||||
|
||||
module XMonad.Config.Kde (
|
||||
-- * Usage
|
||||
-- -- $usage
|
||||
kdeConfig
|
||||
-- $usage
|
||||
kdeConfig,
|
||||
kde4Config
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
@@ -29,13 +30,24 @@ import qualified Data.Map as M
|
||||
-- > import XMonad.Config.Kde
|
||||
-- >
|
||||
-- > main = xmonad kdeConfig
|
||||
--
|
||||
-- For KDE 4, replace 'kdeConfig' with 'kde4Config'
|
||||
--
|
||||
|
||||
kdeConfig = desktopConfig
|
||||
{ terminal = "konsole"
|
||||
, keys = \c -> kdeKeys c `M.union` keys desktopConfig c }
|
||||
|
||||
kde4Config = desktopConfig
|
||||
{ terminal = "konsole"
|
||||
, keys = \c -> kde4Keys c `M.union` keys desktopConfig c }
|
||||
|
||||
kdeKeys (XConfig {modMask = modm}) = M.fromList $
|
||||
[ ((modm, xK_p), spawn "dcop kdesktop default popupExecuteCommand")
|
||||
, ((modm .|. shiftMask, xK_q), spawn "dcop kdesktop default logout")
|
||||
]
|
||||
|
||||
kde4Keys (XConfig {modMask = modm}) = M.fromList $
|
||||
[ ((modm, xK_p), spawn "krunner")
|
||||
, ((modm .|. shiftMask, xK_q), spawn "dbus-send --print-reply --dest=org.kde.ksmserver /KSMServer org.kde.KSMServerInterface.logout int32:1 int32:0 int32:1")
|
||||
]
|
||||
|
49
XMonad/Config/Monad.hs
Normal file
49
XMonad/Config/Monad.hs
Normal file
@@ -0,0 +1,49 @@
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
|
||||
-- experimental, not expected to work
|
||||
|
||||
{- our goal:
|
||||
config = do
|
||||
add layout Full
|
||||
set terminal "urxvt"
|
||||
add keys [blah blah blah]
|
||||
-}
|
||||
|
||||
{-
|
||||
ideas:
|
||||
composability!
|
||||
"only once" features like avoidStruts, ewmhDesktops
|
||||
-}
|
||||
|
||||
module XMonad.Config.Monad where
|
||||
|
||||
import XMonad hiding (terminal, keys)
|
||||
import qualified XMonad as X
|
||||
import Control.Monad.Writer
|
||||
import Data.Monoid
|
||||
import Data.Accessor
|
||||
import Data.Accessor.Basic hiding (set)
|
||||
|
||||
-- Ugly! To fix this we'll need to change the kind of XConfig.
|
||||
newtype LayoutList a = LL [Layout a] deriving Monoid
|
||||
|
||||
type W = Dual (Endo (XConfig LayoutList))
|
||||
mkW = Dual . Endo
|
||||
|
||||
newtype Config a = C (WriterT W IO a)
|
||||
deriving (Functor, Monad, MonadWriter W)
|
||||
|
||||
-- references:
|
||||
layout = fromSetGet (\x c -> c { layoutHook = x }) layoutHook
|
||||
terminal = fromSetGet (\x c -> c { X.terminal = x }) X.terminal
|
||||
keys = fromSetGet (\x c -> c { X.keys = x }) X.keys
|
||||
|
||||
set :: Accessor (XConfig LayoutList) a -> a -> Config ()
|
||||
set r x = tell (mkW $ r ^= x)
|
||||
add r x = tell (mkW (r ^: mappend x))
|
||||
|
||||
--
|
||||
example :: Config ()
|
||||
example = do
|
||||
add layout $ LL [Layout $ Full] -- make this better
|
||||
set terminal "urxvt"
|
@@ -1,528 +0,0 @@
|
||||
{-# LANGUAGE
|
||||
FlexibleInstances,
|
||||
FlexibleContexts,
|
||||
MultiParamTypeClasses,
|
||||
ExistentialQuantification
|
||||
#-}
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Config.PlainConfig
|
||||
-- Copyright : Braden Shepherdson <Braden.Shepherdson@gmail.com>
|
||||
-- License : BSD3
|
||||
--
|
||||
-- Maintainer : Braden Shepherdson <Braden.Shepherdson@gmail.com>
|
||||
--
|
||||
-- Proof-of-concept (but usable) plain-text configuration file
|
||||
-- parser, for use instead of xmonad.hs. Does not require recompilation,
|
||||
-- allowing xmonad to be free of the GHC dependency.
|
||||
--
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
|
||||
module XMonad.Config.PlainConfig
|
||||
(
|
||||
-- * Introduction
|
||||
-- $usage
|
||||
|
||||
-- * Supported Layouts
|
||||
-- $layouts
|
||||
|
||||
-- * Support Key Bindings
|
||||
-- $keys
|
||||
|
||||
-- * Other Notes
|
||||
-- $notes
|
||||
|
||||
-- * Example Config File
|
||||
-- $example
|
||||
|
||||
plainConfig ,readConfig, checkConfig
|
||||
)
|
||||
where
|
||||
|
||||
|
||||
import XMonad
|
||||
import System.Exit
|
||||
|
||||
import qualified XMonad.StackSet as W
|
||||
import qualified Data.Map as M
|
||||
import Data.List
|
||||
import Data.Maybe (isJust,fromJust)
|
||||
import Data.Char (isSpace)
|
||||
|
||||
|
||||
--import Control.Monad
|
||||
import Control.Monad.Error
|
||||
import Control.Monad.Identity
|
||||
|
||||
import Control.Arrow ((&&&))
|
||||
|
||||
import Text.ParserCombinators.ReadP
|
||||
|
||||
import System.IO
|
||||
import Control.Exception (bracket)
|
||||
|
||||
import XMonad.Util.EZConfig (mkKeymap)
|
||||
|
||||
|
||||
|
||||
-- $usage
|
||||
-- The @xmonad.hs@ file is very minimal when used with PlainConfig.
|
||||
-- It typically contains only the following:
|
||||
--
|
||||
-- > module Main where
|
||||
-- > import XMonad
|
||||
-- > import XMonad.Config.PlainConfig (plainConfig)
|
||||
-- > main = plainConfig
|
||||
--
|
||||
-- The 'plainConfig' function parses @~\/.xmonad\/xmonad.conf@,
|
||||
-- the format of which is described below.
|
||||
|
||||
|
||||
-- $layouts
|
||||
-- Only 'Tall', 'Wide' and 'Full' are supported at present.
|
||||
|
||||
|
||||
|
||||
-- $keys
|
||||
--
|
||||
-- Key bindings are specified as a pair of an arbitrary EZConfig and
|
||||
-- one of the following:
|
||||
--
|
||||
-- @ Name Haskell equivalent Default binding(s)@
|
||||
--
|
||||
-- * @spawn \<cmd\> spawn \"\<cmd\>\" none@
|
||||
--
|
||||
-- * @kill kill M-S-c@
|
||||
--
|
||||
-- * @nextLayout sendMessage NextLayout M-\<Space\>@
|
||||
--
|
||||
-- * @refresh refresh M-S-\<Space\>@
|
||||
--
|
||||
-- * @focusDown windows W.focusDown M-\<Tab\>, M-j@
|
||||
--
|
||||
-- * @focusUp windows W.focusUp M-k@
|
||||
--
|
||||
-- * @focusMaster windows W.focusMaster M-m@
|
||||
--
|
||||
-- * @swapDown windows W.swapDown M-S-j@
|
||||
--
|
||||
-- * @swapUp windows W.swapUp M-S-k@
|
||||
--
|
||||
-- * @swapMaster windows W.swapMaster M-\<Return\>@
|
||||
--
|
||||
-- * @shrink sendMessage Shrink M-h@
|
||||
--
|
||||
-- * @expand sendMessage Expand M-l@
|
||||
--
|
||||
-- * @sink withFocused $ windows . W.sink M-t@
|
||||
--
|
||||
-- * @incMaster sendMessage (IncMasterN 1) M-,@
|
||||
--
|
||||
-- * @decMaster sendMessage (IncMasterN (-1)) M-.@
|
||||
--
|
||||
-- * @quit io $ exitWith ExitSuccess M-S-q@
|
||||
--
|
||||
-- * @restart broadcastMessageReleaseResources >> restart \"xmonad\" True M-q@
|
||||
--
|
||||
|
||||
|
||||
-- $notes
|
||||
-- Submaps are allowed.
|
||||
-- These settings override the defaults. Changes made here will be used over
|
||||
-- the default bindings for those keys.
|
||||
|
||||
|
||||
-- $example
|
||||
-- An example @~\/.xmonad\/xmonad.conf@ file follows:
|
||||
--
|
||||
-- @modMask = 3@
|
||||
--
|
||||
-- @numlockMask = 2@
|
||||
--
|
||||
-- @borderWidth = 1@
|
||||
--
|
||||
-- @normalBorderColor = #dddddd@
|
||||
--
|
||||
-- @focusedBorderColor = #00ff00@
|
||||
--
|
||||
-- @terminal=urxvt@
|
||||
--
|
||||
-- @workspaces=[\"1: IRC\",\"2: Web\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\"]@
|
||||
--
|
||||
-- @focusFollowsMouse=True@
|
||||
--
|
||||
-- @layouts=[\"Tall\",\"Full\",\"Wide\"]@
|
||||
--
|
||||
-- @key=(\"M-x t\", \"spawn xmessage Test\")@
|
||||
--
|
||||
-- @manageHook=(ClassName \"MPlayer\" , \"float\" )@
|
||||
--
|
||||
-- @manageHook=(ClassName \"Gimp\" , \"float\" )@
|
||||
--
|
||||
-- @manageHook=(Resource \"desktop_window\", \"ignore\" )@
|
||||
--
|
||||
-- @manageHook=(Resource \"kdesktop\" , \"ignore\" )@
|
||||
--
|
||||
-- @manageHook=(Resource \"gnome-panel\" , \"ignore\" )@
|
||||
--
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----------------------------------------------------------------
|
||||
------ Several functions for parsing the key-value file. -------
|
||||
----------------------------------------------------------------
|
||||
|
||||
parseKVBy :: Char -> ReadP (String,String)
|
||||
parseKVBy sep = do
|
||||
skipSpaces
|
||||
k <- munch1 (\x -> x /= ' ' && x /= sep)
|
||||
skipSpaces
|
||||
char kvSep
|
||||
skipSpaces
|
||||
v <- munch1 (\x -> x /= ' ') --or EOS
|
||||
return (k,v)
|
||||
|
||||
parseKVVBy :: Char -> ReadP (String,String)
|
||||
parseKVVBy sep = do
|
||||
skipSpaces
|
||||
k <- munch1 (\x -> x /= ' ' && x /= sep)
|
||||
skipSpaces
|
||||
char kvSep
|
||||
skipSpaces
|
||||
v <- munch1 (const True) -- until EOS
|
||||
return (k,v)
|
||||
|
||||
|
||||
kvSep :: Char
|
||||
kvSep = '='
|
||||
|
||||
parseKV, parseKVV :: ReadP (String,String)
|
||||
parseKV = parseKVBy kvSep
|
||||
parseKVV = parseKVVBy kvSep
|
||||
|
||||
|
||||
|
||||
readKV :: String -> Integer -> RC (String,String)
|
||||
readKV s ln = case readP_to_S parseKV s of
|
||||
[((k,v),"")] -> return (k,v) --single, correct parse
|
||||
[] -> throwError [(ln,"No parse")]
|
||||
_ -> do
|
||||
case readP_to_S parseKVV s of
|
||||
[((k,v),"")] -> return (k,v) --single, correct parse
|
||||
[] -> throwError [(ln,"No parse")]
|
||||
xs -> throwError [(ln,"Ambiguous parse: "
|
||||
++ show xs)]
|
||||
|
||||
|
||||
|
||||
isComment :: String -> Bool
|
||||
isComment = not . null . readP_to_S parseComment
|
||||
where parseComment = skipSpaces >> char '#' >> return ()
|
||||
-- null means failed parse, so _not_ a comment.
|
||||
|
||||
|
||||
isBlank :: String -> Bool
|
||||
isBlank = null . filter (not . isSpace)
|
||||
|
||||
|
||||
type RC = ErrorT [(Integer,String)] Identity
|
||||
|
||||
instance Error [(Integer,String)] where
|
||||
noMsg = [(-1, "Unknown error.")]
|
||||
strMsg s = [(-1, s)]
|
||||
|
||||
|
||||
parseFile :: [String] -> RC (XConfig Layout)
|
||||
parseFile ss = parseLines baseConfig theLines
|
||||
where theLines = filter (not . liftM2 (||) isComment isBlank . snd)
|
||||
$ zip [1..] ss
|
||||
|
||||
|
||||
|
||||
parseLines :: XConfig Layout -> [(Integer,String)] -> RC (XConfig Layout)
|
||||
parseLines = foldM parse
|
||||
|
||||
|
||||
parse :: XConfig Layout -> (Integer, String) -> RC (XConfig Layout)
|
||||
parse xc (ln,s) = do
|
||||
(k,v) <- readKV s ln
|
||||
case M.lookup k commands of
|
||||
Nothing -> throwError [(ln,"Unknown command: "++k)]
|
||||
Just f -> f v ln xc
|
||||
|
||||
|
||||
|
||||
|
||||
----------------------------------------------------------------
|
||||
-- Now the semantic parts, that convert from the relevant --
|
||||
-- key-value entries to values in an XConfig --
|
||||
----------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
type Command = String -> Integer -> XConfig Layout -> RC (XConfig Layout)
|
||||
|
||||
commands :: M.Map String Command
|
||||
commands = M.fromList $
|
||||
[("modMask" , cmd_modMask )
|
||||
,("numlockMask" , cmd_numlockMask )
|
||||
,("normalBorderColor" , cmd_normalBorderColor )
|
||||
,("focusedBorderColor" , cmd_focusedBorderColor)
|
||||
,("terminal" , cmd_terminal )
|
||||
,("workspaces" , cmd_workspaces )
|
||||
,("focusFollowsMouse" , cmd_focusFollowsMouse )
|
||||
,("layouts" , cmd_layouts )
|
||||
,("key" , cmd_key )
|
||||
,("manageHook" , cmd_manageHook )
|
||||
,("borderWidth" , cmd_borderWidth )
|
||||
]
|
||||
|
||||
|
||||
-- | Behind-the-scenes helper for both 'cmd_modMask' and 'cmd_numlockMask'.
|
||||
genericModKey :: (KeyMask -> XConfig Layout) -> Command
|
||||
genericModKey f s ln _ = do
|
||||
x <- rcRead s ln :: RC Integer
|
||||
case lookup x (zip [1..] [mod1Mask,mod2Mask,mod3Mask,mod4Mask,mod5Mask]) of
|
||||
Just y -> return $ f y
|
||||
Nothing -> throwError [(ln,"Invalid mod key number: "++ show x)]
|
||||
|
||||
|
||||
-- | Reads the mod key modifier number.
|
||||
cmd_modMask :: Command
|
||||
cmd_modMask s ln xc = genericModKey (\k -> xc{modMask = k}) s ln xc
|
||||
|
||||
-- | Reads the numlock key modifier number.
|
||||
cmd_numlockMask :: Command
|
||||
cmd_numlockMask s ln xc = genericModKey (\k -> xc{numlockMask = k}) s ln xc
|
||||
|
||||
|
||||
-- | Reads the border width.
|
||||
cmd_borderWidth :: Command
|
||||
cmd_borderWidth s ln xc = do
|
||||
w <- rcRead s ln
|
||||
return $ xc { borderWidth = w }
|
||||
|
||||
|
||||
-- | Reads the colors but just keeps them as RRGGBB Strings.
|
||||
cmd_normalBorderColor, cmd_focusedBorderColor :: Command
|
||||
cmd_normalBorderColor s _ xc = return $ xc{ normalBorderColor = s }
|
||||
cmd_focusedBorderColor s _ xc = return $ xc{ focusedBorderColor = s }
|
||||
|
||||
|
||||
-- | Reads the terminal. It is just a String, no parsing.
|
||||
cmd_terminal :: Command
|
||||
cmd_terminal s _ xc = return $ xc{ terminal = s }
|
||||
|
||||
|
||||
-- | Reads the workspace tag list. This is given as a Haskell [String].
|
||||
cmd_workspaces :: Command
|
||||
cmd_workspaces s ln xc = rcRead s ln >>= \x -> return xc{ workspaces = x }
|
||||
|
||||
|
||||
-- | Reads the focusFollowsMouse, as a Haskell Bool.
|
||||
cmd_focusFollowsMouse :: Command
|
||||
cmd_focusFollowsMouse s ln xc = rcRead s ln >>=
|
||||
\x -> return xc{focusFollowsMouse = x}
|
||||
|
||||
|
||||
-- | The list known layouts, mapped by name.
|
||||
-- An easy location for improvement is to add more contrib layouts here.
|
||||
layouts :: M.Map String (Layout Window)
|
||||
layouts = M.fromList
|
||||
[("Tall", Layout (Tall 1 (3/100) (1/2)))
|
||||
,("Wide", Layout (Mirror (Tall 1 (3/100) (1/2))))
|
||||
,("Full", Layout Full)
|
||||
]
|
||||
|
||||
|
||||
-- | Expects a [String], the strings being layout names. Quotes required.
|
||||
-- Draws from the `layouts' list above.
|
||||
cmd_layouts :: Command
|
||||
cmd_layouts s ln xc = do
|
||||
xs <- rcRead s ln -- read the list of strings
|
||||
let ls = map (id &&& (flip M.lookup) layouts) xs
|
||||
when (null ls) $ throwError [(ln,"Empty layout list")]
|
||||
case filter (not . isJust . snd) ls of
|
||||
[] -> return $ xc{ layoutHook = foldr1
|
||||
(\(Layout l) (Layout r) ->
|
||||
Layout (l ||| r)) (map (fromJust . snd) ls)
|
||||
}
|
||||
ys -> throwError $ map (\(x,_) -> (ln, "Unknown layout: "++ x)) ys
|
||||
|
||||
|
||||
|
||||
-- | A Map from names to key binding actions.
|
||||
key_actions :: M.Map String (X ())
|
||||
key_actions = M.fromList
|
||||
[("kill" , kill )
|
||||
,("nextLayout" , sendMessage NextLayout )
|
||||
--,("prevLayout" , sendMessage PrevLayout )
|
||||
--,("resetLayout" , setLayout $ XMonad.layoutHook conf)
|
||||
,("refresh" , refresh )
|
||||
,("focusDown" , windows W.focusDown )
|
||||
,("focusUp" , windows W.focusUp )
|
||||
,("focusMaster" , windows W.focusMaster )
|
||||
,("swapMaster" , windows W.swapMaster )
|
||||
,("swapDown" , windows W.swapDown )
|
||||
,("swapUp" , windows W.swapUp )
|
||||
,("shrink" , sendMessage Shrink )
|
||||
,("expand" , sendMessage Expand )
|
||||
,("sink" , withFocused $ windows . W.sink)
|
||||
,("incMaster" , sendMessage (IncMasterN 1))
|
||||
,("decMaster" , sendMessage (IncMasterN (-1)))
|
||||
,("quit" , io $ exitWith ExitSuccess)
|
||||
,("restart" , broadcastMessage ReleaseResources
|
||||
>> restart "xmonad" True)
|
||||
]
|
||||
|
||||
|
||||
-- | Expects keys as described in the preamble, as
|
||||
-- (\"EZConfig key name\", \"action name\"),
|
||||
-- eg. (\"M-S-t\", \"spawn thunderbird\")
|
||||
-- One key per "key=" line.
|
||||
cmd_key :: Command
|
||||
cmd_key s ln xc = do
|
||||
(k,v) <- rcRead s ln
|
||||
if "spawn " `isPrefixOf` v
|
||||
then return $ xc {
|
||||
keys = \c -> M.union (mkKeymap c
|
||||
[(k, spawn (drop 6 v))]
|
||||
) ((keys xc) c)
|
||||
}
|
||||
else do
|
||||
case M.lookup v key_actions of
|
||||
Nothing -> throwError [(ln, "Unknown key action \"" ++ v ++ "\"")]
|
||||
Just ac -> return $
|
||||
xc { keys = \c -> M.union (mkKeymap c [(k, ac)])
|
||||
((keys xc) c)
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- | Map of names to actions for 'ManageHook's.
|
||||
manageHook_actions :: M.Map String ManageHook
|
||||
manageHook_actions = M.fromList
|
||||
[("float" , doFloat )
|
||||
,("ignore" , doIgnore )
|
||||
]
|
||||
|
||||
|
||||
-- | Parses 'ManageHook's in the form given in the preamble.
|
||||
-- eg. (ClassName \"MPlayer\", \"float\")
|
||||
cmd_manageHook :: Command
|
||||
cmd_manageHook s ln xc = do
|
||||
(k,v) <- rcRead s ln
|
||||
let q = parseQuery k
|
||||
if "toWorkspace " `isPrefixOf` v
|
||||
then return $ xc { manageHook = manageHook xc <+>
|
||||
(q --> doShift (drop 12 v))
|
||||
}
|
||||
else case M.lookup v manageHook_actions of
|
||||
Nothing -> throwError [(ln, "Unknown ManageHook action \""
|
||||
++ v ++ "\"")]
|
||||
Just ac -> return $ xc { manageHook = manageHook xc <+> (q --> ac) }
|
||||
|
||||
|
||||
|
||||
-- | Core of the ManageHook expression parser.
|
||||
-- Taken from Roman Cheplyaka's WindowProperties
|
||||
parseQuery :: Property -> Query Bool
|
||||
parseQuery (Title s) = title =? s
|
||||
parseQuery (ClassName s) = className =? s
|
||||
parseQuery (Resource s) = resource =? s
|
||||
parseQuery (And p q) = parseQuery p <&&> parseQuery q
|
||||
parseQuery (Or p q) = parseQuery p <&&> parseQuery q
|
||||
parseQuery (Not p) = not `fmap` parseQuery p
|
||||
parseQuery (Const b) = return b
|
||||
|
||||
|
||||
-- | Property constructors are quite self-explaining.
|
||||
-- Taken from Roman Cheplyaka's WindowProperties
|
||||
data Property = Title String
|
||||
| ClassName String
|
||||
| Resource String
|
||||
| And Property Property
|
||||
| Or Property Property
|
||||
| Not Property
|
||||
| Const Bool
|
||||
deriving (Read, Show)
|
||||
|
||||
|
||||
|
||||
-- | A wrapping of the read function into the RC monad.
|
||||
rcRead :: (Read a) => String -> Integer -> RC a
|
||||
rcRead s ln = case reads s of
|
||||
[(x,"")] -> return x
|
||||
_ -> throwError [(ln, "Failed to parse value")]
|
||||
|
||||
|
||||
|
||||
-- | The standard Config.hs 'defaultConfig', with the layout wrapped.
|
||||
baseConfig :: XConfig Layout
|
||||
baseConfig = defaultConfig{ layoutHook = Layout (layoutHook defaultConfig) }
|
||||
|
||||
|
||||
|
||||
-- | Core function that attempts to parse @~\/.xmonad\/xmonad.conf@
|
||||
readConfig :: IO (Maybe (XConfig Layout))
|
||||
readConfig = do
|
||||
dir <- getXMonadDir
|
||||
cs <- bracket (openFile (dir++"/xmonad.conf") ReadMode)
|
||||
(\h -> hClose h) -- vv force the lazy IO
|
||||
(\h -> (lines `fmap` hGetContents h) >>= \ss ->
|
||||
length ss `seq` return ss)
|
||||
let xce = runIdentity $ runErrorT $ parseFile cs
|
||||
case xce of
|
||||
Left es -> mapM_ (\(ln,e) ->
|
||||
putStrLn $ "readConfig error: line "++show ln++
|
||||
": "++ e) es
|
||||
>> return Nothing
|
||||
Right xc -> return $ Just xc
|
||||
|
||||
|
||||
-- | Attempts to run readConfig, and checks if it failed.
|
||||
checkConfig :: IO Bool
|
||||
checkConfig = isJust `fmap` readConfig
|
||||
|
||||
|
||||
|
||||
{- REMOVED: It was for debugging, and causes an 'orphaned instances'
|
||||
warning to boot.
|
||||
|
||||
|
||||
|
||||
-- | Reads in the config, and then prints the resulting XConfig
|
||||
dumpConfig :: IO ()
|
||||
dumpConfig = readConfig >>= print
|
||||
|
||||
|
||||
instance Show (XConfig Layout) where
|
||||
show x = "XConfig { "
|
||||
++ "normalBorderColor = "++ normalBorderColor x ++", "
|
||||
++ "focusedBorderColor = "++ focusedBorderColor x++", "
|
||||
++ "terminal = "++ terminal x ++", "
|
||||
++ "workspaces = "++ show (workspaces x) ++", "
|
||||
++ "numlockMask = "++ show (numlockMask x) ++", "
|
||||
++ "modMask = "++ show (modMask x) ++", "
|
||||
++ "borderWidth = "++ show (borderWidth x) ++", "
|
||||
++ "focusFollowsMouse = "++ show (focusFollowsMouse x) ++", "
|
||||
++ "layouts = "++ show (layoutHook x) ++" }"
|
||||
|
||||
-}
|
||||
|
||||
-- | Handles the unwrapping of the Layout. Intended for use as
|
||||
-- @main = plainConfig@
|
||||
plainConfig :: IO ()
|
||||
plainConfig = do
|
||||
conf <- readConfig
|
||||
case conf of
|
||||
(Just xc@XConfig{layoutHook= (Layout l)}) ->
|
||||
xmonad (xc{ layoutHook = l })
|
||||
Nothing ->
|
||||
spawn $ "xmessage Failed to read xmonad.conf. See xmonad.errors."
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
|
||||
module XMonad.Config.Sjanssen (sjanssenConfig) where
|
||||
module XMonad.Config.Sjanssen (sjanssenConfig, sjanssenConfigXmobar) where
|
||||
|
||||
import XMonad hiding (Tall(..))
|
||||
import qualified XMonad.StackSet as W
|
||||
@@ -10,41 +10,49 @@ import XMonad.Config (defaultConfig)
|
||||
import XMonad.Layout.NoBorders
|
||||
import XMonad.Hooks.DynamicLog hiding (xmobar)
|
||||
import XMonad.Hooks.ManageDocks
|
||||
import XMonad.Hooks.EwmhDesktops
|
||||
import XMonad.Prompt
|
||||
import XMonad.Prompt.Shell
|
||||
import XMonad.Util.Run (spawnPipe)
|
||||
import XMonad.Actions.SpawnOn
|
||||
|
||||
import XMonad.Layout.LayoutScreens
|
||||
import XMonad.Layout.TwoPane
|
||||
|
||||
import qualified Data.Map as M
|
||||
import System.IO (hPutStrLn)
|
||||
|
||||
sjanssenConfigXmobar = statusBar "xmobar" sjanssenPP strutkey =<< sjanssenConfig
|
||||
where
|
||||
strutkey (XConfig {modMask = modm}) = (modm, xK_b)
|
||||
|
||||
sjanssenConfig = do
|
||||
xmobar <- spawnPipe "xmobar"
|
||||
sp <- mkSpawner
|
||||
return $ defaultConfig
|
||||
{ terminal = "urxvtc"
|
||||
, workspaces = ["irc", "web"] ++ map show [3 .. 9 :: Int]
|
||||
, logHook = dynamicLogWithPP $ sjanssenPP { ppOutput = hPutStrLn xmobar }
|
||||
, mouseBindings = \(XConfig {modMask = modm}) -> M.fromList $
|
||||
[ ((modm, button1), (\w -> focus w >> mouseMoveWindow w))
|
||||
, ((modm, button2), (\w -> focus w >> windows W.swapMaster))
|
||||
, ((modm.|. shiftMask, button1), (\w -> focus w >> mouseResizeWindow w)) ]
|
||||
, keys = \c -> mykeys c `M.union` keys defaultConfig c
|
||||
, keys = \c -> mykeys sp c `M.union` keys defaultConfig c
|
||||
, layoutHook = modifiers layouts
|
||||
, logHook = ewmhDesktopsLogHook
|
||||
, manageHook = composeAll [className =? x --> doF (W.shift w)
|
||||
| (x, w) <- [ ("Firefox", "web")
|
||||
, ("Ktorrent", "7")]]
|
||||
<+> manageHook defaultConfig <+> manageDocks
|
||||
, ("Ktorrent", "7")
|
||||
, ("Amarokapp", "7")]]
|
||||
<+> manageHook defaultConfig <+> manageDocks <+> manageSpawn sp
|
||||
}
|
||||
where
|
||||
tiled = HintedTile 1 0.03 0.5 TopLeft
|
||||
layouts = (tiled Tall ||| (tiled Wide ||| Full)) ||| tabbed shrinkText myTheme
|
||||
modifiers = avoidStruts . smartBorders
|
||||
modifiers = smartBorders
|
||||
|
||||
mykeys (XConfig {modMask = modm, workspaces = ws}) = M.fromList $
|
||||
[((modm, xK_p ), shellPrompt myPromptConfig)
|
||||
mykeys sp (XConfig {modMask = modm, workspaces = ws}) = M.fromList $
|
||||
[((modm, xK_p ), shellPromptHere sp myPromptConfig)
|
||||
,((modm .|. shiftMask, xK_c ), kill1)
|
||||
,((modm .|. shiftMask .|. controlMask, xK_c ), kill)
|
||||
,((modm .|. shiftMask, xK_0 ), windows $ \w -> foldr copy w ws)
|
||||
,((modm, xK_b ), sendMessage ToggleStruts)
|
||||
,((modm, xK_z ), layoutScreens 2 $ TwoPane 0.5 0.5)
|
||||
,((modm .|. shiftMask, xK_z ), rescreen)
|
||||
]
|
||||
|
||||
myFont = "xft:Bitstream Vera Sans Mono:pixelsize=10"
|
||||
@@ -52,4 +60,6 @@ sjanssenConfig = do
|
||||
myPromptConfig = defaultXPConfig
|
||||
{ position = Top
|
||||
, font = myFont
|
||||
, showCompletionOnTab = True
|
||||
, historyFilter = deleteConsecutive
|
||||
, promptBorderWidth = 0 }
|
||||
|
@@ -13,7 +13,7 @@
|
||||
|
||||
module XMonad.Config.Xfce (
|
||||
-- * Usage
|
||||
-- -- $usage
|
||||
-- $usage
|
||||
xfceConfig
|
||||
) where
|
||||
|
||||
|
@@ -490,6 +490,9 @@ A non complete list with a brief description:
|
||||
workspaces in various ways, used by several other modules which need
|
||||
to sort workspaces (e.g. "XMonad.Hooks.DynamicLog").
|
||||
|
||||
* "XMonad.Util.Paste" provides utilities for pasting or sending keys and
|
||||
strings to windows;
|
||||
|
||||
* "XMonad.Util.XSelection" provide utilities for using the mouse
|
||||
selection;
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Hooks.DynamicLog
|
||||
@@ -23,15 +25,17 @@ module XMonad.Hooks.DynamicLog (
|
||||
-- * Drop-in loggers
|
||||
dzen,
|
||||
xmobar,
|
||||
statusBar,
|
||||
dynamicLog,
|
||||
dynamicLogDzen,
|
||||
dynamicLogXmobar,
|
||||
dynamicLogXinerama,
|
||||
|
||||
-- * Build your own formatter
|
||||
dynamicLogWithPP,
|
||||
dynamicLogString,
|
||||
PP(..), defaultPP, dzenPP, xmobarPP, sjanssenPP, byorgeyPP,
|
||||
PP(..), defaultPP,
|
||||
|
||||
-- * Example formatters
|
||||
dzenPP, xmobarPP, sjanssenPP, byorgeyPP,
|
||||
|
||||
-- * Formatting utilities
|
||||
wrap, pad, shorten,
|
||||
@@ -50,6 +54,7 @@ module XMonad.Hooks.DynamicLog (
|
||||
-- Useful imports
|
||||
--
|
||||
import XMonad
|
||||
import Control.Monad
|
||||
import Data.Maybe ( isJust, catMaybes )
|
||||
import Data.List
|
||||
import qualified Data.Map as M
|
||||
@@ -72,21 +77,17 @@ import XMonad.Hooks.ManageDocks
|
||||
-- > import XMonad.Hooks.DynamicLog
|
||||
--
|
||||
-- If you just want a quick-and-dirty status bar with zero effort, try
|
||||
-- the 'dzen' function, which sets up a dzen status bar with a default
|
||||
-- format:
|
||||
-- the 'xmobar' or 'dzen' functions:
|
||||
--
|
||||
-- > main = dzen xmonad
|
||||
-- > main = xmonad =<< xmobar conf
|
||||
--
|
||||
-- or, to use this with your own custom xmonad configuration,
|
||||
-- There is also 'statusBar' if you'd like to use another status bar, or would
|
||||
-- like to use different formatting options. The 'xmobar', 'dzen', and
|
||||
-- 'statusBar' functions are preferred over the other options listed below, as
|
||||
-- they take care of all the necessary plumbing -- no shell scripting required!
|
||||
--
|
||||
-- > main = dzen $ \conf -> xmonad $ conf { <your customizations> }
|
||||
--
|
||||
-- Also you can use 'xmobar' function instead of 'dzen' in the examples above,
|
||||
-- if you have xmobar installed.
|
||||
--
|
||||
-- Alternatively, you can choose among several default status bar
|
||||
-- formats ('dynamicLog', 'dynamicLogDzen', 'dynamicLogXmobar', or
|
||||
-- 'dynamicLogXinerama') by simply setting your logHook to the
|
||||
-- Alternatively, you can choose among several default status bar formats
|
||||
-- ('dynamicLog' or 'dynamicLogXinerama') by simply setting your logHook to the
|
||||
-- appropriate function, for instance:
|
||||
--
|
||||
-- > main = xmonad $ defaultConfig {
|
||||
@@ -139,69 +140,67 @@ import XMonad.Hooks.ManageDocks
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
-- | Run xmonad with a dzen status bar set to some nice defaults. Output
|
||||
-- is taken from the dynamicLogWithPP hook.
|
||||
-- | Run xmonad with a dzen status bar set to some nice defaults.
|
||||
--
|
||||
-- > main = dzen xmonad
|
||||
-- > main = xmonad =<< dzen conf
|
||||
--
|
||||
-- The intent is that the above config file should provide a nice
|
||||
-- status bar with minimal effort. If you want to customize your xmonad
|
||||
-- configuration while using this, you'll have to do something like
|
||||
--
|
||||
-- > main = dzen $ \conf -> xmonad $ conf { <your customized settings...> }
|
||||
-- status bar with minimal effort.
|
||||
--
|
||||
-- If you wish to customize the status bar format at all, you'll have to
|
||||
-- use something like 'dynamicLogWithPP' instead.
|
||||
-- use the 'statusBar' function instead.
|
||||
--
|
||||
-- The binding uses the XMonad.Hooks.ManageDocks module to automatically
|
||||
-- handle screen placement for dzen, and enables 'mod-b' for toggling
|
||||
-- the menu bar.
|
||||
--
|
||||
dzen ::
|
||||
(XConfig
|
||||
(ModifiedLayout AvoidStruts
|
||||
(Choose Tall (Choose (Mirror Tall) Full))) -> IO t) -> IO t
|
||||
dzen f = do
|
||||
h <- spawnPipe ("dzen2" ++ " " ++ flags)
|
||||
f $ defaultConfig
|
||||
{ logHook = dynamicLogWithPP dzenPP
|
||||
{ ppOutput = hPutStrLn h }
|
||||
,layoutHook = avoidStrutsOn [U] (layoutHook defaultConfig)
|
||||
,keys = \c -> toggleStrutsKey c `M.union` keys defaultConfig c
|
||||
,manageHook = manageHook defaultConfig <+> manageDocks
|
||||
}
|
||||
dzen :: LayoutClass l Window
|
||||
=> XConfig l -> IO (XConfig (ModifiedLayout AvoidStruts l))
|
||||
dzen conf = statusBar ("dzen2 " ++ flags) dzenPP toggleStrutsKey conf
|
||||
where
|
||||
fg = "'#a8a3f7'" -- n.b quoting
|
||||
bg = "'#3f3c6d'"
|
||||
flags = "-e 'onstart=lower' -w 400 -ta l -fg " ++ fg ++ " -bg " ++ bg
|
||||
|
||||
|
||||
-- | Run xmonad with a xmobar status bar set to some nice defaults. Output
|
||||
-- is taken from the dynamicLogWithPP hook.
|
||||
-- | Run xmonad with a xmobar status bar set to some nice defaults.
|
||||
--
|
||||
-- > main = xmobar xmonad
|
||||
-- > main = xmonad =<< xmobar conf
|
||||
--
|
||||
-- This works pretty much the same as 'dzen' function above
|
||||
-- This works pretty much the same as 'dzen' function above.
|
||||
--
|
||||
xmobar ::
|
||||
(XConfig
|
||||
(ModifiedLayout AvoidStruts
|
||||
(Choose Tall (Choose (Mirror Tall) Full))) -> IO t) -> IO t
|
||||
xmobar f = do
|
||||
h <- spawnPipe "xmobar"
|
||||
f $ defaultConfig
|
||||
{ logHook = dynamicLogWithPP xmobarPP { ppOutput = hPutStrLn h }
|
||||
, layoutHook = avoidStruts $ layoutHook defaultConfig
|
||||
, keys = \c -> toggleStrutsKey c `M.union` keys defaultConfig c
|
||||
, manageHook = manageHook defaultConfig <+> manageDocks
|
||||
}
|
||||
xmobar :: LayoutClass l Window
|
||||
=> XConfig l -> IO (XConfig (ModifiedLayout AvoidStruts l))
|
||||
xmobar conf = statusBar "xmobar" xmobarPP toggleStrutsKey conf
|
||||
|
||||
-- | Modifies the given base configuration to launch the given status bar,
|
||||
-- send status information to that bar, and allocate space on the screen edges
|
||||
-- for the bar.
|
||||
statusBar :: LayoutClass l Window
|
||||
=> String -- ^ the command line to launch the status bar
|
||||
-> PP -- ^ the pretty printing options
|
||||
-> (XConfig Layout -> (KeyMask, KeySym))
|
||||
-- ^ the desired key binding to toggle bar visibility
|
||||
-> XConfig l -- ^ the base config
|
||||
-> IO (XConfig (ModifiedLayout AvoidStruts l))
|
||||
statusBar cmd pp k conf = do
|
||||
h <- spawnPipe cmd
|
||||
return $ conf
|
||||
{ layoutHook = avoidStruts (layoutHook conf)
|
||||
, logHook = do
|
||||
logHook conf
|
||||
dynamicLogWithPP pp { ppOutput = hPutStrLn h }
|
||||
, manageHook = manageHook conf <+> manageDocks
|
||||
, keys = liftM2 M.union keys' (keys conf)
|
||||
}
|
||||
where
|
||||
keys' = (`M.singleton` sendMessage ToggleStruts) . k
|
||||
|
||||
-- |
|
||||
-- Helper function which provides ToggleStruts keybinding
|
||||
--
|
||||
toggleStrutsKey :: XConfig t -> M.Map (KeyMask, KeySym) (X ())
|
||||
toggleStrutsKey XConfig{modMask = modm} = M.fromList
|
||||
[ ((modm, xK_b ), sendMessage ToggleStruts) ]
|
||||
toggleStrutsKey :: XConfig t -> (KeyMask, KeySym)
|
||||
toggleStrutsKey XConfig{modMask = modm} = (modm, xK_b )
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
@@ -218,16 +217,6 @@ toggleStrutsKey XConfig{modMask = modm} = M.fromList
|
||||
dynamicLog :: X ()
|
||||
dynamicLog = dynamicLogWithPP defaultPP
|
||||
|
||||
-- | An example log hook that emulates dwm's status bar, using colour
|
||||
-- codes printed to dzen. Requires dzen. Workspaces, xinerama,
|
||||
-- layouts and the window title are handled.
|
||||
dynamicLogDzen :: X ()
|
||||
dynamicLogDzen = dynamicLogWithPP dzenPP
|
||||
|
||||
-- | These are good defaults to be used with the xmobar status bar.
|
||||
dynamicLogXmobar :: X ()
|
||||
dynamicLogXmobar = dynamicLogWithPP xmobarPP
|
||||
|
||||
-- | Format the current status using the supplied pretty-printing format,
|
||||
-- and write it to stdout.
|
||||
dynamicLogWithPP :: PP -> X ()
|
||||
@@ -269,7 +258,7 @@ dynamicLogString pp = do
|
||||
pprWindowSet :: WorkspaceSort -> [Window] -> PP -> WindowSet -> String
|
||||
pprWindowSet sort' urgents pp s = sepBy (ppWsSep pp) . map fmt . sort' $
|
||||
map S.workspace (S.current s : S.visible s) ++ S.hidden s
|
||||
where this = S.tag (S.workspace (S.current s))
|
||||
where this = S.currentTag s
|
||||
visibles = map (S.tag . S.workspace) (S.visible s)
|
||||
|
||||
fmt w = printer pp (S.tag w)
|
||||
|
@@ -29,11 +29,10 @@ module XMonad.Hooks.EventHook
|
||||
, HandleEvent
|
||||
) where
|
||||
|
||||
import Control.Applicative ((<$>))
|
||||
import Data.Maybe
|
||||
|
||||
import XMonad
|
||||
import XMonad.StackSet (StackSet (..), Screen (..), Workspace (..))
|
||||
import XMonad.StackSet (Workspace (..), currentTag)
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your
|
||||
@@ -89,7 +88,7 @@ instance Message EventHandleMsg
|
||||
instance (EventHook eh, LayoutClass l a) => LayoutClass (HandleEvent eh l) a where
|
||||
runLayout (Workspace i (HandleEvent Nothing True eh l) ms) r = do
|
||||
broadcastMessage HandlerOff
|
||||
iws <- (tag . workspace . current) <$> gets windowset
|
||||
iws <- gets (currentTag . windowset)
|
||||
(wrs, ml) <- runLayout (Workspace i l ms) r
|
||||
return (wrs, Just $ HandleEvent (Just iws) True eh (fromMaybe l ml))
|
||||
|
||||
|
@@ -82,26 +82,28 @@ ewmhDesktopsLogHookCustom f = withWindowSet $ \s -> do
|
||||
-- Names thereof
|
||||
setDesktopNames (map W.tag ws)
|
||||
|
||||
-- Current desktop
|
||||
let curr = fromJust $ elemIndex (W.tag (W.workspace (W.current s))) $ map W.tag ws
|
||||
|
||||
setCurrentDesktop curr
|
||||
|
||||
-- all windows, with focused windows last
|
||||
let wins = nub . concatMap (maybe [] (\(W.Stack x l r)-> reverse l ++ r ++ [x]) . W.stack) $ ws
|
||||
setClientList wins
|
||||
|
||||
-- Per window Desktop
|
||||
-- To make gnome-panel accept our xinerama stuff, we display
|
||||
-- all visible windows on the current desktop.
|
||||
forM_ (W.current s : W.visible s) $ \x ->
|
||||
forM_ (W.integrate' (W.stack (W.workspace x))) $ \win -> do
|
||||
setWindowDesktop win curr
|
||||
-- Current desktop
|
||||
case (elemIndex (W.currentTag s) $ map W.tag ws) of
|
||||
Nothing -> return ()
|
||||
Just curr -> do
|
||||
setCurrentDesktop curr
|
||||
|
||||
-- Per window Desktop
|
||||
-- To make gnome-panel accept our xinerama stuff, we display
|
||||
-- all visible windows on the current desktop.
|
||||
forM_ (W.current s : W.visible s) $ \x ->
|
||||
forM_ (W.integrate' (W.stack (W.workspace x))) $ \win -> do
|
||||
setWindowDesktop win curr
|
||||
|
||||
forM_ (W.hidden s) $ \w ->
|
||||
let wn = fromJust $ elemIndex (W.tag w) (map W.tag ws) in
|
||||
forM_ (W.integrate' (W.stack w)) $ \win -> do
|
||||
setWindowDesktop win wn
|
||||
case elemIndex (W.tag w) (map W.tag ws) of
|
||||
Nothing -> return ()
|
||||
Just wn -> forM_ (W.integrate' (W.stack w)) $ \win -> do
|
||||
setWindowDesktop win wn
|
||||
|
||||
setActiveWindow
|
||||
|
||||
@@ -138,6 +140,7 @@ handle ClientMessageEvent {
|
||||
a_d <- getAtom "_NET_WM_DESKTOP"
|
||||
a_aw <- getAtom "_NET_ACTIVE_WINDOW"
|
||||
a_cw <- getAtom "_NET_CLOSE_WINDOW"
|
||||
a_ignore <- mapM getAtom ["XMONAD_TIMER"]
|
||||
if mt == a_cd then do
|
||||
let n = fromIntegral (head d)
|
||||
if 0 <= n && n < length ws then
|
||||
@@ -151,8 +154,9 @@ handle ClientMessageEvent {
|
||||
else if mt == a_aw then do
|
||||
windows $ W.focusWindow w
|
||||
else if mt == a_cw then do
|
||||
windows $ W.focusWindow w
|
||||
kill
|
||||
killWindow w
|
||||
else if mt `elem` a_ignore then do
|
||||
return ()
|
||||
else trace $ "Unknown ClientMessageEvent " ++ show mt
|
||||
handle _ = undefined -- does not happen, as otherwise ewmhDesktopsHook would not match
|
||||
|
||||
|
@@ -15,6 +15,7 @@
|
||||
module XMonad.Hooks.FadeInactive (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
setOpacity,
|
||||
fadeInactiveLogHook
|
||||
) where
|
||||
|
||||
@@ -68,8 +69,9 @@ fadeIn = flip setOpacity 0xffffffff
|
||||
-- lowers the opacity of inactive windows to the specified amount
|
||||
fadeInactiveLogHook :: Integer -> X ()
|
||||
fadeInactiveLogHook amt = withWindowSet $ \s ->
|
||||
forM_ (concatMap visibleWins $ W.current s : W.visible s) (fadeOut amt) >>
|
||||
forM_ (visibleWins s) (fadeOut amt) >>
|
||||
withFocused fadeIn
|
||||
where
|
||||
visibleWins = maybe [] unfocused . W.stack . W.workspace
|
||||
visibleWins s = (maybe [] unfocused . W.stack . W.workspace) (W.current s) ++
|
||||
concatMap (W.integrate' . W.stack . W.workspace) (W.visible s)
|
||||
unfocused (W.Stack _ l r) = l ++ r
|
||||
|
@@ -25,10 +25,13 @@
|
||||
-- > }
|
||||
|
||||
module XMonad.Hooks.ManageHelpers (
|
||||
Side(..),
|
||||
composeOne,
|
||||
(-?>), (/=?), (<==?), (</=?), (-->>), (-?>>),
|
||||
isKDETrayWindow,
|
||||
isFullscreen,
|
||||
isDialog,
|
||||
pid,
|
||||
transientTo,
|
||||
maybeToDefinite,
|
||||
MaybeManageHook,
|
||||
@@ -36,7 +39,9 @@ module XMonad.Hooks.ManageHelpers (
|
||||
transience',
|
||||
doRectFloat,
|
||||
doFullFloat,
|
||||
doCenterFloat
|
||||
doCenterFloat,
|
||||
doSideFloat,
|
||||
doHideIgnore
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
@@ -45,6 +50,13 @@ import qualified XMonad.StackSet as W
|
||||
import Data.Maybe
|
||||
import Data.Monoid
|
||||
|
||||
import System.Posix (ProcessID)
|
||||
|
||||
-- | Denotes a side of a screen. @S@ stands for South, @NE@ for Northwest
|
||||
-- etc. @C@ stands for Center.
|
||||
data Side = SC | NC | CE | CW | SE | SW | NE | NW | C
|
||||
deriving (Read, Show, Eq)
|
||||
|
||||
-- | A ManageHook that may or may not have been executed; the outcome is embedded in the Maybe
|
||||
type MaybeManageHook = Query (Maybe (Endo WindowSet))
|
||||
-- | A grouping type, which can hold the outcome of a predicate Query.
|
||||
@@ -124,6 +136,26 @@ isFullscreen = ask >>= \w -> liftX $ do
|
||||
Just xs -> fromIntegral full `elem` xs
|
||||
_ -> False
|
||||
|
||||
-- | A predicate to check whether a window is a dialog.
|
||||
isDialog :: Query Bool
|
||||
isDialog = ask >>= \w -> liftX $ do
|
||||
dpy <- asks display
|
||||
w_type <- getAtom "_NET_WM_WINDOW_TYPE"
|
||||
w_dialog <- getAtom "_NET_WM_WINDOW_TYPE_DIALOG"
|
||||
r <- io $ getWindowProperty32 dpy w_type w
|
||||
return $ case r of
|
||||
Just xs -> fromIntegral w_dialog `elem` xs
|
||||
_ -> False
|
||||
|
||||
pid :: Query (Maybe ProcessID)
|
||||
pid = ask >>= \w -> liftX $ do
|
||||
dpy <- asks display
|
||||
a <- getAtom "_NET_WM_PID"
|
||||
p <- io $ getWindowProperty32 dpy a w
|
||||
return $ case p of
|
||||
Just [x] -> Just (fromIntegral x)
|
||||
_ -> Nothing
|
||||
|
||||
-- | A predicate to check whether a window is Transient.
|
||||
-- It holds the result which might be the window it is transient to
|
||||
-- or it might be 'Nothing'.
|
||||
@@ -160,8 +192,24 @@ doRectFloat r = ask >>= \w -> doF (W.float w r)
|
||||
doFullFloat :: ManageHook
|
||||
doFullFloat = doRectFloat $ W.RationalRect 0 0 1 1
|
||||
|
||||
-- | Floats a new window with its original size on the specified side of a
|
||||
-- screen
|
||||
doSideFloat :: Side -> ManageHook
|
||||
doSideFloat side = ask >>= \w -> doF . W.float w . move . snd =<< liftX (floatLocation w)
|
||||
where
|
||||
move (W.RationalRect _ _ w h) = W.RationalRect cx cy w h
|
||||
where
|
||||
cx = if side `elem` [SC,C ,NC] then (1-w)/2
|
||||
else if side `elem` [SW,CW,NW] then 0
|
||||
else {- side `elem` [SE,CE,NE] -} 1-w
|
||||
cy = if side `elem` [CE,C ,CW] then (1-h)/2
|
||||
else if side `elem` [NE,NC,NW] then 0
|
||||
else {- side `elem` [SE,SC,SW] -} 1-h
|
||||
|
||||
-- | Floats a new window with its original size, but centered.
|
||||
doCenterFloat :: ManageHook
|
||||
doCenterFloat = ask >>= \w -> doF . W.float w . center . snd =<< liftX (floatLocation w)
|
||||
where
|
||||
center (W.RationalRect _ _ w h) = W.RationalRect ((1-w)/2) ((1-h)/2) w h
|
||||
doCenterFloat = doSideFloat C
|
||||
|
||||
-- | Hides window and ignores it.
|
||||
doHideIgnore :: ManageHook
|
||||
doHideIgnore = ask >>= \w -> liftX (hide w) >> doF (W.delete w)
|
||||
|
@@ -53,12 +53,13 @@ module XMonad.Hooks.UrgencyHook (
|
||||
-- * Stuff for your config file:
|
||||
withUrgencyHook, withUrgencyHookC,
|
||||
UrgencyConfig(..), urgencyConfig,
|
||||
SuppressWhen(..),
|
||||
focusUrgent,
|
||||
SuppressWhen(..), RemindWhen(..),
|
||||
focusUrgent, clearUrgents,
|
||||
dzenUrgencyHook,
|
||||
DzenUrgencyHook(..), seconds,
|
||||
DzenUrgencyHook(..),
|
||||
NoUrgencyHook(..),
|
||||
FocusHook(..),
|
||||
minutes, seconds,
|
||||
-- * Stuff for developers:
|
||||
readUrgents, withUrgents,
|
||||
StdoutUrgencyHook(..),
|
||||
@@ -72,12 +73,13 @@ import qualified XMonad.StackSet as W
|
||||
import XMonad.Hooks.EventHook
|
||||
import XMonad.Util.Dzen (dzenWithArgs, seconds)
|
||||
import XMonad.Util.NamedWindows (getName)
|
||||
import XMonad.Util.Timer (TimerId, startTimer, handleTimer)
|
||||
|
||||
import Control.Applicative ((<$>))
|
||||
import Control.Monad (when)
|
||||
import Data.Bits (testBit)
|
||||
import Data.IORef
|
||||
import Data.List ((\\), delete)
|
||||
import Data.List (delete)
|
||||
import Data.Maybe (listToMaybe, maybeToList)
|
||||
import qualified Data.Set as S
|
||||
import Foreign (unsafePerformIO)
|
||||
@@ -193,19 +195,18 @@ import Foreign (unsafePerformIO)
|
||||
-- Hopefully you already read the section on how to configure xmonad. If not,
|
||||
-- hopefully you know where to find it.
|
||||
|
||||
-- | This is the method to enable an urgency hook. It suppresses urgency status
|
||||
-- for windows that are currently visible. If you'd like to change that behavior,
|
||||
-- use 'withUrgencyHookC'.
|
||||
-- | This is the method to enable an urgency hook. It uses the default
|
||||
-- 'urgencyConfig' to control behavior. To change this, use 'withUrgencyHook'
|
||||
-- instead.
|
||||
withUrgencyHook :: (LayoutClass l Window, UrgencyHook h) =>
|
||||
h -> XConfig l -> XConfig (HandleEvent (WithUrgencyHook h) l)
|
||||
withUrgencyHook hook conf = withUrgencyHookC hook urgencyConfig conf
|
||||
|
||||
-- | If you'd like to configure *when* to trigger the urgency hook, call this
|
||||
-- function with a custom 'UrgencyConfig'. Or, by example:
|
||||
-- | This lets you modify the defaults set in 'urgencyConfig'. An example:
|
||||
--
|
||||
-- > withUrgencyHookC dzenUrgencyHook { ... } urgencyConfig { suppressWhen = Focused }
|
||||
--
|
||||
-- (Don't type the @...@, you dolt.) See documentation on your options at 'SuppressWhen'.
|
||||
-- (Don't type the @...@, you dolt.) See 'UrgencyConfig' for details on configuration.
|
||||
withUrgencyHookC :: (LayoutClass l Window, UrgencyHook h) =>
|
||||
h -> UrgencyConfig -> XConfig l -> XConfig (HandleEvent (WithUrgencyHook h) l)
|
||||
withUrgencyHookC hook urgConf conf = conf {
|
||||
@@ -213,16 +214,13 @@ withUrgencyHookC hook urgConf conf = conf {
|
||||
logHook = cleanupUrgents (suppressWhen urgConf) >> logHook conf
|
||||
}
|
||||
|
||||
-- | Global configuration, applicable to all types of 'UrgencyHook'.
|
||||
-- | Global configuration, applied to all types of 'UrgencyHook'. See
|
||||
-- 'urgencyConfig' for the defaults.
|
||||
data UrgencyConfig = UrgencyConfig
|
||||
{ suppressWhen :: SuppressWhen -- ^ see 'SuppressWhen' for options
|
||||
{ suppressWhen :: SuppressWhen -- ^ when to trigger the urgency hook
|
||||
, remindWhen :: RemindWhen -- ^ when to re-trigger the urgency hook
|
||||
} deriving (Read, Show)
|
||||
|
||||
-- | The default 'UrgencyConfig'. Use a variation of this in your config just
|
||||
-- as you use a variation of defaultConfig for your xmonad definition.
|
||||
urgencyConfig :: UrgencyConfig
|
||||
urgencyConfig = UrgencyConfig { suppressWhen = Visible }
|
||||
|
||||
-- | A set of choices as to /when/ you should (or rather, shouldn't) be notified of an urgent window.
|
||||
-- The default is 'Visible'. Prefix each of the following with \"don't bug me when\":
|
||||
data SuppressWhen = Visible -- ^ the window is currently visible
|
||||
@@ -231,6 +229,26 @@ data SuppressWhen = Visible -- ^ the window is currently visible
|
||||
| Never -- ^ ... aww, heck, go ahead and bug me, just in case.
|
||||
deriving (Read, Show)
|
||||
|
||||
-- | A set of choices as to when you want to be re-notified of an urgent
|
||||
-- window. Perhaps you focused on something and you miss the dzen popup bar. Or
|
||||
-- you're AFK. Or you feel the need to be more distracted. I don't care.
|
||||
--
|
||||
-- The interval arguments are in seconds. See the 'minutes' helper.
|
||||
data RemindWhen = Dont -- ^ triggering once is enough
|
||||
| Repeatedly Int Interval -- ^ repeat <arg1> times every <arg2> seconds
|
||||
| Every Interval -- ^ repeat every <arg1> until the urgency hint is cleared
|
||||
deriving (Read, Show)
|
||||
|
||||
-- | A prettified way of multiplying by 60. Use like: @(5 `minutes`)@.
|
||||
minutes :: Rational -> Rational
|
||||
minutes secs = secs * 60
|
||||
|
||||
-- | The default 'UrgencyConfig'. suppressWhen = Visible, remindWhen = Dont.
|
||||
-- Use a variation of this in your config just as you use a variation of
|
||||
-- defaultConfig for your xmonad definition.
|
||||
urgencyConfig :: UrgencyConfig
|
||||
urgencyConfig = UrgencyConfig { suppressWhen = Visible, remindWhen = Dont }
|
||||
|
||||
-- | Focuses the most recently urgent window. Good for what ails ya -- I mean, your keybindings.
|
||||
-- Example keybinding:
|
||||
--
|
||||
@@ -238,6 +256,13 @@ data SuppressWhen = Visible -- ^ the window is currently visible
|
||||
focusUrgent :: X ()
|
||||
focusUrgent = withUrgents $ flip whenJust (windows . W.focusWindow) . listToMaybe
|
||||
|
||||
-- | Just makes the urgents go away.
|
||||
-- Example keybinding:
|
||||
--
|
||||
-- > , ((modMask .|. shiftMask, xK_BackSpace), clearUrgents)
|
||||
clearUrgents :: X ()
|
||||
clearUrgents = adjustUrgents (const []) >> adjustReminders (const [])
|
||||
|
||||
-- | Stores the global set of all urgent windows, across workspaces. Not exported -- use
|
||||
-- 'readUrgents' or 'withUrgents' instead.
|
||||
{-# NOINLINE urgents #-}
|
||||
@@ -255,7 +280,35 @@ readUrgents = io $ readIORef urgents
|
||||
withUrgents :: ([Window] -> X a) -> X a
|
||||
withUrgents f = readUrgents >>= f
|
||||
|
||||
data WithUrgencyHook h = WithUrgencyHook h UrgencyConfig deriving (Read, Show)
|
||||
adjustUrgents :: ([Window] -> [Window]) -> X ()
|
||||
adjustUrgents f = io $ modifyIORef urgents f
|
||||
|
||||
type Interval = Rational
|
||||
|
||||
-- | An urgency reminder, as reified for 'RemindWhen'.
|
||||
-- The last value is the countdown number, for 'Repeatedly'.
|
||||
data Reminder = Reminder { timer :: TimerId
|
||||
, window :: Window
|
||||
, interval :: Interval
|
||||
, remaining :: Maybe Int
|
||||
} deriving Eq
|
||||
|
||||
-- | Stores the list of urgency reminders.
|
||||
{-# NOINLINE reminders #-}
|
||||
reminders :: IORef [Reminder]
|
||||
reminders = unsafePerformIO (newIORef [])
|
||||
|
||||
readReminders :: X [Reminder]
|
||||
readReminders = io $ readIORef reminders
|
||||
|
||||
adjustReminders :: ([Reminder] -> [Reminder]) -> X ()
|
||||
adjustReminders f = io $ modifyIORef reminders f
|
||||
|
||||
clearUrgency :: Window -> X ()
|
||||
clearUrgency w = adjustUrgents (delete w) >> adjustReminders (filter $ (w /=) . window)
|
||||
|
||||
data WithUrgencyHook h = WithUrgencyHook h UrgencyConfig
|
||||
deriving (Read, Show)
|
||||
|
||||
-- The Non-ICCCM Manifesto:
|
||||
-- Note: Some non-standard choices have been made in this implementation to
|
||||
@@ -270,41 +323,53 @@ data WithUrgencyHook h = WithUrgencyHook h UrgencyConfig deriving (Read, Show)
|
||||
-- set urgency if a window is visible. If you have a better idea, please, let us
|
||||
-- know!
|
||||
instance UrgencyHook h => EventHook (WithUrgencyHook h) where
|
||||
handleEvent wuh event =
|
||||
case event of
|
||||
handleEvent wuh event = case event of
|
||||
PropertyEvent { ev_event_type = t, ev_atom = a, ev_window = w } -> do
|
||||
when (t == propertyNotify && a == wM_HINTS) $ withDisplay $ \dpy -> do
|
||||
WMHints { wmh_flags = flags } <- io $ getWMHints dpy w
|
||||
if (testBit flags urgencyHintBit) then do
|
||||
-- Add to list of urgents.
|
||||
adjustUrgents (\ws -> if elem w ws then ws else w : ws)
|
||||
-- Call the urgencyHook.
|
||||
callUrgencyHook wuh w
|
||||
else do
|
||||
-- Remove from list of urgents.
|
||||
adjustUrgents (delete w)
|
||||
-- Call logHook after IORef has been modified.
|
||||
userCode =<< asks (logHook . config)
|
||||
DestroyWindowEvent {ev_window = w} -> do
|
||||
adjustUrgents (delete w)
|
||||
else
|
||||
clearUrgency w
|
||||
userCodeDef () =<< asks (logHook . config) -- call *after* IORef has been modified
|
||||
DestroyWindowEvent {ev_window = w} ->
|
||||
clearUrgency w
|
||||
_ ->
|
||||
return ()
|
||||
|
||||
adjustUrgents :: ([Window] -> [Window]) -> X ()
|
||||
adjustUrgents f = io $ modifyIORef urgents f
|
||||
mapM_ handleReminder =<< readReminders
|
||||
where handleReminder reminder = handleTimer (timer reminder) event $ reminderHook wuh reminder
|
||||
|
||||
callUrgencyHook :: UrgencyHook h => WithUrgencyHook h -> Window -> X ()
|
||||
callUrgencyHook (WithUrgencyHook hook UrgencyConfig { suppressWhen = sw }) w =
|
||||
whenX (not <$> shouldSuppress sw w)
|
||||
(userCode $ urgencyHook hook w)
|
||||
callUrgencyHook (WithUrgencyHook hook UrgencyConfig { suppressWhen = sw, remindWhen = rw }) w =
|
||||
whenX (not <$> shouldSuppress sw w) $ do
|
||||
userCodeDef () $ urgencyHook hook w
|
||||
case rw of
|
||||
Repeatedly times int -> addReminder w int $ Just times
|
||||
Every int -> addReminder w int Nothing
|
||||
Dont -> return ()
|
||||
|
||||
addReminder :: Window -> Rational -> Maybe Int -> X ()
|
||||
addReminder w int times = do
|
||||
timerId <- startTimer int
|
||||
let reminder = Reminder timerId w int times
|
||||
adjustReminders (\rs -> if w `elem` map window rs then rs else reminder : rs)
|
||||
|
||||
reminderHook :: UrgencyHook h => WithUrgencyHook h -> Reminder -> X (Maybe a)
|
||||
reminderHook (WithUrgencyHook hook _) reminder = do
|
||||
case remaining reminder of
|
||||
Just x | x > 0 -> remind $ Just (x - 1)
|
||||
Just _ -> adjustReminders $ delete reminder
|
||||
Nothing -> remind Nothing
|
||||
return Nothing
|
||||
where remind remaining' = do userCode $ urgencyHook hook (window reminder)
|
||||
adjustReminders $ delete reminder
|
||||
addReminder (window reminder) (interval reminder) remaining'
|
||||
|
||||
shouldSuppress :: SuppressWhen -> Window -> X Bool
|
||||
shouldSuppress sw w = elem w <$> suppressibleWindows sw
|
||||
|
||||
cleanupUrgents :: SuppressWhen -> X ()
|
||||
cleanupUrgents sw = do
|
||||
suppressibles <- suppressibleWindows sw
|
||||
adjustUrgents (\\ suppressibles)
|
||||
cleanupUrgents sw = mapM_ clearUrgency =<< suppressibleWindows sw
|
||||
|
||||
suppressibleWindows :: SuppressWhen -> X [Window]
|
||||
suppressibleWindows Visible = gets $ S.toList . mapped
|
||||
@@ -356,7 +421,7 @@ instance UrgencyHook FocusHook where
|
||||
-- Defaults to a duration of five seconds, and no extra args to dzen.
|
||||
-- See 'DzenUrgencyHook'.
|
||||
dzenUrgencyHook :: DzenUrgencyHook
|
||||
dzenUrgencyHook = DzenUrgencyHook { duration = (5 `seconds`), args = [] }
|
||||
dzenUrgencyHook = DzenUrgencyHook { duration = seconds 5, args = [] }
|
||||
|
||||
-- | Spawn a commandline thing, appending the window id to the prefix string
|
||||
-- you provide. (Make sure to add a space if you need it.) Do your crazy
|
||||
|
110
XMonad/Layout/CenteredMaster.hs
Normal file
110
XMonad/Layout/CenteredMaster.hs
Normal file
@@ -0,0 +1,110 @@
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.CenteredMaster
|
||||
-- Copyright : (c) 2009 Ilya Portnov
|
||||
-- License : GNU GPL v3 or any later
|
||||
--
|
||||
-- Maintainer : Ilya Portnov <portnov84@rambler.ru>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Two layout modifiers. centerMaster places master window at center,
|
||||
-- on top of all other windows, which are managed by base layout.
|
||||
-- topRightMaster is similar, but places master window in top right corner
|
||||
-- instead of center.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.CenteredMaster (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
|
||||
centerMaster,
|
||||
topRightMaster
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Layout.LayoutModifier
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
-- $usage
|
||||
-- This module defines two new layout modifiers: centerMaster and topRightMaster.
|
||||
-- centerMaster places master window at center of screen, on top of others.
|
||||
-- All other windows in background are managed by base layout.
|
||||
-- topRightMaster is like centerMaster, but places master window in top right corner instead of center.
|
||||
--
|
||||
-- Yo can use this module by adding folowing in your @xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.CenteredMaster
|
||||
--
|
||||
-- Then add layouts to your layoutHook:
|
||||
--
|
||||
-- > myLayoutHook = centerMaster Grid ||| ...
|
||||
|
||||
-- | Function that decides where master window should be placed
|
||||
type Positioner = Rectangle -> Rectangle
|
||||
|
||||
-- | Data type for LayoutModifier
|
||||
data CenteredMaster a = CenteredMaster deriving (Read,Show)
|
||||
|
||||
instance LayoutModifier CenteredMaster Window where
|
||||
modifyLayout CenteredMaster = applyPosition (center (5/7) (5/7))
|
||||
|
||||
data TopRightMaster a = TopRightMaster deriving (Read,Show)
|
||||
|
||||
instance LayoutModifier TopRightMaster Window where
|
||||
modifyLayout TopRightMaster = applyPosition (topRight (3/7) (1/2))
|
||||
|
||||
-- | Modifier that puts master window in center, other windows in background
|
||||
-- are managed by given layout
|
||||
centerMaster :: LayoutClass l a => l a -> ModifiedLayout CenteredMaster l a
|
||||
centerMaster = ModifiedLayout CenteredMaster
|
||||
|
||||
-- | Modifier that puts master window in top right corner, other windows in background
|
||||
-- are managed by given layout
|
||||
topRightMaster :: LayoutClass l a => l a -> ModifiedLayout TopRightMaster l a
|
||||
topRightMaster = ModifiedLayout TopRightMaster
|
||||
|
||||
-- | Internal function, doing main job
|
||||
applyPosition :: (LayoutClass l a, Eq a) =>
|
||||
Positioner
|
||||
-> W.Workspace WorkspaceId (l a) a
|
||||
-> Rectangle
|
||||
-> X ([(a, Rectangle)], Maybe (l a))
|
||||
|
||||
applyPosition pos wksp rect = do
|
||||
let stack = W.stack wksp
|
||||
let ws = W.integrate' $ stack
|
||||
if null ws then
|
||||
runLayout wksp rect
|
||||
else do
|
||||
let first = head ws
|
||||
let other = tail ws
|
||||
let filtStack = stack >>= W.filter (first /=)
|
||||
wrs <- runLayout (wksp {W.stack = filtStack}) rect
|
||||
return ((first, place pos other rect) : fst wrs, snd wrs)
|
||||
|
||||
-- | Place master window (it's Rectangle is given), using the given Positioner.
|
||||
-- If second argument is empty (that is, there is only one window on workspace),
|
||||
-- place that window fullscreen.
|
||||
place :: Positioner -> [a] -> Rectangle -> Rectangle
|
||||
place _ [] rect = rect
|
||||
place pos _ rect = pos rect
|
||||
|
||||
-- | Function that calculates Rectangle at top right corner of given Rectangle
|
||||
topRight :: Float -> Float -> Rectangle -> Rectangle
|
||||
topRight rx ry (Rectangle sx sy sw sh) = Rectangle x sy w h
|
||||
where w = round (fromIntegral sw * rx)
|
||||
h = round (fromIntegral sh * ry)
|
||||
x = sx + fromIntegral (sw-w)
|
||||
|
||||
-- | Function that calculates Rectangle at center of given Rectangle.
|
||||
center :: Float -> Float -> Rectangle -> Rectangle
|
||||
center rx ry (Rectangle sx sy sw sh) = Rectangle x y w h
|
||||
where w = round (fromIntegral sw * rx)
|
||||
h = round (fromIntegral sh * ry)
|
||||
x = sx + fromIntegral (sw-w) `div` 2
|
||||
y = sy + fromIntegral (sh-h) `div` 2
|
||||
|
||||
|
@@ -24,7 +24,7 @@ module XMonad.Layout.Decoration
|
||||
, DecorationStyle (..)
|
||||
, DefaultDecoration (..)
|
||||
, Shrinker (..), DefaultShrinker
|
||||
, shrinkText, CustomShrink ( CustomShrink )
|
||||
, shrinkText, CustomShrink ( CustomShrink ), shrinkWhile
|
||||
, isInStack, isVisible, isInvisible, isWithin, fi
|
||||
, module XMonad.Layout.LayoutModifier
|
||||
) where
|
||||
@@ -201,7 +201,12 @@ instance Eq a => DecorationStyle DefaultDecoration a
|
||||
-- 'handleEvent', which will call the appropriate 'DecorationStyle'
|
||||
-- methods to perform its tasks.
|
||||
instance (DecorationStyle ds Window, Shrinker s) => LayoutModifier (Decoration ds s) Window where
|
||||
redoLayout (Decoration st sh t ds) sc stack wrs
|
||||
redoLayout (Decoration (I (Just s)) sh t ds) _ Nothing _ = do
|
||||
releaseResources s
|
||||
return ([], Just $ Decoration (I Nothing) sh t ds)
|
||||
redoLayout _ _ Nothing _ = return ([], Nothing)
|
||||
|
||||
redoLayout (Decoration st sh t ds) sc (Just stack) wrs
|
||||
| I Nothing <- st = initState t ds sc stack wrs >>= processState
|
||||
| I (Just s) <- st = do let dwrs = decos s
|
||||
(d,a) = curry diff (get_ws dwrs) ws
|
||||
@@ -264,11 +269,6 @@ instance (DecorationStyle ds Window, Shrinker s) => LayoutModifier (Decoration d
|
||||
return $ Just $ Decoration (I Nothing) sh t ds
|
||||
handleMess _ _ = return Nothing
|
||||
|
||||
emptyLayoutMod (Decoration (I (Just s)) sh t ds) _ _ = do
|
||||
releaseResources s
|
||||
return ([], Just $ Decoration (I Nothing) sh t ds)
|
||||
emptyLayoutMod _ _ _ = return ([], Nothing)
|
||||
|
||||
modifierDescription (Decoration _ _ _ ds) = describeDeco ds
|
||||
|
||||
-- | By default 'Decoration' handles 'PropertyEvent' and 'ExposeEvent'
|
||||
|
91
XMonad/Layout/FixedColumn.hs
Normal file
91
XMonad/Layout/FixedColumn.hs
Normal file
@@ -0,0 +1,91 @@
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances #-}
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.FixedColumn
|
||||
-- Copyright : (c) 2008 Justin Bogner <mail@justinbogner.com>
|
||||
-- License : BSD3-style (as xmonad)
|
||||
--
|
||||
-- Maintainer : Justin Bogner <mail@justinbogner.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- A layout much like Tall, but using a multiple of a window's minimum
|
||||
-- resize amount instead of a percentage of screen to decide where to
|
||||
-- split. This is useful when you usually leave a text editor or
|
||||
-- terminal in the master pane and like it to be 80 columns wide.
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.FixedColumn (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
FixedColumn(..)
|
||||
) where
|
||||
|
||||
import Control.Monad (msum)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Graphics.X11.Xlib (Window, rect_width)
|
||||
import Graphics.X11.Xlib.Extras ( getWMNormalHints
|
||||
, getWindowAttributes
|
||||
, sh_base_size
|
||||
, sh_resize_inc
|
||||
, wa_border_width)
|
||||
|
||||
import XMonad.Core (X, LayoutClass(..), fromMessage, io, withDisplay)
|
||||
import XMonad.Layout (Resize(..), IncMasterN(..), tile)
|
||||
import XMonad.StackSet as W
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.FixedColumn
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding the FixedColumn layout:
|
||||
--
|
||||
-- > myLayouts = FixedColumn 20 80 10 ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||
|
||||
-- | A tiling mode based on preserving a nice fixed width
|
||||
-- window. Supports 'Shrink', 'Expand' and 'IncMasterN'.
|
||||
data FixedColumn a = FixedColumn !Int -- Number of windows in the master pane
|
||||
!Int -- Number to increment by when resizing
|
||||
!Int -- Default width of master pane
|
||||
!Int -- Column width for normal windows
|
||||
deriving (Read, Show)
|
||||
|
||||
instance LayoutClass FixedColumn Window where
|
||||
doLayout (FixedColumn nmaster _ ncol fallback) r s = do
|
||||
fws <- mapM (widthCols fallback ncol) ws
|
||||
let frac = maximum (take nmaster fws) // rect_width r
|
||||
rs = tile frac r nmaster (length ws)
|
||||
return $ (zip ws rs, Nothing)
|
||||
where ws = W.integrate s
|
||||
x // y = fromIntegral x / fromIntegral y
|
||||
|
||||
pureMessage (FixedColumn nmaster delta ncol fallback) m =
|
||||
msum [fmap resize (fromMessage m)
|
||||
,fmap incmastern (fromMessage m)]
|
||||
where resize Shrink
|
||||
= FixedColumn nmaster delta (max 0 $ ncol - delta) fallback
|
||||
resize Expand
|
||||
= FixedColumn nmaster delta (ncol + delta) fallback
|
||||
incmastern (IncMasterN d)
|
||||
= FixedColumn (max 0 (nmaster+d)) delta ncol fallback
|
||||
|
||||
description _ = "FixedColumn"
|
||||
|
||||
-- | Determine the width of @w@ given that we would like it to be @n@
|
||||
-- columns wide, using @inc@ as a resize increment for windows that
|
||||
-- don't have one
|
||||
widthCols :: Int -> Int -> Window -> X Int
|
||||
widthCols inc n w = withDisplay $ \d -> io $ do
|
||||
sh <- getWMNormalHints d w
|
||||
bw <- fmap (fromIntegral . wa_border_width) $ getWindowAttributes d w
|
||||
let widthHint f = f sh >>= return . fromIntegral . fst
|
||||
oneCol = fromMaybe inc $ widthHint sh_resize_inc
|
||||
base = fromMaybe 0 $ widthHint sh_base_size
|
||||
return $ 2 * bw + base + n * oneCol
|
@@ -17,7 +17,7 @@
|
||||
module XMonad.Layout.Grid (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
Grid(..), arrange
|
||||
Grid(..), arrange, defaultRatio
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
@@ -33,21 +33,31 @@ import XMonad.StackSet
|
||||
-- > myLayouts = Grid ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
--
|
||||
-- You can also specify an aspect ratio for Grid to strive for with the
|
||||
-- GridRatio constructor. For example, if you want Grid to try to make a grid
|
||||
-- four windows wide and three windows tall, you could use
|
||||
--
|
||||
-- > myLayouts = GridRatio (4/3) ||| etc.
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||
|
||||
data Grid a = Grid deriving (Read, Show)
|
||||
data Grid a = Grid | GridRatio Double deriving (Read, Show)
|
||||
|
||||
defaultRatio :: Double
|
||||
defaultRatio = 16/9
|
||||
|
||||
instance LayoutClass Grid a where
|
||||
pureLayout Grid r s = arrange r (integrate s)
|
||||
pureLayout Grid r = pureLayout (GridRatio defaultRatio) r
|
||||
pureLayout (GridRatio d) r = arrange d r . integrate
|
||||
|
||||
arrange :: Rectangle -> [a] -> [(a, Rectangle)]
|
||||
arrange (Rectangle rx ry rw rh) st = zip st rectangles
|
||||
arrange :: Double -> Rectangle -> [a] -> [(a, Rectangle)]
|
||||
arrange aspectRatio (Rectangle rx ry rw rh) st = zip st rectangles
|
||||
where
|
||||
nwins = length st
|
||||
ncols = max 1 . round . sqrt $ fromIntegral nwins * 9 * fromIntegral rw / (16 * fromIntegral rh :: Double)
|
||||
mincs = nwins `div` ncols
|
||||
ncols = max 1 . round . sqrt $ fromIntegral nwins * fromIntegral rw / (fromIntegral rh * aspectRatio)
|
||||
mincs = max 1 $ nwins `div` ncols
|
||||
extrs = nwins - ncols * mincs
|
||||
chop :: Int -> Dimension -> [(Position, Dimension)]
|
||||
chop n m = ((0, m - k * fromIntegral (pred n)) :) . map (flip (,) k) . tail . reverse . take n . tail . iterate (subtract k') $ m'
|
||||
|
166
XMonad/Layout/GridVariants.hs
Normal file
166
XMonad/Layout/GridVariants.hs
Normal file
@@ -0,0 +1,166 @@
|
||||
{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, MultiParamTypeClasses #-}
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.GridVariants
|
||||
-- Copyright : (c) Norbert Zeh
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : nzeh@cs.dal.ca
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Two layouts: one is a variant of the Grid layout that allows the
|
||||
-- desired aspect ratio of windows to be specified. The other is like
|
||||
-- Tall but places a grid with fixed number of rows and columns in the
|
||||
-- master area and uses an aspect-ratio-specified layout for the
|
||||
-- slaves.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.GridVariants ( -- * Usage
|
||||
-- $usage
|
||||
ChangeMasterGeom(..)
|
||||
, Grid(..)
|
||||
, TallGrid(..)
|
||||
) where
|
||||
|
||||
import Control.Monad
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
-- $usage
|
||||
-- This module can be used as follows:
|
||||
--
|
||||
-- > import XMonad.Layout.Master
|
||||
--
|
||||
-- Then add something like this to your layouts:
|
||||
--
|
||||
-- > Grid (16/10)
|
||||
--
|
||||
-- for a 16:10 aspect ratio grid, or
|
||||
--
|
||||
-- > TallGrid 2 3 (2/3) (16/10) (5/100)
|
||||
--
|
||||
-- for a layout with a 2x3 master grid that uses 2/3 of the screen,
|
||||
-- and a 16:10 aspect ratio slave grid. The last parameter is again
|
||||
-- the percentage by which the split between master and slave area
|
||||
-- changes in response to Expand/Shrink messages.
|
||||
--
|
||||
-- To be able to change the geometry of the master grid, add something
|
||||
-- like this to your keybindings:
|
||||
--
|
||||
-- > ((modMask .|. shiftMask, xK_equal), sendMessage $ IncMasterCols 1),
|
||||
-- > ((modMask .|. shiftMask, xK_minus), sendMessage $ IncMasterCols (-1)),
|
||||
-- > ((modMask .|. ctrlMask, xK_equal), sendMessage $ IncMasterRows 1),
|
||||
-- > ((modMask .|. ctrlMask, xK_minus), sendMessage $ IncMasterRows (-1))
|
||||
|
||||
-- | Grid layout. The parameter is the desired x:y aspect ratio of windows
|
||||
data Grid a = Grid !Rational
|
||||
deriving (Read, Show)
|
||||
|
||||
instance LayoutClass Grid a where
|
||||
|
||||
pureLayout (Grid aspect) rect st = zip wins rects
|
||||
where
|
||||
wins = W.integrate st
|
||||
nwins = length wins
|
||||
rects = arrangeAspectGrid rect nwins aspect
|
||||
|
||||
description _ = "Grid"
|
||||
|
||||
-- | TallGrid layout. Parameters are
|
||||
--
|
||||
-- - number of master rows
|
||||
-- - number of master columns
|
||||
-- - portion of screen used for master grid
|
||||
-- - x:y aspect ratio of slave windows
|
||||
-- - increment for resize messages
|
||||
data TallGrid a = TallGrid !Int !Int !Rational !Rational !Rational
|
||||
deriving (Read, Show)
|
||||
|
||||
instance LayoutClass TallGrid a where
|
||||
|
||||
pureLayout (TallGrid mrows mcols mfrac saspect _) rect st = zip wins rects
|
||||
where
|
||||
wins = W.integrate st
|
||||
nwins = length wins
|
||||
rects = arrangeTallGrid rect nwins mrows mcols mfrac saspect
|
||||
|
||||
pureMessage layout msg =
|
||||
msum [ fmap (resizeMaster layout) (fromMessage msg)
|
||||
, fmap (changeMasterGrid layout) (fromMessage msg) ]
|
||||
|
||||
description _ = "TallGrid"
|
||||
|
||||
-- |The geometry change message understood by the master grid
|
||||
data ChangeMasterGeom
|
||||
= IncMasterRows !Int -- ^Change the number of master rows
|
||||
| IncMasterCols !Int -- ^Change the number of master columns
|
||||
deriving Typeable
|
||||
|
||||
instance Message ChangeMasterGeom
|
||||
|
||||
arrangeTallGrid :: Rectangle -> Int -> Int -> Int -> Rational -> Rational -> [Rectangle]
|
||||
arrangeTallGrid rect@(Rectangle rx ry rw rh) nwins mrows mcols mfrac saspect
|
||||
| nwins <= mwins = arrangeMasterGrid rect nwins mcols
|
||||
| mwins == 0 = arrangeAspectGrid rect nwins saspect
|
||||
| otherwise = (arrangeMasterGrid mrect mwins mcols) ++
|
||||
(arrangeAspectGrid srect swins saspect)
|
||||
where
|
||||
mwins = mrows * mcols
|
||||
swins = nwins - mwins
|
||||
mrect = Rectangle rx ry rw mh
|
||||
srect = Rectangle rx (fromIntegral ry + fromIntegral mh) rw sh
|
||||
mh = ceiling (fromIntegral rh * mfrac)
|
||||
sh = rh - mh
|
||||
|
||||
arrangeMasterGrid :: Rectangle -> Int -> Int -> [Rectangle]
|
||||
arrangeMasterGrid rect nwins mcols = arrangeGrid rect nwins (min nwins mcols)
|
||||
|
||||
arrangeAspectGrid :: Rectangle -> Int -> Rational -> [Rectangle]
|
||||
arrangeAspectGrid rect@(Rectangle _ _ rw rh) nwins aspect =
|
||||
arrangeGrid rect nwins (min nwins ncols)
|
||||
where
|
||||
ncols = ceiling $ sqrt $ ( fromRational
|
||||
( (fromIntegral rw * fromIntegral nwins) / (fromIntegral rh * aspect) ) :: Double)
|
||||
|
||||
arrangeGrid :: Rectangle -> Int -> Int -> [Rectangle]
|
||||
arrangeGrid (Rectangle rx ry rw rh) nwins ncols =
|
||||
[Rectangle (fromIntegral x + rx) (fromIntegral y + ry) (fromIntegral w) (fromIntegral h)
|
||||
| (x, y, w, h) <- rects]
|
||||
where
|
||||
nrows_in_cols = listDifference $ splitEvenly nwins ncols
|
||||
x_slabs = splitIntoSlabs (fromIntegral rw) ncols
|
||||
y_slabs = [splitIntoSlabs (fromIntegral rh) nrows | nrows <- nrows_in_cols]
|
||||
rects_in_cols = [[(x, y, w, h) | (y, h) <- lst]
|
||||
| ((x, w), lst) <- zip x_slabs y_slabs]
|
||||
rects = foldr (++) [] rects_in_cols
|
||||
|
||||
splitIntoSlabs :: Int -> Int -> [(Int, Int)]
|
||||
splitIntoSlabs width nslabs = zip (0:xs) widths
|
||||
where
|
||||
xs = splitEvenly width nslabs
|
||||
widths = listDifference xs
|
||||
|
||||
listDifference :: [Int] -> [Int]
|
||||
listDifference lst = [cur-pre | (cur,pre) <- zip lst (0:lst)]
|
||||
|
||||
splitEvenly :: Int -> Int -> [Int]
|
||||
splitEvenly n parts = [ sz-off | (sz,off) <- zip sizes offsets]
|
||||
where
|
||||
size = ceiling ( (fromIntegral n / fromIntegral parts) :: Double )
|
||||
extra = size*parts - n
|
||||
sizes = [i*size | i <- [1..parts]]
|
||||
offsets = (take (fromIntegral extra) [1..]) ++ [extra,extra..]
|
||||
|
||||
resizeMaster :: TallGrid a -> Resize -> TallGrid a
|
||||
resizeMaster (TallGrid mrows mcols mfrac saspect delta) Shrink =
|
||||
TallGrid mrows mcols (max 0 (mfrac - delta)) saspect delta
|
||||
resizeMaster (TallGrid mrows mcols mfrac saspect delta) Expand =
|
||||
TallGrid mrows mcols (min 1 (mfrac + delta)) saspect delta
|
||||
|
||||
changeMasterGrid :: TallGrid a -> ChangeMasterGeom -> TallGrid a
|
||||
changeMasterGrid (TallGrid mrows mcols mfrac saspect delta) (IncMasterRows d) =
|
||||
TallGrid (max 0 (mrows + d)) mcols mfrac saspect delta
|
||||
changeMasterGrid (TallGrid mrows mcols mfrac saspect delta) (IncMasterCols d) =
|
||||
TallGrid mrows (max 0 (mcols + d)) mfrac saspect delta
|
@@ -18,7 +18,7 @@
|
||||
module XMonad.Layout.HintedGrid (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
Grid(..), arrange
|
||||
Grid(..), arrange, defaultRatio
|
||||
) where
|
||||
|
||||
import Prelude hiding ((.))
|
||||
@@ -44,16 +44,25 @@ infixr 9 .
|
||||
-- > myLayouts = Grid False ||| Full ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
--
|
||||
-- You can also specify an aspect ratio for Grid to strive for with the
|
||||
-- GridRatio constructor:
|
||||
--
|
||||
-- > myLayouts = GridRatio (4/3) False ||| etc.
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook".
|
||||
|
||||
-- | Automatic mirroring of hinted layouts doesn't work very well, so this
|
||||
-- 'Grid' comes with built-in mirroring. @Grid False@ is the normal layout,
|
||||
-- @Grid True@ is the mirrored variant (rotated by 90 degrees).
|
||||
data Grid a = Grid Bool deriving (Read, Show)
|
||||
data Grid a = Grid Bool | GridRatio Double Bool deriving (Read, Show)
|
||||
|
||||
defaultRatio :: Double
|
||||
defaultRatio = 16/9
|
||||
|
||||
instance LayoutClass Grid Window where
|
||||
doLayout (Grid m) r w = flip (,) Nothing . arrange m r (integrate w)
|
||||
doLayout (Grid m) r w = doLayout (GridRatio defaultRatio m) r w
|
||||
doLayout (GridRatio d m) r w = flip (,) Nothing . arrange d m r (integrate w)
|
||||
|
||||
replicateS :: Int -> (a -> (b, a)) -> a -> ([b], a)
|
||||
replicateS n = runState . replicateM n . State
|
||||
@@ -92,12 +101,12 @@ doRect height = doR
|
||||
zipWith3 (\x y (w, h) -> Rectangle (fromIntegral x) (fromIntegral y) w h) xs ys c' ++ doR width' (n - 1) cs
|
||||
|
||||
-- | The internal function for computing the grid layout.
|
||||
arrange :: Bool -> Rectangle -> [Window] -> X [(Window, Rectangle)]
|
||||
arrange mirror (Rectangle rx ry rw rh) wins = do
|
||||
arrange :: Double -> Bool -> Rectangle -> [Window] -> X [(Window, Rectangle)]
|
||||
arrange aspectRatio mirror (Rectangle rx ry rw rh) wins = do
|
||||
proto <- mapM mkAdjust wins
|
||||
let
|
||||
adjs = map (\f -> twist . f . twist) proto
|
||||
rs = arrange' (twist (rw, rh)) adjs
|
||||
rs = arrange' aspectRatio (twist (rw, rh)) adjs
|
||||
rs' = map (\(Rectangle x y w h) -> uncurry (uncurry Rectangle (twist (x, y))) (twist (w, h))) rs
|
||||
return . zip wins . map (\r -> r{ rect_x = rect_x r + rx, rect_y = rect_y r + ry }) $ rs'
|
||||
where
|
||||
@@ -105,11 +114,11 @@ arrange mirror (Rectangle rx ry rw rh) wins = do
|
||||
| mirror = \(a, b) -> (b, a)
|
||||
| otherwise = id
|
||||
|
||||
arrange' :: D -> [D -> D] -> [Rectangle]
|
||||
arrange' (rw, rh) adjs = reverse $ doRect rh rw (fromIntegral ncolumns) (ecols ++ cols)
|
||||
arrange' :: Double -> D -> [D -> D] -> [Rectangle]
|
||||
arrange' aspectRatio (rw, rh) adjs = reverse $ doRect rh rw (fromIntegral ncolumns) (ecols ++ cols)
|
||||
where
|
||||
nwindows = length adjs
|
||||
ncolumns = max 1 . round . sqrt $ fromIntegral nwindows * 9 * fromIntegral rw / (16 * fromIntegral rh :: Double)
|
||||
ncolumns = max 1 . round . sqrt $ fromIntegral nwindows * fromIntegral rw / (fromIntegral rh * aspectRatio)
|
||||
nrows = nwindows `div` ncolumns
|
||||
nextras = nwindows - ncolumns * nrows
|
||||
(ecols, adjs') = replicateS nextras (splitAt (nrows + 1)) $ reverse adjs
|
||||
|
@@ -50,11 +50,12 @@ import Control.Monad
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||
|
||||
data HintedTile a = HintedTile
|
||||
{ nmaster :: !Int
|
||||
, delta, frac :: !Rational
|
||||
{ nmaster :: !Int -- ^ number of windows in the master pane
|
||||
, delta :: !Rational -- ^ how much to change when resizing
|
||||
, frac :: !Rational -- ^ ratio between master/nonmaster panes
|
||||
, alignment :: !Alignment -- ^ Where to place windows that are smaller
|
||||
-- than their preordained rectangles.
|
||||
, orientation :: !Orientation
|
||||
, orientation :: !Orientation -- ^ Tall or Wide (mirrored) layout?
|
||||
} deriving ( Show, Read )
|
||||
|
||||
data Orientation
|
||||
|
@@ -122,6 +122,6 @@ instance LayoutClass IM Window where
|
||||
let (masterRect, slaveRect) = splitHorizontallyBy r rect
|
||||
master <- findM (hasProperty prop) ws
|
||||
let positions = case master of
|
||||
Just w -> (w, masterRect) : arrange slaveRect (filter (w /=) ws)
|
||||
Nothing -> arrange rect ws
|
||||
Just w -> (w, masterRect) : arrange defaultRatio slaveRect (filter (w /=) ws)
|
||||
Nothing -> arrange defaultRatio rect ws
|
||||
return (positions, Nothing)
|
||||
|
@@ -46,7 +46,8 @@ data LayoutHints a = LayoutHints deriving (Read, Show)
|
||||
|
||||
instance LayoutModifier LayoutHints Window where
|
||||
modifierDescription _ = "Hinted"
|
||||
redoLayout _ _ s xs = do
|
||||
redoLayout _ _ Nothing xs = return (xs, Nothing)
|
||||
redoLayout _ _ (Just s) xs = do
|
||||
xs' <- mapM applyHint xs
|
||||
return (xs', Nothing)
|
||||
where
|
||||
|
@@ -164,18 +164,17 @@ class (Show (m a), Read (m a)) => LayoutModifier m a where
|
||||
-- consider implementing 'hook' and 'pureModifier' instead of
|
||||
-- 'redoLayout'.
|
||||
--
|
||||
-- If you also need to perform some action when 'runLayout' is
|
||||
-- called on an empty workspace, see 'emptyLayoutMod'.
|
||||
-- On empty workspaces, the Stack is Nothing.
|
||||
--
|
||||
-- The default implementation of 'redoLayout' calls 'hook' and
|
||||
-- then 'pureModifier'.
|
||||
redoLayout :: m a -- ^ the layout modifier
|
||||
-> Rectangle -- ^ screen rectangle
|
||||
-> Stack a -- ^ current window stack
|
||||
redoLayout :: m a -- ^ the layout modifier
|
||||
-> Rectangle -- ^ screen rectangle
|
||||
-> Maybe (Stack a) -- ^ current window stack
|
||||
-> [(a, Rectangle)] -- ^ (window,rectangle) pairs returned
|
||||
-- by the underlying layout
|
||||
-> X ([(a, Rectangle)], Maybe (m a))
|
||||
redoLayout m r s wrs = do hook m; return $ pureModifier m r s wrs
|
||||
redoLayout m r ms wrs = do hook m; return $ pureModifier m r ms wrs
|
||||
|
||||
-- | 'pureModifier' allows you to intercept a call to 'runLayout'
|
||||
-- /after/ it is called on the underlying layout, in order to
|
||||
@@ -184,33 +183,14 @@ class (Show (m a), Read (m a)) => LayoutModifier m a where
|
||||
--
|
||||
-- The default implementation of 'pureModifier' returns the
|
||||
-- window rectangles unmodified.
|
||||
pureModifier :: m a -- ^ the layout modifier
|
||||
-> Rectangle -- ^ screen rectangle
|
||||
-> Stack a -- ^ current window stack
|
||||
pureModifier :: m a -- ^ the layout modifier
|
||||
-> Rectangle -- ^ screen rectangle
|
||||
-> Maybe (Stack a) -- ^ current window stack
|
||||
-> [(a, Rectangle)] -- ^ (window, rectangle) pairs returned
|
||||
-- by the underlying layout
|
||||
-> ([(a, Rectangle)], Maybe (m a))
|
||||
pureModifier _ _ _ wrs = (wrs, Nothing)
|
||||
|
||||
-- | 'emptyLayoutMod' allows you to intercept a call to
|
||||
-- 'runLayout' on an empty workspace, /after/ it is called on
|
||||
-- the underlying layout, in order to perform some effect in the
|
||||
-- X monad, possibly return a new layout modifier, and\/or
|
||||
-- modify the results of 'runLayout' before returning them.
|
||||
--
|
||||
-- If you don't need access to the X monad, then tough luck.
|
||||
-- There isn't a pure version of 'emptyLayoutMod'.
|
||||
--
|
||||
-- The default implementation of 'emptyLayoutMod' ignores its
|
||||
-- arguments and returns an empty list of window\/rectangle
|
||||
-- pairings.
|
||||
--
|
||||
-- /NOTE/: 'emptyLayoutMod' will likely be combined with
|
||||
-- 'redoLayout' soon!
|
||||
emptyLayoutMod :: m a -> Rectangle -> [(a, Rectangle)]
|
||||
-> X ([(a, Rectangle)], Maybe (m a))
|
||||
emptyLayoutMod _ _ _ = return ([], Nothing)
|
||||
|
||||
-- | 'hook' is called by the default implementation of
|
||||
-- 'redoLayout', and as such represents an X action which is to
|
||||
-- be run each time 'runLayout' is called on the underlying
|
||||
@@ -256,9 +236,7 @@ class (Show (m a), Read (m a)) => LayoutModifier m a where
|
||||
instance (LayoutModifier m a, LayoutClass l a) => LayoutClass (ModifiedLayout m l) a where
|
||||
runLayout (Workspace i (ModifiedLayout m l) ms) r =
|
||||
do (ws, ml') <- modifyLayout m (Workspace i l ms) r
|
||||
(ws', mm') <- case ms of
|
||||
Just s -> redoLayout m r s ws
|
||||
Nothing -> emptyLayoutMod m r ws
|
||||
(ws', mm') <- redoLayout m r ms ws
|
||||
let ml'' = case mm' of
|
||||
Just m' -> Just $ (ModifiedLayout m') $ maybe l id ml'
|
||||
Nothing -> ModifiedLayout m `fmap` ml'
|
||||
|
@@ -114,14 +114,13 @@ data Toggle = On | Off deriving (Read, Show)
|
||||
data MagnifyMaster = All | NoMaster deriving (Read, Show)
|
||||
|
||||
instance LayoutModifier Magnifier Window where
|
||||
redoLayout (Mag z On All ) = applyMagnifier z
|
||||
redoLayout (Mag z On NoMaster) = unlessMaster $ applyMagnifier z
|
||||
redoLayout _ = nothing
|
||||
where nothing _ _ wrs = return (wrs, Nothing)
|
||||
redoLayout (Mag z On All ) r (Just s) wrs = applyMagnifier z r s wrs
|
||||
redoLayout (Mag z On NoMaster) r (Just s) wrs = unlessMaster (applyMagnifier z) r s wrs
|
||||
redoLayout _ _ _ wrs = return (wrs, Nothing)
|
||||
|
||||
handleMess (Mag z On t) m
|
||||
| Just MagnifyMore <- fromMessage m = return . Just $ (Mag (z `addto` 0.1) On t)
|
||||
| Just MagnifyLess <- fromMessage m = return . Just $ (Mag (z `addto` 0.1) On t)
|
||||
| Just MagnifyMore <- fromMessage m = return . Just $ (Mag (z `addto` 0.1 ) On t)
|
||||
| Just MagnifyLess <- fromMessage m = return . Just $ (Mag (z `addto` (-0.1)) On t)
|
||||
| Just ToggleOff <- fromMessage m = return . Just $ (Mag (z ) Off t)
|
||||
| Just Toggle <- fromMessage m = return . Just $ (Mag (z ) Off t)
|
||||
where addto (x,y) i = (x+i,y+i)
|
||||
|
172
XMonad/Layout/Monitor.hs
Normal file
172
XMonad/Layout/Monitor.hs
Normal file
@@ -0,0 +1,172 @@
|
||||
{-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses, DeriveDataTypeable, PatternGuards #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.Monitor
|
||||
-- Copyright : (c) Roman Cheplyaka
|
||||
-- License : BSD-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : Roman Cheplyaka <roma@ro-che.info>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- Layout modfier for displaying some window (monitor) above other windows
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
module XMonad.Layout.Monitor (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
|
||||
-- * Hints and issues
|
||||
-- $hints
|
||||
|
||||
Monitor(..),
|
||||
monitor,
|
||||
Property(..),
|
||||
MonitorMessage(..),
|
||||
doHideIgnore,
|
||||
manageMonitor
|
||||
|
||||
-- * TODO
|
||||
-- $todo
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import XMonad.Layout.LayoutModifier
|
||||
import XMonad.Util.WindowProperties
|
||||
import XMonad.Hooks.ManageHelpers (doHideIgnore)
|
||||
import XMonad.Hooks.FadeInactive (setOpacity)
|
||||
import Control.Monad
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.Monitor
|
||||
--
|
||||
-- Define 'Monitor' record. 'monitor' can be used as a template. At least 'prop'
|
||||
-- and 'rect' should be set here. Also consider setting 'persistent' to True.
|
||||
--
|
||||
-- Minimal example:
|
||||
--
|
||||
-- > myMonitor = monitor
|
||||
-- > { prop = ClassName "SomeClass"
|
||||
-- > , rect = Rectangle 0 0 40 20 -- rectangle 40x20 in upper left corner
|
||||
-- > }
|
||||
--
|
||||
-- More interesting example:
|
||||
--
|
||||
-- > clock = monitor {
|
||||
-- > -- Cairo-clock creates 2 windows with the same classname, thus also using title
|
||||
-- > prop = ClassName "Cairo-clock" `And` Title "MacSlow's Cairo-Clock"
|
||||
-- > -- rectangle 150x150 in lower right corner, assuming 1280x800 resolution
|
||||
-- > , rect = Rectangle (1280-150) (800-150) 150 150
|
||||
-- > -- avoid flickering
|
||||
-- > , persistent = True
|
||||
-- > -- make the window transparent
|
||||
-- > , opacity = 0xAAAAAAAA
|
||||
-- > -- hide on start
|
||||
-- > , visible = False
|
||||
-- > -- assign it a name to be able to toggle it independently of others
|
||||
-- > , name = "clock"
|
||||
-- > }
|
||||
--
|
||||
-- Add ManageHook to de-manage monitor windows and apply opacity settings.
|
||||
--
|
||||
-- > manageHook = myManageHook <+> manageMonitor clock
|
||||
--
|
||||
-- Apply layout modifier.
|
||||
--
|
||||
-- > myLayouts = ModifiedLayout clock $ tall ||| Full ||| ...
|
||||
--
|
||||
-- After that, if there exists a window with specified properties, it will be
|
||||
-- displayed on top of all /tiled/ (not floated) windows on specified
|
||||
-- position.
|
||||
--
|
||||
-- It's also useful to add some keybinding to toggle monitor visibility:
|
||||
--
|
||||
-- > , ((mod1Mask, xK_u ), broadcastMessage ToggleMonitor >> refresh)
|
||||
--
|
||||
-- Screenshot: <http://www.haskell.org/haskellwiki/Image:Xmonad-clock.png>
|
||||
|
||||
data Monitor a = Monitor
|
||||
{ prop :: Property -- ^ property which uniquely identifies monitor window
|
||||
, rect :: Rectangle -- ^ specifies where to put monitor
|
||||
, visible :: Bool -- ^ is it visible by default?
|
||||
, name :: String -- ^ name of monitor (useful when we have many of them)
|
||||
, persistent :: Bool -- ^ is it shown on all layouts?
|
||||
, opacity :: Integer -- ^ opacity level
|
||||
} deriving (Read, Show)
|
||||
|
||||
-- | Template for 'Monitor' record. At least 'prop' and 'rect' should be
|
||||
-- redefined. Default settings: 'visible' is 'True', 'persistent' is 'False'.
|
||||
monitor :: Monitor a
|
||||
monitor = Monitor
|
||||
{ prop = Const False
|
||||
, rect = Rectangle 0 0 0 0
|
||||
, visible = True
|
||||
, name = ""
|
||||
, persistent = False
|
||||
, opacity = 0xFFFFFFFF
|
||||
}
|
||||
|
||||
-- | Messages without names affect all monitors. Messages with names affect only
|
||||
-- monitors whose names match.
|
||||
data MonitorMessage = ToggleMonitor | ShowMonitor | HideMonitor
|
||||
| ToggleMonitorNamed String
|
||||
| ShowMonitorNamed String
|
||||
| HideMonitorNamed String
|
||||
deriving (Read,Show,Eq,Typeable)
|
||||
instance Message MonitorMessage
|
||||
|
||||
withMonitor :: Property -> a -> (Window -> X a) -> X a
|
||||
withMonitor p a fn = do
|
||||
monitorWindows <- allWithProperty p
|
||||
case monitorWindows of
|
||||
[] -> return a
|
||||
w:_ -> fn w
|
||||
|
||||
instance LayoutModifier Monitor Window where
|
||||
redoLayout mon _ _ rects = withMonitor (prop mon) (rects, Nothing) $ \w ->
|
||||
if visible mon
|
||||
then do tileWindow w (rect mon)
|
||||
reveal w
|
||||
return ((w,rect mon):rects, Nothing)
|
||||
else do hide w
|
||||
return (rects, Nothing)
|
||||
handleMess mon mess
|
||||
| Just ToggleMonitor <- fromMessage mess = return $ Just $ mon { visible = not $ visible mon }
|
||||
| Just (ToggleMonitorNamed n) <- fromMessage mess = return $
|
||||
if name mon == n then Just $ mon { visible = not $ visible mon } else Nothing
|
||||
| Just ShowMonitor <- fromMessage mess = return $ Just $ mon { visible = True }
|
||||
| Just (ShowMonitorNamed n) <- fromMessage mess = return $
|
||||
if name mon == n then Just $ mon { visible = True } else Nothing
|
||||
| Just HideMonitor <- fromMessage mess = return $ Just $ mon { visible = False }
|
||||
| Just (HideMonitorNamed n) <- fromMessage mess = return $
|
||||
if name mon == n then Just $ mon { visible = False } else Nothing
|
||||
| Just Hide <- fromMessage mess = do unless (persistent mon) $ withMonitor (prop mon) () hide; return Nothing
|
||||
| otherwise = return Nothing
|
||||
|
||||
-- | ManageHook which demanages monitor window and applies opacity settings.
|
||||
manageMonitor :: Monitor a -> ManageHook
|
||||
manageMonitor mon = propertyToQuery (prop mon) --> do
|
||||
w <- ask
|
||||
liftX $ setOpacity w $ opacity mon
|
||||
if persistent mon then doIgnore else doHideIgnore
|
||||
|
||||
-- $hints
|
||||
-- - This module assumes that there is only one window satisfying property exists.
|
||||
--
|
||||
-- - If your monitor is available on /all/ layouts, set
|
||||
-- 'persistent' to 'True' to avoid unnecessary
|
||||
-- flickering. You can still toggle monitor with a keybinding.
|
||||
--
|
||||
-- - You can use several monitors with nested modifiers. Give them names
|
||||
--- to be able to toggle them independently.
|
||||
--
|
||||
-- - You can display monitor only on specific workspaces with
|
||||
-- "XMonad.Layout.PerWorkspace".
|
||||
|
||||
-- $todo
|
||||
-- - make Monitor remember the window it manages
|
||||
--
|
||||
-- - specify position relative to the screen
|
@@ -75,9 +75,9 @@ data SmartBorder a = SmartBorder [a] deriving (Read, Show)
|
||||
instance LayoutModifier SmartBorder Window where
|
||||
unhook (SmartBorder s) = asks (borderWidth . config) >>= setBorders s
|
||||
|
||||
redoLayout (SmartBorder s) _ st wrs = do
|
||||
redoLayout (SmartBorder s) _ mst wrs = do
|
||||
wset <- gets windowset
|
||||
let managedwindows = W.integrate st
|
||||
let managedwindows = W.integrate' mst
|
||||
screens = filter (nonzerorect . screenRect . W.screenDetail) . W.screens $ wset
|
||||
ws = tiled ++ floating
|
||||
tiled = case filter (`elem` managedwindows) $ map fst wrs of
|
||||
|
@@ -70,8 +70,6 @@ defaultSWNConfig =
|
||||
instance LayoutModifier ShowWName a where
|
||||
redoLayout sn r _ wrs = doShow sn r wrs
|
||||
|
||||
emptyLayoutMod sn r wrs = doShow sn r wrs
|
||||
|
||||
handleMess (SWN _ c (Just (i,w))) m
|
||||
| Just e <- fromMessage m = handleTimer i e (deleteWindow w >> return Nothing)
|
||||
| Just Hide <- fromMessage m = do deleteWindow w
|
||||
@@ -89,7 +87,7 @@ doShow (SWN False _ _ ) _ wrs = return (wrs, Nothing)
|
||||
flashName :: SWNConfig -> Rectangle -> [(a, Rectangle)] -> X ([(a, Rectangle)], Maybe (ShowWName a))
|
||||
flashName c (Rectangle _ _ wh ht) wrs = do
|
||||
d <- asks display
|
||||
n <- withWindowSet (return . S.tag . S.workspace . S.current)
|
||||
n <- withWindowSet (return . S.currentTag)
|
||||
f <- initXMF (swn_font c)
|
||||
width <- textWidthXMF d f n
|
||||
(as,ds) <- textExtentsXMF f n
|
||||
|
92
XMonad/Layout/ThreeColumnsMiddle.hs
Normal file
92
XMonad/Layout/ThreeColumnsMiddle.hs
Normal file
@@ -0,0 +1,92 @@
|
||||
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- |
|
||||
-- Module : XMonad.Layout.ThreeColumnsMiddle
|
||||
-- Copyright : (c) Carsten Otto <xmonad@c-otto.de>,
|
||||
-- based on ThreeColumns (c) Kai Grossjohann <kai@emptydomain.de>
|
||||
-- License : BSD3-style (see LICENSE)
|
||||
--
|
||||
-- Maintainer : ?
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
-- A layout similar to tall but with three columns, where the main window is
|
||||
-- in the middle. With 2560x1600 pixels this layout can be used for a huge
|
||||
-- main window and up to six reasonable sized slave windows.
|
||||
--
|
||||
-- > Screenshot: http://server.c-otto.de/xmonad/ThreeColumnsMiddle.png
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Layout.ThreeColumnsMiddle (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
ThreeColMid(..)
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
|
||||
import Data.Ratio
|
||||
|
||||
import Control.Monad
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
--
|
||||
-- > import XMonad.Layout.ThreeColumnsMiddle
|
||||
--
|
||||
-- Then edit your @layoutHook@ by adding the ThreeColMid layout:
|
||||
--
|
||||
-- > myLayouts = ThreeColMid 1 (3/100) (1/2) ||| etc..
|
||||
-- > main = xmonad defaultConfig { layoutHook = myLayouts }
|
||||
--
|
||||
-- The first argument specifies how many windows appear in the main window.
|
||||
-- The second argument specifies how much the main window size changes when resizing.
|
||||
-- The third argument specifies the initial size of the main window as a fraction of
|
||||
-- total screen size.
|
||||
--
|
||||
-- For more detailed instructions on editing the layoutHook see:
|
||||
--
|
||||
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||
|
||||
data ThreeColMid a = ThreeColMid !Int !Rational !Rational deriving (Show,Read)
|
||||
|
||||
instance LayoutClass ThreeColMid a where
|
||||
doLayout (ThreeColMid nmaster _ frac) r =
|
||||
return . (\x->(x,Nothing)) .
|
||||
ap zip (tile3 frac r nmaster . length) . W.integrate
|
||||
handleMessage (ThreeColMid nmaster delta frac) m =
|
||||
return $ msum [fmap resize (fromMessage m)
|
||||
,fmap incmastern (fromMessage m)]
|
||||
where resize Shrink = ThreeColMid nmaster delta (max 0 $ frac-delta)
|
||||
resize Expand = ThreeColMid nmaster delta (min 1 $ frac+delta)
|
||||
incmastern (IncMasterN d) = ThreeColMid (max 0 (nmaster+d)) delta frac
|
||||
description _ = "ThreeColMid"
|
||||
|
||||
-- | tile3. Compute window positions using 3 panes
|
||||
tile3 :: Rational -> Rectangle -> Int -> Int -> [Rectangle]
|
||||
tile3 f r nmaster n
|
||||
-- split horizontally, if there are very few windows (only the main screen is used)
|
||||
| n <= nmaster || nmaster == 0 = splitHorizontally n r
|
||||
|
||||
-- one window more than the master window can hold (the additional window is shown right of the main screen)
|
||||
| n == nmaster+1 = splitVertically nmaster s1 ++ splitVertically (n-nmaster) s2
|
||||
|
||||
-- many windows (the main windows are shown in the center, all other windows are shown left and right of it)
|
||||
| otherwise = splitVertically nmaster r1 ++ splitVertically nleft r2 ++ splitVertically nright r3
|
||||
where (r1, r2, r3) = split3HorizontallyBy f r
|
||||
(s1, s2) = splitHorizontallyBy f r
|
||||
nslave = (n - nmaster)
|
||||
nleft = ceiling (nslave % 2)
|
||||
nright = (n - nmaster - nleft)
|
||||
|
||||
split3HorizontallyBy :: Rational -> Rectangle -> (Rectangle, Rectangle, Rectangle)
|
||||
split3HorizontallyBy f (Rectangle sx sy sw sh) =
|
||||
( Rectangle (sx + fromIntegral leftw) sy midw sh
|
||||
, Rectangle sx sy leftw sh
|
||||
, Rectangle (sx + fromIntegral leftw + fromIntegral midw) sy rightw sh )
|
||||
where midw = ceiling $ fromIntegral sw * f
|
||||
leftw = ceiling ( (sw - midw) % 2 )
|
||||
rightw = sw - leftw - midw
|
@@ -109,9 +109,9 @@ type ArrangeAll = Bool
|
||||
data WindowArranger a = WA Bool ArrangeAll [ArrangedWindow a] deriving (Read, Show)
|
||||
|
||||
instance (Show a, Read a, Eq a) => LayoutModifier WindowArranger a where
|
||||
pureModifier (WA True b []) _ _ wrs = arrangeWindows b wrs
|
||||
pureModifier (WA True b []) _ (Just _) wrs = arrangeWindows b wrs
|
||||
|
||||
pureModifier (WA True b awrs) _ (S.Stack w _ _) wrs = curry process wrs awrs
|
||||
pureModifier (WA True b awrs) _ (Just (S.Stack w _ _)) wrs = curry process wrs awrs
|
||||
where
|
||||
wins = map fst *** map awrWin
|
||||
update (a,r) = mkNewAWRs b a *** removeAWRs r >>> uncurry (++)
|
||||
|
@@ -106,7 +106,7 @@ configurableNavigation :: LayoutClass l a => WNConfig -> l a -> ModifiedLayout W
|
||||
configurableNavigation conf = ModifiedLayout (WindowNavigation conf (I Nothing))
|
||||
|
||||
instance LayoutModifier WindowNavigation Window where
|
||||
redoLayout (WindowNavigation conf (I state)) rscr s origwrs =
|
||||
redoLayout (WindowNavigation conf (I state)) rscr (Just s) origwrs =
|
||||
do XConf { normalBorder = nbc, focusedBorder = fbc, display = dpy } <- ask
|
||||
[uc,dc,lc,rc] <-
|
||||
case brightness conf of
|
||||
@@ -136,6 +136,7 @@ instance LayoutModifier WindowNavigation Window where
|
||||
mapM_ (sc nbc) (wothers \\ map fst wnavigable)
|
||||
mapM_ (\(win,c) -> sc c win) wnavigablec
|
||||
return (origwrs, Just $ WindowNavigation conf $ I $ Just $ NS pt wnavigable)
|
||||
redoLayout _ _ _ origwrs = return (origwrs, Nothing)
|
||||
|
||||
handleMessOrMaybeModifyIt (WindowNavigation conf (I (Just (NS pt wrs)))) m
|
||||
| Just (Go d) <- fromMessage m =
|
||||
|
@@ -37,7 +37,7 @@ import XMonad.Util.Run ( runProcessWithInput )
|
||||
import XMonad.Prompt ( XPConfig )
|
||||
import XMonad.Prompt.Directory ( directoryPrompt )
|
||||
import XMonad.Layout.LayoutModifier
|
||||
import XMonad.StackSet ( tag, current, workspace )
|
||||
import XMonad.StackSet ( tag, currentTag )
|
||||
|
||||
-- $usage
|
||||
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||||
@@ -69,7 +69,7 @@ instance Message Chdir
|
||||
data WorkspaceDir a = WorkspaceDir String deriving ( Read, Show )
|
||||
|
||||
instance LayoutModifier WorkspaceDir Window where
|
||||
modifyLayout (WorkspaceDir d) w r = do tc <- gets (tag.workspace.current.windowset)
|
||||
modifyLayout (WorkspaceDir d) w r = do tc <- gets (currentTag.windowset)
|
||||
when (tc == tag w) $ scd d
|
||||
runLayout w r
|
||||
handleMess (WorkspaceDir _) m
|
||||
|
202
XMonad/Prompt.hs
202
XMonad/Prompt.hs
@@ -5,7 +5,7 @@
|
||||
-- Copyright : (C) 2007 Andrea Rossato
|
||||
-- License : BSD3
|
||||
--
|
||||
-- Maintainer : andrea.rossato@unibz.it
|
||||
-- Maintainer : Spencer Janssen <spencerjanssen@gmail.com>
|
||||
-- Stability : unstable
|
||||
-- Portability : unportable
|
||||
--
|
||||
@@ -18,7 +18,9 @@ module XMonad.Prompt
|
||||
-- $usage
|
||||
mkXPrompt
|
||||
, mkXPromptWithReturn
|
||||
, amberXPConfig
|
||||
, defaultXPConfig
|
||||
, greenXPConfig
|
||||
, XPType (..)
|
||||
, XPPosition (..)
|
||||
, XPConfig (..)
|
||||
@@ -44,8 +46,13 @@ module XMonad.Prompt
|
||||
, decodeInput
|
||||
, encodeOutput
|
||||
, historyCompletion
|
||||
-- * History filters
|
||||
, deleteAllDuplicates
|
||||
, deleteConsecutive
|
||||
) where
|
||||
|
||||
import Prelude hiding (catch)
|
||||
|
||||
import XMonad hiding (config, io)
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad.Util.Font
|
||||
@@ -61,9 +68,13 @@ import Data.Bits ((.&.))
|
||||
import Data.Maybe
|
||||
import Data.List
|
||||
import Data.Set (fromList, toList)
|
||||
import System.Environment (getEnv)
|
||||
import System.Directory
|
||||
import System.IO
|
||||
import System.Posix.Files
|
||||
import Control.Exception hiding (handle)
|
||||
|
||||
import qualified Data.Map as Map
|
||||
import Data.Map (Map)
|
||||
|
||||
-- $usage
|
||||
-- For usage examples see "XMonad.Prompt.Shell",
|
||||
@@ -83,13 +94,14 @@ data XPState =
|
||||
, complWin :: Maybe Window
|
||||
, complWinDim :: Maybe ComplWindowDim
|
||||
, completionFunction :: String -> IO [String]
|
||||
, showComplWin :: Bool
|
||||
, gcon :: !GC
|
||||
, fontS :: !XMonadFont
|
||||
, xptype :: !XPType
|
||||
, command :: String
|
||||
, commandHistory :: W.Stack String
|
||||
, offset :: !Int
|
||||
, history :: [History]
|
||||
, config :: XPConfig
|
||||
, successful :: Bool
|
||||
}
|
||||
|
||||
data XPConfig =
|
||||
@@ -103,10 +115,14 @@ data XPConfig =
|
||||
, position :: XPPosition -- ^ Position: 'Top' or 'Bottom'
|
||||
, height :: !Dimension -- ^ Window height
|
||||
, historySize :: !Int -- ^ The number of history entries to be saved
|
||||
, historyFilter :: [String] -> [String]
|
||||
-- ^ a filter to determine which
|
||||
-- history entries to remember
|
||||
, defaultText :: String -- ^ The text by default in the prompt line
|
||||
, autoComplete :: Maybe Int -- ^ Just x: if only one completion remains, auto-select it,
|
||||
, showCompletionOnTab :: Bool -- ^ Only show list of completions when Tab was pressed
|
||||
-- and delay by x microseconds
|
||||
} deriving (Show, Read)
|
||||
}
|
||||
|
||||
data XPType = forall p . XPrompt p => XPT p
|
||||
|
||||
@@ -161,26 +177,29 @@ data XPPosition = Top
|
||||
| Bottom
|
||||
deriving (Show,Read)
|
||||
|
||||
defaultXPConfig :: XPConfig
|
||||
amberXPConfig, defaultXPConfig, greenXPConfig :: XPConfig
|
||||
defaultXPConfig =
|
||||
XPC { font = "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"
|
||||
, bgColor = "#333333"
|
||||
, fgColor = "#FFFFFF"
|
||||
, fgHLight = "#000000"
|
||||
, bgHLight = "#BBBBBB"
|
||||
, borderColor = "#FFFFFF"
|
||||
XPC { font = "-misc-fixed-*-*-*-*-12-*-*-*-*-*-*-*"
|
||||
, bgColor = "grey22"
|
||||
, fgColor = "grey80"
|
||||
, fgHLight = "black"
|
||||
, bgHLight = "grey"
|
||||
, borderColor = "white"
|
||||
, promptBorderWidth = 1
|
||||
, position = Bottom
|
||||
, height = 18
|
||||
, historySize = 256
|
||||
, historyFilter = id
|
||||
, defaultText = []
|
||||
, autoComplete = Nothing
|
||||
}
|
||||
, showCompletionOnTab = False }
|
||||
greenXPConfig = defaultXPConfig { fgColor = "green", bgColor = "black" }
|
||||
amberXPConfig = defaultXPConfig { fgColor = "#ca8f2d", bgColor = "black", fgHLight = "#eaaf4c" }
|
||||
|
||||
type ComplFunction = String -> IO [String]
|
||||
|
||||
initState :: XPrompt p => Display -> Window -> Window -> Rectangle -> ComplFunction
|
||||
-> GC -> XMonadFont -> p -> [History] -> XPConfig -> XPState
|
||||
-> GC -> XMonadFont -> p -> [String] -> XPConfig -> XPState
|
||||
initState d rw w s compl gc fonts pt h c =
|
||||
XPS { dpy = d
|
||||
, rootw = rw
|
||||
@@ -189,15 +208,25 @@ initState d rw w s compl gc fonts pt h c =
|
||||
, complWin = Nothing
|
||||
, complWinDim = Nothing
|
||||
, completionFunction = compl
|
||||
, showComplWin = not (showCompletionOnTab c)
|
||||
, gcon = gc
|
||||
, fontS = fonts
|
||||
, xptype = XPT pt
|
||||
, command = defaultText c
|
||||
, commandHistory = W.Stack { W.focus = defaultText c
|
||||
, W.up = []
|
||||
, W.down = h }
|
||||
, offset = length (defaultText c)
|
||||
, history = h
|
||||
, config = c
|
||||
, successful = False
|
||||
}
|
||||
|
||||
-- this would be much easier with functional references
|
||||
command :: XPState -> String
|
||||
command = W.focus . commandHistory
|
||||
|
||||
setCommand :: String -> XPState -> XPState
|
||||
setCommand xs s = s { commandHistory = (commandHistory s) { W.focus = xs }}
|
||||
|
||||
-- | Same as 'mkXPrompt', except that the action function can have
|
||||
-- type @String -> X a@, for any @a@, and the final action returned
|
||||
-- by 'mkXPromptWithReturn' will have type @X (Maybe a)@. @Nothing@
|
||||
@@ -210,25 +239,26 @@ mkXPromptWithReturn t conf compl action = do
|
||||
let d = display c
|
||||
rw = theRoot c
|
||||
s <- gets $ screenRect . W.screenDetail . W.current . windowset
|
||||
hist <- liftIO $ readHistory
|
||||
w <- liftIO $ createWin d rw conf s
|
||||
liftIO $ selectInput d w $ exposureMask .|. keyPressMask
|
||||
gc <- liftIO $ createGC d w
|
||||
liftIO $ setGraphicsExposures d gc False
|
||||
(hist,h) <- liftIO $ readHistory
|
||||
fs <- initXMF (font conf)
|
||||
let st = initState d rw w s compl gc fs (XPT t) hist conf
|
||||
let hs = fromMaybe [] $ Map.lookup (showXPrompt t) hist
|
||||
st = initState d rw w s compl gc fs (XPT t) hs conf
|
||||
st' <- liftIO $ execStateT runXP st
|
||||
|
||||
releaseXMF fs
|
||||
liftIO $ freeGC d gc
|
||||
liftIO $ hClose h
|
||||
if (command st' /= "")
|
||||
if successful st'
|
||||
then do
|
||||
let htw = take (historySize conf) (history st')
|
||||
liftIO $ writeHistory htw
|
||||
liftIO $ writeHistory $ Map.insertWith
|
||||
(\xs ys -> take (historySize conf)
|
||||
. historyFilter conf $ xs ++ ys)
|
||||
(showXPrompt t) [command st'] hist
|
||||
Just <$> action (command st')
|
||||
else
|
||||
return Nothing
|
||||
else return Nothing
|
||||
|
||||
-- | Creates a prompt given:
|
||||
--
|
||||
@@ -277,6 +307,7 @@ handle :: KeyStroke -> Event -> XP ()
|
||||
handle k@(ks,_) e@(KeyEvent {ev_event_type = t})
|
||||
| t == keyPress && ks == xK_Tab = do
|
||||
c <- getCompletions
|
||||
if length c > 1 then modify $ \s -> s { showComplWin = True } else return ()
|
||||
completionHandle c k e
|
||||
handle ks (KeyEvent {ev_event_type = t, ev_state = m})
|
||||
| t == keyPress = keyPressHandle m ks
|
||||
@@ -292,7 +323,7 @@ completionHandle c (ks,_) (KeyEvent {ev_event_type = t})
|
||||
| t == keyPress && ks == xK_Tab = do
|
||||
st <- get
|
||||
let updateState l = do let new_command = nextCompletion (xptype st) (command st) l
|
||||
modify $ \s -> s { command = new_command, offset = length new_command }
|
||||
modify $ \s -> setCommand new_command $ s { offset = length new_command }
|
||||
updateWins l = do redrawWindows l
|
||||
eventLoop (completionHandle l)
|
||||
case c of
|
||||
@@ -319,11 +350,10 @@ tryAutoComplete = do
|
||||
where runCompleted cmd delay = do
|
||||
st <- get
|
||||
let new_command = nextCompletion (xptype st) (command st) [cmd]
|
||||
modify $ \s -> s { command = "autocompleting..." }
|
||||
modify $ setCommand "autocompleting..."
|
||||
updateWindows
|
||||
io $ threadDelay delay
|
||||
modify $ \s -> s { command = new_command }
|
||||
historyPush
|
||||
modify $ setCommand new_command
|
||||
return True
|
||||
|
||||
-- KeyPresses
|
||||
@@ -348,19 +378,20 @@ keyPressHandle mask (ks,_)
|
||||
| ks == xK_w -> killWord Prev >> go
|
||||
| ks == xK_g || ks == xK_c -> quit
|
||||
| otherwise -> eventLoop handle -- unhandled control sequence
|
||||
| ks == xK_Return = historyPush >> return ()
|
||||
| ks == xK_Return = setSuccess True
|
||||
| ks == xK_BackSpace = deleteString Prev >> go
|
||||
| ks == xK_Delete = deleteString Next >> go
|
||||
| ks == xK_Left = moveCursor Prev >> go
|
||||
| ks == xK_Right = moveCursor Next >> go
|
||||
| ks == xK_Up = moveHistory Prev >> go
|
||||
| ks == xK_Down = moveHistory Next >> go
|
||||
| ks == xK_Home = startOfLine >> go
|
||||
| ks == xK_End = endOfLine >> go
|
||||
| ks == xK_Down = moveHistory W.focusUp' >> go
|
||||
| ks == xK_Up = moveHistory W.focusDown' >> go
|
||||
| ks == xK_Escape = quit
|
||||
where
|
||||
go = updateWindows >> eventLoop handle
|
||||
quit = flushString >> return () -- quit and discard everything
|
||||
quit = flushString >> setSuccess False -- quit and discard everything
|
||||
setSuccess b = modify $ \s -> s { successful = b }
|
||||
-- insert a character
|
||||
keyPressHandle _ (_,s)
|
||||
| s == "" = eventLoop handle
|
||||
@@ -374,18 +405,18 @@ keyPressHandle _ (_,s)
|
||||
-- | Kill the portion of the command before the cursor
|
||||
killBefore :: XP ()
|
||||
killBefore =
|
||||
modify $ \s -> s { command = drop (offset s) (command s)
|
||||
, offset = 0 }
|
||||
modify $ \s -> setCommand (drop (offset s) (command s)) $ s { offset = 0 }
|
||||
|
||||
-- | Kill the portion of the command including and after the cursor
|
||||
killAfter :: XP ()
|
||||
killAfter =
|
||||
modify $ \s -> s { command = take (offset s) (command s) }
|
||||
modify $ \s -> setCommand (take (offset s) (command s)) s
|
||||
|
||||
-- | Kill the next\/previous word
|
||||
killWord :: Direction -> XP ()
|
||||
killWord d = do
|
||||
XPS { command = c, offset = o } <- get
|
||||
o <- gets offset
|
||||
c <- gets command
|
||||
let (f,ss) = splitAt o c
|
||||
delNextWord w =
|
||||
case w of
|
||||
@@ -396,7 +427,7 @@ killWord d = do
|
||||
case d of
|
||||
Next -> (f ++ delNextWord ss, o)
|
||||
Prev -> (delPrevWord f ++ ss, length $ delPrevWord f) -- laziness!!
|
||||
modify $ \s -> s { command = ncom, offset = noff}
|
||||
modify $ \s -> setCommand ncom $ s { offset = noff}
|
||||
|
||||
-- | Put the cursor at the end of line
|
||||
endOfLine :: XP ()
|
||||
@@ -411,12 +442,12 @@ startOfLine =
|
||||
-- | Flush the command string and reset the offset
|
||||
flushString :: XP ()
|
||||
flushString = do
|
||||
modify $ \s -> s { command = "", offset = 0}
|
||||
modify $ \s -> setCommand "" $ s { offset = 0}
|
||||
|
||||
-- | Insert a character at the cursor position
|
||||
insertString :: String -> XP ()
|
||||
insertString str =
|
||||
modify $ \s -> s { command = c (command s) (offset s), offset = o (offset s)}
|
||||
modify $ \s -> setCommand (c (command s) (offset s)) $ s { offset = o (offset s)}
|
||||
where o oo = oo + length str
|
||||
c oc oo | oo >= length oc = oc ++ str
|
||||
| otherwise = f ++ str ++ ss
|
||||
@@ -429,7 +460,7 @@ pasteString = join $ io $ liftM insertString $ getSelection
|
||||
-- | Remove a character at the cursor position
|
||||
deleteString :: Direction -> XP ()
|
||||
deleteString d =
|
||||
modify $ \s -> s { command = c (command s) (offset s), offset = o (offset s)}
|
||||
modify $ \s -> setCommand (c (command s) (offset s)) $ s { offset = o (offset s)}
|
||||
where o oo = if d == Prev then max 0 (oo - 1) else oo
|
||||
c oc oo
|
||||
| oo >= length oc && d == Prev = take (oo - 1) oc
|
||||
@@ -459,17 +490,10 @@ moveWord d = do
|
||||
Next -> o + (ln id ss)
|
||||
modify $ \s -> s { offset = newoff }
|
||||
|
||||
moveHistory :: Direction -> XP ()
|
||||
moveHistory d = do
|
||||
h <- getHistory
|
||||
c <- gets command
|
||||
let str = if h /= [] then head h else c
|
||||
let nc = case elemIndex c h of
|
||||
Just i -> case d of
|
||||
Prev -> h !! (if (i + 1) > (length h - 1) then 0 else i + 1)
|
||||
Next -> h !! (max (i - 1) 0)
|
||||
Nothing -> str
|
||||
modify $ \s -> s { command = nc, offset = length nc}
|
||||
moveHistory :: (W.Stack String -> W.Stack String) -> XP ()
|
||||
moveHistory f = modify $ \s -> let ch = f $ commandHistory s
|
||||
in s { commandHistory = ch
|
||||
, offset = length $ W.focus ch }
|
||||
|
||||
-- X Stuff
|
||||
|
||||
@@ -636,7 +660,7 @@ redrawComplWin compl = do
|
||||
let recreate = do destroyComplWin
|
||||
w <- createComplWin nwi
|
||||
drawComplWin w compl
|
||||
if (compl /= [] )
|
||||
if (compl /= [] && showComplWin st)
|
||||
then case complWin st of
|
||||
Just w -> case complWinDim st of
|
||||
Just wi -> if nwi == wi -- complWinDim did not change
|
||||
@@ -673,41 +697,28 @@ printComplString d drw gc fc bc x y s = do
|
||||
|
||||
-- History
|
||||
|
||||
data History =
|
||||
H { prompt :: String
|
||||
, command_history :: String
|
||||
} deriving (Show, Read, Eq)
|
||||
type History = Map String [String]
|
||||
|
||||
historyPush :: XP ()
|
||||
historyPush = do
|
||||
c <- gets command
|
||||
when (c /= []) $ modify (\s -> s { history = nub $ H (showXPrompt (xptype s)) c : history s })
|
||||
emptyHistory :: History
|
||||
emptyHistory = Map.empty
|
||||
|
||||
getHistory :: XP [String]
|
||||
getHistory = do
|
||||
hist <- gets history
|
||||
pt <- gets xptype
|
||||
return $ map command_history . filter (\h -> prompt h == showXPrompt pt) $ hist
|
||||
getHistoryFile :: IO FilePath
|
||||
getHistoryFile = fmap (++ "/history") $ getAppUserDataDirectory "xmonad"
|
||||
|
||||
readHistory :: IO ([History],Handle)
|
||||
readHistory = do
|
||||
home <- getEnv "HOME"
|
||||
let path = home ++ "/.xmonad/history"
|
||||
f <- fileExist path
|
||||
if f then do h <- openFile path ReadMode
|
||||
str <- hGetContents h
|
||||
case (reads str) of
|
||||
[(hist,_)] -> return (hist,h)
|
||||
[] -> return ([],h)
|
||||
_ -> return ([],h)
|
||||
else do h <- openFile path WriteMode
|
||||
return ([],h)
|
||||
readHistory :: IO History
|
||||
readHistory = catch readHist (const (return emptyHistory))
|
||||
where
|
||||
readHist = do
|
||||
path <- getHistoryFile
|
||||
xs <- bracket (openFile path ReadMode) hClose hGetLine
|
||||
readIO xs
|
||||
|
||||
writeHistory :: [History] -> IO ()
|
||||
writeHistory :: History -> IO ()
|
||||
writeHistory hist = do
|
||||
home <- getEnv "HOME"
|
||||
let path = home ++ "/.xmonad/history"
|
||||
catch (writeFile path (show hist)) (\_ -> do putStrLn "error in writing"; return ())
|
||||
path <- getHistoryFile
|
||||
catch (writeFile path (show hist)) $ const $ hPutStrLn stderr "error in writing"
|
||||
setFileMode path mode
|
||||
where mode = ownerReadMode .|. ownerWriteMode
|
||||
|
||||
-- $xutils
|
||||
|
||||
@@ -802,18 +813,21 @@ breakAtSpace s
|
||||
where (s1, s2 ) = break isSpace s
|
||||
(s1',s2') = breakAtSpace $ tail s2
|
||||
|
||||
-- | Sort a list and remove duplicates.
|
||||
-- | 'historyCompletion' provides a canned completion function much like
|
||||
-- 'getShellCompl'; you pass it to mkXPrompt, and it will make completions work
|
||||
-- from the query history stored in ~\/.xmonad\/history.
|
||||
historyCompletion :: ComplFunction
|
||||
historyCompletion x = fmap (deleteConsecutive . filter (isInfixOf x) . Map.fold (++) []) readHistory
|
||||
|
||||
-- | Sort a list and remove duplicates. Like 'deleteAllDuplicates', but trades off
|
||||
-- laziness and stability for efficiency.
|
||||
uniqSort :: Ord a => [a] -> [a]
|
||||
uniqSort = toList . fromList
|
||||
|
||||
-- | 'historyCompletion' provides a canned completion function much like
|
||||
-- getShellCompl; you pass it to mkXPrompt, and it will make completions work
|
||||
-- from the query history stored in ~/.xmonad/history.
|
||||
historyCompletion :: ComplFunction
|
||||
historyCompletion = \x -> liftM (filter $ isInfixOf x) readHistoryIO
|
||||
|
||||
-- We need to define this locally because there is no function with the type "XP a -> IO a", and
|
||||
-- 'getHistory' is uselessly of the type "XP [String]".
|
||||
readHistoryIO :: IO [String]
|
||||
readHistoryIO = do (hist,_) <- readHistory
|
||||
return $ map command_history hist
|
||||
-- | Functions to be used with the 'historyFilter' setting.
|
||||
-- 'deleteAllDuplicates' will remove all duplicate entries.
|
||||
-- 'deleteConsecutive' will only remove duplicate elements
|
||||
-- immediately next to each other.
|
||||
deleteAllDuplicates, deleteConsecutive :: [String] -> [String]
|
||||
deleteAllDuplicates = nub
|
||||
deleteConsecutive = map head . group
|
||||
|
@@ -88,12 +88,12 @@ manCompl mans s | s == "" || last s == ' ' = return []
|
||||
-- better\/more idiomatic.)
|
||||
getCommandOutput :: String -> IO String
|
||||
getCommandOutput s = do
|
||||
(pin, pout, perr, ph) <- runInteractiveCommand s
|
||||
-- we can ignore the process handle because we ignor SIGCHLD
|
||||
(pin, pout, perr, _) <- runInteractiveCommand s
|
||||
hClose pin
|
||||
output <- hGetContents pout
|
||||
E.evaluate (length output)
|
||||
hClose perr
|
||||
waitForProcess ph
|
||||
return output
|
||||
|
||||
stripExt :: String -> String
|
||||
|
@@ -15,7 +15,8 @@
|
||||
module XMonad.Prompt.Shell
|
||||
( -- * Usage
|
||||
-- $usage
|
||||
shellPrompt
|
||||
Shell (..)
|
||||
, shellPrompt
|
||||
, getCommands
|
||||
, getBrowser
|
||||
, getEditor
|
||||
@@ -141,4 +142,4 @@ getBrowser = env "BROWSER" "firefox"
|
||||
|
||||
-- | Like 'getBrowser', but should be of a text editor. This gets the $EDITOR variable, defaulting to \"emacs\".
|
||||
getEditor :: IO String
|
||||
getEditor = env "EDITOR" "emacs"
|
||||
getEditor = env "EDITOR" "emacs"
|
||||
|
@@ -19,7 +19,8 @@ module XMonad.Prompt.Window
|
||||
-- * Usage
|
||||
-- $usage
|
||||
windowPromptGoto,
|
||||
windowPromptBring
|
||||
windowPromptBring,
|
||||
windowPromptBringCopy
|
||||
) where
|
||||
|
||||
import qualified Data.Map as M
|
||||
@@ -28,6 +29,7 @@ import Data.List
|
||||
import qualified XMonad.StackSet as W
|
||||
import XMonad
|
||||
import XMonad.Prompt
|
||||
import XMonad.Actions.CopyWindow
|
||||
import XMonad.Actions.WindowBringer
|
||||
|
||||
-- $usage
|
||||
@@ -57,16 +59,18 @@ import XMonad.Actions.WindowBringer
|
||||
-- For detailed instruction on editing the key binding see
|
||||
-- "XMonad.Doc.Extending#Editing_key_bindings".
|
||||
|
||||
data WindowPrompt = Goto | Bring
|
||||
data WindowPrompt = Goto | Bring | BringCopy
|
||||
instance XPrompt WindowPrompt where
|
||||
showXPrompt Goto = "Go to window: "
|
||||
showXPrompt Bring = "Bring me here: "
|
||||
showXPrompt Bring = "Bring window: "
|
||||
showXPrompt BringCopy = "Bring a copy: "
|
||||
commandToComplete _ c = c
|
||||
nextCompletion _ = getNextCompletion
|
||||
|
||||
windowPromptGoto, windowPromptBring :: XPConfig -> X ()
|
||||
windowPromptGoto, windowPromptBring, windowPromptBringCopy :: XPConfig -> X ()
|
||||
windowPromptGoto c = doPrompt Goto c
|
||||
windowPromptBring c = doPrompt Bring c
|
||||
windowPromptBringCopy c = doPrompt BringCopy c
|
||||
|
||||
-- | Pops open a prompt with window titles. Choose one, and you will be
|
||||
-- taken to the corresponding workspace.
|
||||
@@ -75,6 +79,7 @@ doPrompt t c = do
|
||||
a <- case t of
|
||||
Goto -> fmap gotoAction windowMap
|
||||
Bring -> fmap bringAction windowMap
|
||||
BringCopy -> fmap bringCopyAction windowMap
|
||||
wm <- windowMap
|
||||
mkXPrompt t c (compList wm) a
|
||||
|
||||
@@ -82,5 +87,11 @@ doPrompt t c = do
|
||||
winAction a m = flip whenJust (windows . a) . flip M.lookup m
|
||||
gotoAction = winAction W.focusWindow
|
||||
bringAction = winAction bringWindow
|
||||
bringCopyAction = winAction bringCopyWindow
|
||||
|
||||
compList m s = return . filter (isPrefixOf s) . map fst . M.toList $ m
|
||||
|
||||
|
||||
-- | Brings a copy of the specified window into the current workspace.
|
||||
bringCopyWindow :: Window -> WindowSet -> WindowSet
|
||||
bringCopyWindow w ws = copyWindow w (W.currentTag $ ws) ws
|
||||
|
@@ -15,10 +15,10 @@
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module XMonad.Util.Dmenu (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
dmenu, dmenuXinerama, dmenuMap
|
||||
) where
|
||||
-- * Usage
|
||||
-- $usage
|
||||
dmenu, dmenuXinerama, dmenuMap, menu, menuMap
|
||||
) where
|
||||
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
@@ -40,9 +40,17 @@ dmenuXinerama opts = do
|
||||
io $ runProcessWithInput "dmenu" ["-xs", show (curscreen+1)] (unlines opts)
|
||||
|
||||
dmenu :: [String] -> X String
|
||||
dmenu opts = io $ runProcessWithInput "dmenu" [] (unlines opts)
|
||||
dmenu opts = menu "dmenu" opts
|
||||
|
||||
menu :: String -> [String] -> X String
|
||||
menu menuCmd opts = io $ runProcessWithInput menuCmd [] (unlines opts)
|
||||
|
||||
menuMap :: String -> M.Map String a -> X (Maybe a)
|
||||
menuMap menuCmd selectionMap = do
|
||||
selection <- menuFunction (M.keys selectionMap)
|
||||
return $ M.lookup selection selectionMap
|
||||
where
|
||||
menuFunction = menu menuCmd
|
||||
|
||||
dmenuMap :: M.Map String a -> X (Maybe a)
|
||||
dmenuMap selectionMap = do
|
||||
selection <- dmenu (M.keys selectionMap)
|
||||
return $ M.lookup selection selectionMap
|
||||
dmenuMap selectionMap = menuMap "dmenu" selectionMap
|
@@ -134,11 +134,16 @@ removeMouseBindings conf mouseBindingList =
|
||||
-- the key sequence descriptions contained in the Strings. The key
|
||||
-- sequence descriptions are \"emacs-style\": @M-@, @C-@, @S-@, and
|
||||
-- @M\#-@ denote mod, control, shift, and mod1-mod5 (where @\#@ is
|
||||
-- replaced by the appropriate number) respectively; some special
|
||||
-- keys can be specified by enclosing their name in angle brackets.
|
||||
-- replaced by the appropriate number) respectively. Note that if
|
||||
-- you want to make a keybinding using \'alt\' even though you use a
|
||||
-- different key (like the \'windows\' key) for \'mod\', you can use
|
||||
-- something like @\"M1-x\"@ for alt+x (check the output of @xmodmap@
|
||||
-- to see which mod key \'alt\' is bound to). Some special keys can
|
||||
-- also be specified by enclosing their name in angle brackets.
|
||||
--
|
||||
-- For example, @\"M-C-x\"@ denotes mod+ctrl+x; @\"S-\<Escape\>\"@ denotes
|
||||
-- shift-escape.
|
||||
-- For example, @\"M-C-x\"@ denotes mod+ctrl+x; @\"S-\<Escape\>\"@
|
||||
-- denotes shift-escape; @\"M1-C-\<Delete\>\"@ denotes alt+ctrl+delete
|
||||
-- (assuming alt is bound to mod1, which is common).
|
||||
--
|
||||
-- Sequences of keys can also be specified by separating the key
|
||||
-- descriptions with spaces. For example, @\"M-x y \<Down\>\"@ denotes the
|
||||
@@ -159,7 +164,10 @@ removeMouseBindings conf mouseBindingList =
|
||||
-- create a keymap and add it to your config.
|
||||
--
|
||||
-- Here is a complete list of supported special keys. Note that a few
|
||||
-- keys, such as the arrow keys, have synonyms:
|
||||
-- keys, such as the arrow keys, have synonyms. If there are other
|
||||
-- special keys you would like to see supported, feel free to submit a
|
||||
-- patch, or ask on the xmonad mailing list; adding special keys is
|
||||
-- quite simple.
|
||||
--
|
||||
-- > <Backspace>
|
||||
-- > <Tab>
|
||||
|
@@ -43,7 +43,7 @@ import Graphics.X11.Xft
|
||||
import Graphics.X11.Xrender
|
||||
#endif
|
||||
|
||||
#if defined XFT || defined UTF8
|
||||
#if defined XFT || defined USE_UTF8
|
||||
import Codec.Binary.UTF8.String (encodeString, decodeString)
|
||||
#endif
|
||||
|
||||
@@ -103,7 +103,7 @@ initXMF s =
|
||||
return (Xft xftdraw)
|
||||
else
|
||||
#endif
|
||||
#ifdef UTF8
|
||||
#ifdef USE_UTF8
|
||||
fmap Utf8 $ initUtf8Font s
|
||||
#else
|
||||
fmap Core $ initCoreFont s
|
||||
@@ -195,14 +195,14 @@ printStringXMF dpy drw fs@(Xft font) gc fc bc x y s = do
|
||||
#endif
|
||||
|
||||
decodeInput :: String -> String
|
||||
#if defined XFT || defined UTF8
|
||||
#if defined XFT || defined USE_UTF8
|
||||
decodeInput = decodeString
|
||||
#else
|
||||
decodeInput = id
|
||||
#endif
|
||||
|
||||
encodeOutput :: String -> String
|
||||
#if defined XFT || defined UTF8
|
||||
#if defined XFT || defined USE_UTF8
|
||||
encodeOutput = encodeString
|
||||
#else
|
||||
encodeOutput = id
|
||||
|
@@ -30,7 +30,7 @@ import XMonad.Core
|
||||
|
||||
import System.Time
|
||||
import System.IO
|
||||
import System.Process
|
||||
import System.Process (runInteractiveCommand)
|
||||
import System.Locale
|
||||
|
||||
-- $usage
|
||||
@@ -82,7 +82,7 @@ battery = logCmd "/usr/bin/acpi | sed -r 's/.*?: (.*%).*/\\1/; s/discharging, ([
|
||||
|
||||
-- | Create a 'Logger' from an arbitrary shell command.
|
||||
logCmd :: String -> Logger
|
||||
logCmd c = io $ do (_, out, _, proc) <- runInteractiveCommand c
|
||||
logCmd c = io $ do (_, out, _, _) <- runInteractiveCommand c
|
||||
output <- hGetLine out
|
||||
waitForProcess proc
|
||||
-- no need to waitForProcess, we ignore SIGCHLD
|
||||
return $ Just output
|
||||
|
94
XMonad/Util/Paste.hs
Normal file
94
XMonad/Util/Paste.hs
Normal file
@@ -0,0 +1,94 @@
|
||||
{- |
|
||||
Module : XMonad.Util.Paste
|
||||
Copyright : (C) 2008 Jérémy Bobbio, gwern
|
||||
License : BSD3
|
||||
|
||||
Maintainer : gwern <gwern0@gmail.com>
|
||||
Stability : unstable
|
||||
Portability : unportable
|
||||
|
||||
A module for sending key presses to windows. This modules provides generalized
|
||||
and specialized functions for this task.
|
||||
-}
|
||||
|
||||
module XMonad.Util.Paste ( -- * Usage
|
||||
-- $usage
|
||||
pasteSelection,
|
||||
pasteString,
|
||||
pasteChar,
|
||||
sendKey,
|
||||
sendKeyWindow,
|
||||
noModMask
|
||||
)
|
||||
where
|
||||
|
||||
import XMonad (io, theRoot, withDisplay, X ())
|
||||
import Graphics.X11
|
||||
import Graphics.X11.Xlib.Extras (none, setEventType, setKeyEvent)
|
||||
import Control.Monad.Reader (asks)
|
||||
import XMonad.Operations (withFocused)
|
||||
import Data.Char (isUpper)
|
||||
import Graphics.X11.Xlib.Misc (stringToKeysym)
|
||||
import XMonad.Util.XSelection (getSelection)
|
||||
|
||||
{- $usage
|
||||
|
||||
Import this module into your xmonad.hs as usual:
|
||||
|
||||
> import XMonad.Util.Paste
|
||||
|
||||
And use the functions. They all return 'X' (), and so are appropriate
|
||||
for use as keybindings. Example:
|
||||
|
||||
> , ((m, xK_d), pasteString "foo bar") ]
|
||||
|
||||
Don't expect too much of the functions; they probably don't work on complex
|
||||
texts.
|
||||
-}
|
||||
|
||||
-- | Paste the current X mouse selection. Note that this uses 'getSelection' from
|
||||
-- "XMonad.Util.XSelection" and so is heir to its flaws.
|
||||
pasteSelection :: X ()
|
||||
pasteSelection = getSelection >>= pasteString
|
||||
|
||||
-- | Send a string to the window which is currently focused. This function correctly
|
||||
-- handles capitalization.
|
||||
pasteString :: String -> X ()
|
||||
pasteString = mapM_ (\x -> if isUpper x then pasteChar shiftMask x else pasteChar noModMask x)
|
||||
|
||||
{- | Send a character to the current window. This is more low-level.
|
||||
Remember that you must handle the case of capitalization appropriately.
|
||||
That is, from the window's perspective:
|
||||
|
||||
> pasteChar mod2Mask 'F' ~> "f"
|
||||
|
||||
You would want to do something like:
|
||||
|
||||
> pasteChar shiftMask 'F'
|
||||
|
||||
Note that this function makes use of 'stringToKeysym', and so will probably
|
||||
have trouble with any 'Char' outside ASCII.
|
||||
-}
|
||||
pasteChar :: KeyMask -> Char -> X ()
|
||||
pasteChar m c = sendKey m $ stringToKeysym [c]
|
||||
|
||||
sendKey :: KeyMask -> KeySym -> X ()
|
||||
sendKey = (withFocused .) . sendKeyWindow
|
||||
|
||||
-- | The primitive. Allows you to send any combination of 'KeyMask' and 'KeySym' to any 'Window' you specify.
|
||||
sendKeyWindow :: KeyMask -> KeySym -> Window -> X ()
|
||||
sendKeyWindow mods key w = withDisplay $ \d -> do
|
||||
rootw <- asks theRoot
|
||||
keycode <- io $ keysymToKeycode d key
|
||||
io $ allocaXEvent $ \ev -> do
|
||||
setEventType ev keyPress
|
||||
setKeyEvent ev w rootw none mods keycode True
|
||||
sendEvent d w True keyPressMask ev
|
||||
setEventType ev keyRelease
|
||||
sendEvent d w True keyReleaseMask ev
|
||||
|
||||
-- | A null 'KeyMask'. Used when you don't want a character or string shifted, control'd, or what.
|
||||
--
|
||||
-- TODO: This really should be a function in the X11 binding. When noModMask shows up there, remove.
|
||||
noModMask :: KeyMask
|
||||
noModMask = 0
|
@@ -31,7 +31,7 @@ module XMonad.Util.Run (
|
||||
) where
|
||||
|
||||
import System.Posix.IO
|
||||
import System.Posix.Process (executeFile)
|
||||
import System.Posix.Process (executeFile, forkProcess)
|
||||
import Control.Concurrent (threadDelay)
|
||||
import Control.Exception (try)
|
||||
import System.IO
|
||||
@@ -54,20 +54,20 @@ import Control.Monad
|
||||
-- This corresponds to dmenu's notion of exit code 1 for a cancelled invocation.
|
||||
runProcessWithInput :: FilePath -> [String] -> String -> IO String
|
||||
runProcessWithInput cmd args input = do
|
||||
(pin, pout, perr, ph) <- runInteractiveProcess cmd args Nothing Nothing
|
||||
(pin, pout, perr, _) <- runInteractiveProcess cmd args Nothing Nothing
|
||||
hPutStr pin input
|
||||
hClose pin
|
||||
output <- hGetContents pout
|
||||
when (output == output) $ return ()
|
||||
hClose pout
|
||||
hClose perr
|
||||
waitForProcess ph
|
||||
-- no need to waitForProcess, we ignore SIGCHLD
|
||||
return output
|
||||
|
||||
-- | Wait is in µs (microseconds)
|
||||
runProcessWithInputAndWait :: FilePath -> [String] -> String -> Int -> IO ()
|
||||
runProcessWithInputAndWait cmd args input timeout = do
|
||||
doubleFork $ do
|
||||
forkProcess $ do
|
||||
(pin, pout, perr, ph) <- runInteractiveProcess cmd args Nothing Nothing
|
||||
hPutStr pin input
|
||||
hFlush pin
|
||||
@@ -77,6 +77,7 @@ runProcessWithInputAndWait cmd args input timeout = do
|
||||
hClose perr
|
||||
waitForProcess ph
|
||||
return ()
|
||||
return ()
|
||||
|
||||
-- | Multiplies by ONE MILLION, for functions that take microseconds.
|
||||
--
|
||||
@@ -106,7 +107,7 @@ it makes use of shell interpretation by relying on @$HOME@ and
|
||||
interpolation, whereas the safeSpawn example can be safe because
|
||||
Firefox doesn't need any arguments if it is just being started. -}
|
||||
safeSpawn :: MonadIO m => FilePath -> String -> m ()
|
||||
safeSpawn prog arg = liftIO (try (doubleFork $ executeFile prog True [arg] Nothing) >> return ())
|
||||
safeSpawn prog arg = liftIO (try (forkProcess $ executeFile prog True [arg] Nothing) >> return ())
|
||||
|
||||
unsafeSpawn :: MonadIO m => String -> m ()
|
||||
unsafeSpawn = spawn
|
||||
@@ -128,7 +129,7 @@ spawnPipe x = do
|
||||
setFdOption wr CloseOnExec True
|
||||
h <- fdToHandle wr
|
||||
hSetBuffering h LineBuffering
|
||||
doubleFork $ do
|
||||
forkProcess $ do
|
||||
dupTo rd stdInput
|
||||
executeFile "/bin/sh" False ["-c", x] Nothing
|
||||
return h
|
||||
|
@@ -23,6 +23,7 @@ import XMonad
|
||||
import Control.Applicative
|
||||
import Control.Concurrent
|
||||
import Data.Unique
|
||||
import System.Posix.Process (forkProcess)
|
||||
|
||||
-- $usage
|
||||
-- This module can be used to setup a timer to handle deferred events.
|
||||
@@ -35,7 +36,7 @@ type TimerId = Int
|
||||
startTimer :: Rational -> X TimerId
|
||||
startTimer s = io $ do
|
||||
u <- hashUnique <$> newUnique
|
||||
doubleFork $ do
|
||||
forkProcess $ do
|
||||
d <- openDisplay ""
|
||||
rw <- rootWindow d $ defaultScreen d
|
||||
threadDelay (fromEnum $ s * 1000000)
|
||||
|
@@ -14,10 +14,12 @@
|
||||
module XMonad.Util.WindowProperties (
|
||||
-- * Usage
|
||||
-- $usage
|
||||
Property(..), hasProperty, focusedHasProperty)
|
||||
Property(..), hasProperty, focusedHasProperty, allWithProperty,
|
||||
propertyToQuery)
|
||||
where
|
||||
import XMonad
|
||||
import qualified XMonad.StackSet as W
|
||||
import Control.Monad
|
||||
|
||||
-- $usage
|
||||
-- This module allows to specify window properties, such as title, classname or
|
||||
@@ -59,3 +61,20 @@ focusedHasProperty p = do
|
||||
Just s -> hasProperty p $ W.focus s
|
||||
Nothing -> return False
|
||||
|
||||
-- | Find all existing windows with specified property
|
||||
allWithProperty :: Property -> X [Window]
|
||||
allWithProperty prop = withDisplay $ \dpy -> do
|
||||
rootw <- asks theRoot
|
||||
(_,_,wins) <- io $ queryTree dpy rootw
|
||||
hasProperty prop `filterM` wins
|
||||
|
||||
-- | Convert property to 'Query' 'Bool' (see "XMonad.ManageHook")
|
||||
propertyToQuery :: Property -> Query Bool
|
||||
propertyToQuery (Title s) = title =? s
|
||||
propertyToQuery (Resource s) = resource =? s
|
||||
propertyToQuery (ClassName s) = className =? s
|
||||
propertyToQuery (Role s) = stringProperty "WM_WINDOW_ROLE" =? s
|
||||
propertyToQuery (And p1 p2) = propertyToQuery p1 <&&> propertyToQuery p2
|
||||
propertyToQuery (Or p1 p2) = propertyToQuery p1 <||> propertyToQuery p2
|
||||
propertyToQuery (Not p) = not `fmap` propertyToQuery p
|
||||
propertyToQuery (Const b) = return b
|
||||
|
@@ -1,10 +1,10 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{- |
|
||||
Module : XMonad.Util.XSelection
|
||||
Copyright : (C) 2007 Andrea Rossato, Matthew Sackman
|
||||
License : BSD3
|
||||
|
||||
Maintainer : Andrea Rossato <andrea.rossato@unibz.it>,
|
||||
Matthew Sackman <matthew@wellquite.org>
|
||||
Maintainer : Gwern Branwen <gwern0@gmail.com>
|
||||
Stability : unstable
|
||||
Portability : unportable
|
||||
|
||||
@@ -19,18 +19,62 @@ module XMonad.Util.XSelection ( -- * Usage
|
||||
getSelection,
|
||||
promptSelection,
|
||||
safePromptSelection,
|
||||
transformPromptSelection,
|
||||
transformSafePromptSelection,
|
||||
putSelection) where
|
||||
|
||||
import Control.Concurrent (forkIO)
|
||||
import Control.Exception as E (catch)
|
||||
import Control.Monad(Monad (return, (>>)), Functor(..), liftM, join)
|
||||
import Data.Bits (shiftL, (.&.), (.|.))
|
||||
import Data.Char (chr, ord)
|
||||
import Data.Char (ord)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Word (Word8)
|
||||
import XMonad
|
||||
import XMonad.Util.Run (safeSpawn, unsafeSpawn)
|
||||
|
||||
#ifdef USE_UTF8
|
||||
import Codec.Binary.UTF8.String (decode)
|
||||
#else
|
||||
import Data.Bits (shiftL, (.&.), (.|.))
|
||||
import Data.Char (chr)
|
||||
import Data.Word (Word8)
|
||||
{- | Decode a UTF8 string packed into a list of Word8 values, directly to
|
||||
String; does not deal with CChar, hence you will want the counter-intuitive @map fromIntegral@
|
||||
UTF-8 decoding for internal use in getSelection.
|
||||
|
||||
This code is copied from Eric Mertens's "utf-string" library <http://code.haskell.org/utf8-string/>
|
||||
(as of version 0.1),\which is BSD-3 licensed like this module.
|
||||
It'd be better to just @import Codec.Binary.UTF8.String (decode)@, but then users of this would need to install it; XMonad has enough
|
||||
dependencies already. -}
|
||||
decode :: [Word8] -> String
|
||||
decode [] = ""
|
||||
decode (c:cs)
|
||||
| c < 0x80 = chr (fromEnum c) : decode cs
|
||||
| c < 0xc0 = replacement_character : decode cs
|
||||
| c < 0xe0 = multi_byte 1 0x1f 0x80
|
||||
| c < 0xf0 = multi_byte 2 0xf 0x800
|
||||
| c < 0xf8 = multi_byte 3 0x7 0x10000
|
||||
| c < 0xfc = multi_byte 4 0x3 0x200000
|
||||
| c < 0xfe = multi_byte 5 0x1 0x4000000
|
||||
| otherwise = replacement_character : decode cs
|
||||
where
|
||||
|
||||
replacement_character :: Char
|
||||
replacement_character = '\xfffd'
|
||||
|
||||
multi_byte :: Int -> Word8 -> Int -> [Char]
|
||||
multi_byte i mask overlong = aux i cs (fromEnum (c .&. mask))
|
||||
where
|
||||
aux 0 rs acc
|
||||
| overlong <= acc && acc <= 0x10ffff &&
|
||||
(acc < 0xd800 || 0xdfff < acc) &&
|
||||
(acc < 0xfffe || 0xffff < acc) = chr acc : decode rs
|
||||
| otherwise = replacement_character : decode rs
|
||||
aux n (r:rs) acc
|
||||
| r .&. 0xc0 == 0x80 = aux (n-1) rs
|
||||
$ shiftL acc 6 .|. fromEnum (r .&. 0x3f)
|
||||
aux _ rs _ = replacement_character : decode rs
|
||||
#endif
|
||||
|
||||
{- $usage
|
||||
Add @import XMonad.Util.XSelection@ to the top of Config.hs
|
||||
Then make use of getSelection or promptSelection as needed; if
|
||||
@@ -127,42 +171,12 @@ shell using 'safeSpawn' from "XMonad.Util.Run"; see its documentation for more
|
||||
details on the advantages and disadvantages of using safeSpawn. -}
|
||||
promptSelection, safePromptSelection, unsafePromptSelection :: String -> X ()
|
||||
promptSelection = unsafePromptSelection
|
||||
safePromptSelection app = join $ io $ liftM (safeSpawn app) (getSelection)
|
||||
safePromptSelection app = join $ io $ liftM (safeSpawn app) getSelection
|
||||
unsafePromptSelection app = join $ io $ liftM unsafeSpawn $ fmap (\x -> app ++ " " ++ x) getSelection
|
||||
|
||||
{- | Decode a UTF8 string packed into a list of Word8 values, directly to
|
||||
String; does not deal with CChar, hence you will want the counter-intuitive @map fromIntegral@
|
||||
UTF-8 decoding for internal use in getSelection.
|
||||
|
||||
This code is copied from Eric Mertens's "utf-string" library <http://code.haskell.org/utf8-string/>
|
||||
(as of version 0.1),\which is BSD-3 licensed like this module.
|
||||
It'd be better to just @import Codec.Binary.UTF8.String (decode)@, but then users of this would need to install it; XMonad has enough
|
||||
dependencies already. -}
|
||||
decode :: [Word8] -> String
|
||||
decode [ ] = ""
|
||||
decode (c:cs)
|
||||
| c < 0x80 = chr (fromEnum c) : decode cs
|
||||
| c < 0xc0 = replacement_character : decode cs
|
||||
| c < 0xe0 = multi_byte 1 0x1f 0x80
|
||||
| c < 0xf0 = multi_byte 2 0xf 0x800
|
||||
| c < 0xf8 = multi_byte 3 0x7 0x10000
|
||||
| c < 0xfc = multi_byte 4 0x3 0x200000
|
||||
| c < 0xfe = multi_byte 5 0x1 0x4000000
|
||||
| otherwise = replacement_character : decode cs
|
||||
where
|
||||
|
||||
replacement_character :: Char
|
||||
replacement_character = '\xfffd'
|
||||
|
||||
multi_byte :: Int -> Word8 -> Int -> [Char]
|
||||
multi_byte i mask overlong = aux i cs (fromEnum (c .&. mask))
|
||||
where
|
||||
aux 0 rs acc
|
||||
| overlong <= acc && acc <= 0x10ffff &&
|
||||
(acc < 0xd800 || 0xdfff < acc) &&
|
||||
(acc < 0xfffe || 0xffff < acc) = chr acc : decode rs
|
||||
| otherwise = replacement_character : decode rs
|
||||
aux n (r:rs) acc
|
||||
| r .&. 0xc0 == 0x80 = aux (n-1) rs
|
||||
$ shiftL acc 6 .|. fromEnum (r .&. 0x3f)
|
||||
aux _ rs _ = replacement_character : decode rs
|
||||
{- | A wrapper around 'promptSelection' and its safe variant. They take two parameters, the first is a function that transforms strings, and the second is the application to run. The transformer essentially transforms the selection in X.
|
||||
One example is to wrap code, such as a command line action copied out of the browser to be run as '"sudo" ++ cmd' or '"su - -c \"" ++ cmd ++ "\"".
|
||||
-}
|
||||
transformPromptSelection, transformSafePromptSelection :: (String -> String) -> String -> X ()
|
||||
transformPromptSelection f app = join $ io $ liftM (safeSpawn app) (fmap f getSelection)
|
||||
transformSafePromptSelection f app = join $ io $ liftM unsafeSpawn $ fmap (\x -> app ++ " " ++ x) (fmap f getSelection)
|
||||
|
@@ -137,7 +137,7 @@ paintWindow' win (Rectangle x y wh ht) bw color b_color str = do
|
||||
io $ fillRectangle d p gc 0 0 wh ht
|
||||
-- and now again
|
||||
io $ setForeground d gc color'
|
||||
io $ fillRectangle d p gc (fi bw) (fi bw) ((wh - (bw * 2))) (ht - (bw * 2))
|
||||
io $ fillRectangle d p gc (fi bw) (fi bw) (wh - (bw * 2)) (ht - (bw * 2))
|
||||
when (isJust str) $ do
|
||||
let (xmf,fc,bc,s) = fromJust str
|
||||
printStringXMF d p xmf gc fc bc x y s
|
||||
|
@@ -1,5 +1,5 @@
|
||||
name: xmonad-contrib
|
||||
version: 0.8
|
||||
version: 0.8.1
|
||||
homepage: http://xmonad.org/
|
||||
synopsis: Third party extensions for xmonad
|
||||
description:
|
||||
@@ -43,7 +43,7 @@ flag testing
|
||||
|
||||
library
|
||||
if flag(small_base)
|
||||
build-depends: base >= 3, containers, directory, process, random, old-time, old-locale
|
||||
build-depends: base >= 3 && < 4, containers, directory, process, random, old-time, old-locale
|
||||
else
|
||||
build-depends: base < 3
|
||||
|
||||
@@ -55,14 +55,17 @@ library
|
||||
if flag(with_utf8)
|
||||
build-depends: utf8-string
|
||||
extensions: ForeignFunctionInterface
|
||||
cpp-options: -DUTF8
|
||||
cpp-options: -DUSE_UTF8
|
||||
|
||||
build-depends: mtl, unix, X11>=1.4.1, xmonad>=0.8, xmonad<0.9
|
||||
build-depends: mtl, unix, X11>=1.4.3, xmonad>=0.8, xmonad<0.9
|
||||
ghc-options: -Wall
|
||||
|
||||
if flag(testing)
|
||||
ghc-options: -Werror
|
||||
|
||||
if impl (ghc >= 6.10.1) && arch (x86_64)
|
||||
ghc-options: -O0
|
||||
|
||||
exposed-modules: XMonad.Doc
|
||||
XMonad.Doc.Configuring
|
||||
XMonad.Doc.Extending
|
||||
@@ -81,6 +84,7 @@ library
|
||||
XMonad.Actions.FlexibleResize
|
||||
XMonad.Actions.FloatKeys
|
||||
XMonad.Actions.FocusNth
|
||||
XMonad.Actions.GridSelect
|
||||
XMonad.Actions.MouseGestures
|
||||
XMonad.Actions.MouseResize
|
||||
XMonad.Actions.NoBorders
|
||||
@@ -91,6 +95,7 @@ library
|
||||
XMonad.Actions.Search
|
||||
XMonad.Actions.SimpleDate
|
||||
XMonad.Actions.SinkAll
|
||||
XMonad.Actions.SpawnOn
|
||||
XMonad.Actions.Submap
|
||||
XMonad.Actions.SwapWorkspaces
|
||||
XMonad.Actions.TagWindows
|
||||
@@ -100,11 +105,11 @@ library
|
||||
XMonad.Actions.WindowGo
|
||||
XMonad.Actions.WindowBringer
|
||||
XMonad.Config.Arossato
|
||||
XMonad.Config.Azerty
|
||||
XMonad.Config.Desktop
|
||||
XMonad.Config.Droundy
|
||||
XMonad.Config.Gnome
|
||||
XMonad.Config.Kde
|
||||
XMonad.Config.PlainConfig
|
||||
XMonad.Config.Sjanssen
|
||||
XMonad.Config.Xfce
|
||||
XMonad.Hooks.DynamicHooks
|
||||
@@ -121,6 +126,7 @@ library
|
||||
XMonad.Hooks.XPropManage
|
||||
XMonad.Layout.Accordion
|
||||
XMonad.Layout.BoringWindows
|
||||
XMonad.Layout.CenteredMaster
|
||||
XMonad.Layout.Circle
|
||||
XMonad.Layout.Combo
|
||||
XMonad.Layout.Decoration
|
||||
@@ -128,8 +134,10 @@ library
|
||||
XMonad.Layout.Dishes
|
||||
XMonad.Layout.DragPane
|
||||
XMonad.Layout.DwmStyle
|
||||
XMonad.Layout.FixedColumn
|
||||
XMonad.Layout.Gaps
|
||||
XMonad.Layout.Grid
|
||||
XMonad.Layout.GridVariants
|
||||
XMonad.Layout.HintedGrid
|
||||
XMonad.Layout.HintedTile
|
||||
XMonad.Layout.IM
|
||||
@@ -141,6 +149,7 @@ library
|
||||
XMonad.Layout.Magnifier
|
||||
XMonad.Layout.Master
|
||||
XMonad.Layout.Maximize
|
||||
XMonad.Layout.Monitor
|
||||
XMonad.Layout.MosaicAlt
|
||||
XMonad.Layout.MultiToggle
|
||||
XMonad.Layout.MultiToggle.Instances
|
||||
@@ -161,6 +170,7 @@ library
|
||||
XMonad.Layout.Tabbed
|
||||
XMonad.Layout.TabBarDecoration
|
||||
XMonad.Layout.ThreeColumns
|
||||
XMonad.Layout.ThreeColumnsMiddle
|
||||
XMonad.Layout.ToggleLayouts
|
||||
XMonad.Layout.TwoPane
|
||||
XMonad.Layout.WindowArranger
|
||||
@@ -197,5 +207,6 @@ library
|
||||
XMonad.Util.Timer
|
||||
XMonad.Util.WindowProperties
|
||||
XMonad.Util.WorkspaceCompare
|
||||
XMonad.Util.Paste
|
||||
XMonad.Util.XSelection
|
||||
XMonad.Util.XUtils
|
||||
|
Reference in New Issue
Block a user