From f24008745e645cf03a1e78fdbf751924f75fb9eb Mon Sep 17 00:00:00 2001 From: Don Stewart Date: Wed, 11 Apr 2007 08:04:56 +0200 Subject: Statically distinguish Workspace and Screen indices darcs-hash:20070411060456-9c5c1-a5642813cffe434e60310d21497217ef3704b296.gz --- Config.hs | 131 +++++++++++++++++++++++++++++----------------------------- Operations.hs | 22 +++++----- StackSet.hs | 77 ++++++++++++++++++++-------------- XMonad.hs | 7 +--- 4 files changed, 122 insertions(+), 115 deletions(-) diff --git a/Config.hs b/Config.hs index 9b2d2f5..ab92814 100644 --- a/Config.hs +++ b/Config.hs @@ -1,70 +1,69 @@ module Config where -{- -xmonad bindings follow mostly the dwm/wmii conventions: - - key combination action - - mod-shift-return new xterm - mod-p launch dmenu - mod-shift-p launch gmrun - - mod-space switch tiling mode - - mod-tab raise next window in stack - mod-j - mod-k - - mod-h resize currently focused window - mod-l - - mod-shift-c kill client - mod-shift-q exit window manager - mod-shift-ctrl-q restart window manager - - mod-return move currently focused window into master position - - mod-1..9 switch to workspace N - mod-shift-1..9 move client to workspace N - - mod-w,e,r switch to physical/Xinerama screen 1, 2 or 3. - -xmonad places each window into a "workspace." Each workspace can have -any number of windows, which you can cycle though with mod-j and mod-k. -Windows are either displayed full screen, tiled horizontally, or tiled -vertically. You can toggle the layout mode with mod-space, which will -cycle through the available modes. - -You can switch to workspace N with mod-N. For example, to switch to -workspace 5, you would press mod-5. Similarly, you can move the current -window to another workspace with mod-shift-N. - -When running with multiple monitors (Xinerama), each screen has exactly -1 workspace visible. When xmonad starts, workspace 1 is on screen 1, -workspace 2 is on screen 2, etc. If you switch to a workspace which is -currently visible on another screen, xmonad simply switches focus to -that screen. If you switch to a workspace which is *not* visible, xmonad -replaces the workspace on the *current* screen with the workspace you -selected. - -For example, if you have the following configuration: - -Screen 1: Workspace 2 -Screen 2: Workspace 5 (current workspace) - -and you wanted to view workspace 7 on screen 1, you would press: - -mod-2 (to select workspace 2, and make screen 1 the current screen) -mod-7 (to select workspace 7) - -Since switching to the workspace currently visible on a given screen is -such a common operation, shortcuts are provided: mod-{w,e,r} switch to -the workspace currently visible on screens 1, 2, and 3 respectively. -Likewise, shift-mod-{w,e,r} moves the current window to the workspace on -that screen. Using these keys, the above example would become mod-w -mod-7. - --} +-- +-- xmonad bindings follow mostly the dwm/wmii conventions: +-- +-- key combination action +-- +-- mod-shift-return new xterm +-- mod-p launch dmenu +-- mod-shift-p launch gmrun +-- +-- mod-space switch tiling mode +-- +-- mod-tab raise next window in stack +-- mod-j +-- mod-k +-- +-- mod-h resize currently focused window +-- mod-l +-- +-- mod-shift-c kill client +-- mod-shift-q exit window manager +-- mod-shift-ctrl-q restart window manager +-- +-- mod-return move currently focused window into master position +-- +-- mod-1..9 switch to workspace N +-- mod-shift-1..9 move client to workspace N +-- +-- mod-w,e,r switch to physical/Xinerama screen 1, 2 or 3. +-- +-- xmonad places each window into a "workspace." Each workspace can have +-- any number of windows, which you can cycle though with mod-j and mod-k. +-- Windows are either displayed full screen, tiled horizontally, or tiled +-- vertically. You can toggle the layout mode with mod-space, which will +-- cycle through the available modes. +-- +-- You can switch to workspace N with mod-N. For example, to switch to +-- workspace 5, you would press mod-5. Similarly, you can move the current +-- window to another workspace with mod-shift-N. +-- +-- When running with multiple monitors (Xinerama), each screen has exactly +-- 1 workspace visible. When xmonad starts, workspace 1 is on screen 1, +-- workspace 2 is on screen 2, etc. If you switch to a workspace which is +-- currently visible on another screen, xmonad simply switches focus to +-- that screen. If you switch to a workspace which is *not* visible, xmonad +-- replaces the workspace on the *current* screen with the workspace you +-- selected. +-- +-- For example, if you have the following configuration: +-- +-- Screen 1: Workspace 2 +-- Screen 2: Workspace 5 (current workspace) +-- +-- and you wanted to view workspace 7 on screen 1, you would press: +-- +-- mod-2 (to select workspace 2, and make screen 1 the current screen) +-- mod-7 (to select workspace 7) +-- +-- Since switching to the workspace currently visible on a given screen is +-- such a common operation, shortcuts are provided: mod-{w,e,r} switch to +-- the workspace currently visible on screens 1, 2, and 3 respectively. +-- Likewise, shift-mod-{w,e,r} moves the current window to the workspace on +-- that screen. Using these keys, the above example would become mod-w +-- mod-7. +-- import Data.Ratio import Data.Bits @@ -129,7 +128,7 @@ keys = M.fromList $ ] ++ -- Keybindings to get to each workspace: - [((m .|. modMask, xK_0 + fromIntegral i), f i) + [((m .|. modMask, xK_0 + fromIntegral i), f (fromIntegral (pred i))) -- index from 0. | i <- [1 .. workspaces] , (f, m) <- [(view, 0), (tag, shiftMask)]] diff --git a/Operations.hs b/Operations.hs index f2576b1..b181414 100644 --- a/Operations.hs +++ b/Operations.hs @@ -29,7 +29,7 @@ refresh = do ,display = d ,layoutDescs = fls ,defaultLayoutDesc = dfltfl } <- get flip mapM_ (M.assocs (W.screen2ws ws)) $ \(scn, n) -> do - let sc = xinesc !! scn + let sc = genericIndex xinesc scn -- temporary coercion! fl = M.findWithDefault dfltfl n fls mapM_ (\(w, rect) -> io $ moveWindowInside d w rect) $ case layoutType fl of @@ -214,20 +214,19 @@ kill = withDisplay $ \d -> do sendEvent d w False noEventMask ev else io (killClient d w) >> return () --- | tag. Move a window to a new workspace -tag :: Int -> X () -tag o = do +-- | tag. Move a window to a new workspace, 0 indexed. +tag :: W.WorkspaceId -> X () +tag n = do ws <- gets workspace - let m = W.current ws + let m = W.current ws -- :: WorkspaceId when (n /= m) $ whenJust (W.peek ws) $ \w -> do hide w windows $ W.shift n - where n = o-1 --- | view. Change the current workspace to workspce at offset 'n-1'. -view :: Int -> X () -view o = do +-- | view. Change the current workspace to workspce at offset n (0 indexed). +view :: W.WorkspaceId -> X () +view n = do ws <- gets workspace let m = W.current ws windows $ W.view n @@ -236,11 +235,10 @@ view o = do -- in case we're switching to an empty workspace. when (m `notElem` (W.visibleWorkspaces ws')) (mapM_ hide (W.index m ws)) setTopFocus - where n = o-1 -- | 'screenWorkspace sc' returns the workspace number viewed by 'sc'. -screenWorkspace :: Int -> X Int -screenWorkspace sc = fmap (succ . fromMaybe 0 . W.workspace sc) (gets workspace) +screenWorkspace :: W.ScreenId -> X W.WorkspaceId +screenWorkspace sc = fmap (fromMaybe 0 . W.workspace sc) (gets workspace) -- | True if window is under management by us isClient :: Window -> X Bool diff --git a/StackSet.hs b/StackSet.hs index 6bc2e99..58cbac6 100644 --- a/StackSet.hs +++ b/StackSet.hs @@ -22,7 +22,7 @@ module StackSet where import Data.Maybe -import qualified Data.List as L (delete) +import qualified Data.List as L (delete,genericLength) import qualified Data.Map as M ------------------------------------------------------------------------ @@ -35,14 +35,20 @@ import qualified Data.Map as M -- | The StackSet data structure. A table of stacks, with a current pointer data StackSet a = StackSet - { current :: !Int -- ^ the currently visible stack - , ws2screen:: !(M.Map Int Int) -- ^ workspace -> screen map - , screen2ws:: !(M.Map Int Int) -- ^ screen -> workspace - , stacks :: !(M.Map Int [a]) -- ^ the separate stacks - , focus :: !(M.Map Int a) -- ^ the window focused in each stack - , cache :: !(M.Map a Int) -- ^ a cache of windows back to their stacks + { current :: !WorkspaceId -- ^ the currently visible stack + , screen2ws:: !(M.Map ScreenId WorkspaceId) -- ^ screen -> workspace + , ws2screen:: !(M.Map WorkspaceId ScreenId) -- ^ workspace -> screen map + , stacks :: !(M.Map WorkspaceId [a]) -- ^ the separate stacks + , focus :: !(M.Map WorkspaceId a) -- ^ the window focused in each stack + , cache :: !(M.Map a WorkspaceId) -- ^ a cache of windows back to their stacks } deriving Eq +-- | Physical screen indicies +newtype ScreenId = S Int deriving (Eq,Ord,Show,Enum,Num,Integral,Real) + +-- | Virtual workspace indicies +newtype WorkspaceId = W Int deriving (Eq,Ord,Show,Enum,Num,Integral,Real) + instance Show a => Show (StackSet a) where showsPrec p s r = showsPrec p (show . toList $ s) r @@ -57,19 +63,23 @@ instance Show a => Show (StackSet a) where -- screens. (also indexed from 0) The 0-indexed stack will be current. empty :: Int -> Int -> StackSet a empty n m = StackSet { current = 0 - , ws2screen = wsScreenAssn - , screen2ws = wsScreenAssn - , stacks = M.fromList (zip [0..n-1] (repeat [])) + , screen2ws = wsScrs2Works + + , ws2screen = wsWorks2Scrs + , stacks = M.fromList (zip [0..W n-1] (repeat [])) , focus = M.empty , cache = M.empty } - where wsScreenAssn = M.fromList $ map (\x -> (x,x)) [0..m-1] - + + where (scrs,wrks) = unzip $ map (\x -> (S x, W x)) [0..m-1] + wsScrs2Works = M.fromList (zip scrs wrks) + wsWorks2Scrs = M.fromList (zip wrks scrs) + -- | /O(log w)/. True if x is somewhere in the StackSet member :: Ord a => a -> StackSet a -> Bool member a w = M.member a (cache w) --- | /O(log n)/. Looks up the stack that x is in, if it is in the StackSet -lookup :: (Monad m, Ord a) => a -> StackSet a -> m Int +-- | /O(log n)/. Looks up the workspace that x is in, if it is in the StackSet +lookup :: (Monad m, Ord a) => a -> StackSet a -> m WorkspaceId lookup x w = M.lookup x (cache w) -- | /O(n)/. Number of stacks @@ -80,10 +90,11 @@ size = M.size . stacks -- | fromList. Build a new StackSet from a list of list of elements -- If there are duplicates in the list, the last occurence wins. -fromList :: Ord a => (Int,Int,[[a]]) -> StackSet a -fromList (_,_,[]) = error "Cannot build a StackSet from an empty list" +-- FIXME: This always makes a StackSet with 1 screen. +fromList :: Ord a => (Int,[[a]]) -> StackSet a +fromList (_,[]) = error "Cannot build a StackSet from an empty list" -fromList (n,m,xs) | n < 0 || n >= length xs +fromList (n,xs) | n < 0 || n >= length xs = error $ "Cursor index is out of range: " ++ show (n, length xs) | m < 1 || m > length xs = error $ "Can't have more screens than workspaces: " ++ show (m, length xs) @@ -93,8 +104,8 @@ fromList (o,m,xs) = view o $ foldr (\(i,ys) s -> (empty (length xs) m) (zip [0..] xs) -- | toList. Flatten a stackset to a list of lists -toList :: StackSet a -> (Int,Int,[[a]]) -toList x = (current x, M.size $ screen2ws x, map snd $ M.toList (stacks x)) +toList :: StackSet a -> (Int,[[a]]) +toList x = (current x, map snd $ M.toList (stacks x)) -- | Push. Insert an element onto the top of the current stack. -- If the element is already in the current stack, it is moved to the top. @@ -110,23 +121,25 @@ peek w = peekStack (current w) w -- | /O(log s)/. Extract the element on the top of the given stack. If no such -- element exists, Nothing is returned. -peekStack :: Int -> StackSet a -> Maybe a +peekStack :: WorkspaceId -> StackSet a -> Maybe a peekStack n w = M.lookup n (focus w) -- | /O(log s)/. Index. Extract the stack at index 'n'. -- If the index is invalid, an exception is thrown. -index :: Int -> StackSet a -> [a] +index :: WorkspaceId -> StackSet a -> [a] index k w = fromJust (M.lookup k (stacks w)) --- | view. Set the stack specified by the Int argument as being visible and the +-- | view. Set the stack specified by the argument as being visible and the -- current StackSet. If the stack wasn't previously visible, it will become -- visible on the current screen. If the index is out of range an exception is -- thrown. -view :: Int -> StackSet a -> StackSet a -view n w | n >= 0 && n < M.size (stacks w) = if M.member n (ws2screen w) - then w { current = n } - else tweak (fromJust $ screen (current w) w) - | otherwise = error $ "view: index out of bounds: " ++ show n +view :: WorkspaceId -> StackSet a -> StackSet a +-- view n w | n >= 0 && n < fromIntegral (M.size (stacks w)) -- coerce + +view n w | M.member n (stacks w) + = if M.member n (ws2screen w) then w { current = n } + else tweak (fromJust $ screen (current w) w) + | otherwise = error $ "view: index out of bounds: " ++ show n where tweak sc = w { screen2ws = M.insert sc n (screen2ws w) , ws2screen = M.insert n sc (M.filter (/=sc) (ws2screen w)) @@ -134,15 +147,15 @@ view n w | n >= 0 && n < M.size (stacks w) = if M.member n (ws2screen w) } -- | That screen that workspace 'n' is visible on, if any. -screen :: Int -> StackSet a -> Maybe Int +screen :: WorkspaceId -> StackSet a -> Maybe ScreenId screen n w = M.lookup n (ws2screen w) -- | The workspace visible on screen 'sc'. Nothing if screen is out of bounds. -workspace :: Int -> StackSet a -> Maybe Int +workspace :: ScreenId -> StackSet a -> Maybe WorkspaceId workspace sc w = M.lookup sc (screen2ws w) -- | A list of the currently visible workspaces. -visibleWorkspaces :: StackSet a -> [Int] +visibleWorkspaces :: StackSet a -> [WorkspaceId] visibleWorkspaces = M.keys . ws2screen -- @@ -168,7 +181,7 @@ rotate o w = maybe w id $ do -- the top of stack 'n'. If the stack to move to is not valid, and -- exception is thrown. -- -shift :: Ord a => Int -> StackSet a -> StackSet a +shift :: Ord a => WorkspaceId -> StackSet a -> StackSet a shift n w = maybe w (\k -> insert k n (delete k w)) (peek w) -- | /O(log n)/. Insert an element onto the top of stack 'n'. @@ -176,7 +189,7 @@ shift n w = maybe w (\k -> insert k n (delete k w)) (peek w) -- If the element exists on another stack, it is removed from that stack. -- If the index is wrong an exception is thrown. -- -insert :: Ord a => a -> Int -> StackSet a -> StackSet a +insert :: Ord a => a -> WorkspaceId -> StackSet a -> StackSet a insert k n old = new { cache = M.insert k n (cache new) , stacks = M.adjust (k:) n (stacks new) , focus = M.insert n k (focus new) } diff --git a/XMonad.hs b/XMonad.hs index 537f212..abc422b 100644 --- a/XMonad.hs +++ b/XMonad.hs @@ -20,7 +20,7 @@ module XMonad ( spawn, trace, whenJust, rot ) where -import StackSet (StackSet) +import StackSet (StackSet,WorkspaceId) import Control.Monad.State import System.IO @@ -43,7 +43,7 @@ data XState = XState , dimensions :: !(Int,Int) -- ^ dimensions of the screen, used for hiding windows , workspace :: !WorkSpace -- ^ workspace list , defaultLayoutDesc :: !LayoutDesc -- ^ default layout - , layoutDescs :: !(M.Map Int LayoutDesc) -- ^ mapping of workspaces to descriptions of their layouts + , layoutDescs :: !(M.Map WorkspaceId LayoutDesc) -- ^ mapping of workspaces to descriptions of their layouts } type WorkSpace = StackSet Window @@ -60,9 +60,6 @@ data LayoutDesc = LayoutDesc { layoutType :: !Layout , tileFraction :: !Rational } - - - -- | The X monad, a StateT transformer over IO encapsulating the window -- manager state newtype X a = X (StateT XState IO a) -- cgit v1.2.3