Move xinerama current/visible/hidden workspace logic into StackSet directly.

This commit is contained in:
Don Stewart
2007-05-21 05:52:53 +00:00
parent 02073c547b
commit ea80d2a71f
3 changed files with 120 additions and 126 deletions

View File

@@ -65,11 +65,12 @@ shift n = withFocused hide >> windows (W.shift n)
-- | view. Change the current workspace to workspace at offset n (0 indexed). -- | view. Change the current workspace to workspace at offset n (0 indexed).
view :: WorkspaceId -> X () view :: WorkspaceId -> X ()
view n = withWorkspace $ \w -> when (n /= (W.tag (W.current w))) $ do view n = withWorkspace $ \old -> when (n /= (W.tag (W.workspace (W.current old)))) $ do
windows $ W.view n -- move in new workspace first, to avoid flicker windows $ W.view n -- move in new workspace first, to avoid flicker
-- Hide the old workspace if it is no longer visible -- Hide the old workspace if it is no longer visible
oldWsNotVisible <- (not . M.member (W.tag . W.current $ w) . W.screens) `liftM` gets windowset oldWsNotVisible <- liftM (notElem (W.current old)) (gets (W.visible . windowset))
when oldWsNotVisible $ mapM_ hide (W.index w) when oldWsNotVisible $ mapM_ hide (W.index old)
clearEnterEvents -- better clear any events from the old workspace clearEnterEvents -- better clear any events from the old workspace
-- | Kill the currently focused client. If we do kill it, we'll get a -- | Kill the currently focused client. If we do kill it, we'll get a
@@ -114,12 +115,13 @@ refresh = do
XConf { xineScreens = xinesc, display = d } <- ask XConf { xineScreens = xinesc, display = d } <- ask
-- for each workspace, layout the currently visible workspaces -- for each workspace, layout the currently visible workspaces
flip mapM_ (M.assocs (W.screens ws)) $ \(n, scn) -> do (`mapM_` (W.current ws : W.visible ws)) $ \w -> do
let this = W.view n ws let n = W.tag (W.workspace w)
this = W.view n ws
Just l = fmap fst $ M.lookup n fls Just l = fmap fst $ M.lookup n fls
-- now tile the windows on this workspace -- now tile the windows on this workspace
rs <- doLayout l (genericIndex xinesc scn) (W.index this) rs <- doLayout l (genericIndex xinesc (W.screen w)) (W.index this)
mapM_ (\(w,rect) -> io (tileWindow d w rect)) rs mapM_ (\(win,rect) -> io (tileWindow d win rect)) rs
-- and raise the focused window if there is one. -- and raise the focused window if there is one.
whenJust (W.peek this) $ io . raiseWindow d whenJust (W.peek this) $ io . raiseWindow d
@@ -178,8 +180,8 @@ setFocusX w = withWorkspace $ \ws -> do
XConf { display = dpy , normalBorder = nbc, focusedBorder = fbc } <- ask XConf { display = dpy , normalBorder = nbc, focusedBorder = fbc } <- ask
-- clear mouse button grab and border on other windows -- clear mouse button grab and border on other windows
(`mapM_` (M.keys . W.screens $ ws)) $ \n -> do (`mapM_` (W.current ws : W.visible ws)) $ \wk -> do
(`mapM_` (W.index (W.view n ws))) $ \otherw -> do (`mapM_` (W.index (W.view (W.tag (W.workspace wk)) ws))) $ \otherw -> do
setButtonGrab True otherw setButtonGrab True otherw
io $ setWindowBorder dpy otherw (color_pixel nbc) io $ setWindowBorder dpy otherw (color_pixel nbc)
@@ -282,7 +284,7 @@ splitVerticallyBy f r = (\(a,b)->(mirrorRect a,mirrorRect b)) $ splitHorizontall
layout :: ((Layout, [Layout]) -> (Layout, [Layout])) -> X () layout :: ((Layout, [Layout]) -> (Layout, [Layout])) -> X ()
layout f = do layout f = do
modify $ \s -> modify $ \s ->
let n = W.tag . W.current . windowset $ s let n = W.tag . W.workspace . W.current . windowset $ s
(Just fl) = M.lookup n $ layouts s (Just fl) = M.lookup n $ layouts s
in s { layouts = M.insert n (f fl) (layouts s) } in s { layouts = M.insert n (f fl) (layouts s) }
refresh refresh

View File

@@ -75,13 +75,13 @@
-- 'delete'. -- 'delete'.
-- --
module StackSet ( module StackSet (
StackSet(..), Workspace(..), Stack(..), StackSet(..), Workspace(..), Screen(..), Stack(..),
new, view, lookupWorkspace, peek, index, focusLeft, focusRight, new, view, lookupWorkspace, peek, index, focusLeft, focusRight,
focusWindow, member, findIndex, insertLeft, delete, swap, shift focusWindow, member, findIndex, insertLeft, delete, swap, shift
) where ) where
import qualified Data.Map as M
import Data.Maybe (listToMaybe) import Data.Maybe (listToMaybe)
import qualified Data.List as L (delete,find,genericSplitAt)
-- API changes from xmonad 0.1: -- API changes from xmonad 0.1:
@@ -103,15 +103,22 @@ import Data.Maybe (listToMaybe)
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- --
-- A cursor into a non-empty list of workspaces. -- A cursor into a non-empty list of workspaces.
-- We puncture the workspace list, producing a hole in the structure
-- used to track the currently focused workspace. The two other lists
-- that are produced are used to track those workspaces visible as
-- Xinerama screens, and those workspaces not visible anywhere.
-- --
data StackSet i a screen = data StackSet i a sid =
StackSet { size :: !i -- number of workspaces StackSet { size :: !i -- number of workspaces
, current :: !(Workspace i a) -- currently focused workspace , current :: !(Screen i a sid) -- currently focused workspace
, prev :: [Workspace i a] -- workspaces to the left , visible :: [Screen i a sid] -- non-focused workspaces, visible in xinerama
, next :: [Workspace i a] -- workspaces to the right , hidden :: [Workspace i a] -- workspaces not visible anywhere
, screens :: M.Map i screen -- a map of visible workspaces to their screens } deriving (Show, Eq)
} deriving (Show, Eq)
-- Visible workspaces, and their Xinerama screens.
data Screen i a sid = Screen { workspace :: !(Workspace i a), screen :: !sid }
deriving (Show, Eq)
-- --
-- A workspace is just a tag - its index - and a stack -- A workspace is just a tag - its index - and a stack
@@ -119,8 +126,6 @@ data StackSet i a screen =
data Workspace i a = Workspace { tag :: !i, stack :: Stack a } data Workspace i a = Workspace { tag :: !i, stack :: Stack a }
deriving (Show, Eq) deriving (Show, Eq)
-- TODO an unmanaged floating layer would go in here somewhere (a 2nd stack?)
-- --
-- A stack is a cursor onto a (possibly empty) window list. -- A stack is a cursor onto a (possibly empty) window list.
-- The data structure tracks focus by construction, and -- The data structure tracks focus by construction, and
@@ -149,40 +154,41 @@ data Stack a = Empty
-- Xinerama: Virtual workspaces are assigned to physical screens, starting at 0. -- Xinerama: Virtual workspaces are assigned to physical screens, starting at 0.
-- --
new :: (Integral i, Integral s) => i -> s -> StackSet i a s new :: (Integral i, Integral s) => i -> s -> StackSet i a s
new n m | n > 0 && m > 0 = StackSet n h [] ts xine new n m | n > 0 && m > 0 = StackSet n cur visi unseen
| otherwise = error "non-positive arguments to StackSet.new" | otherwise = error "non-positive arguments to StackSet.new"
where (h:ts) = Workspace 0 Empty : [ Workspace i Empty | i <- [1 ..n-1]]
xine = M.fromList [ (fromIntegral s, s) | s <- [0 .. m-1] ] where (seen,unseen) = L.genericSplitAt m $ Workspace 0 Empty : [ Workspace i Empty | i <- [1 ..n-1]]
(cur:visi) = [ Screen i s | (i,s) <- zip seen [0..] ]
-- now zip up visibles with their screen id
-- --
-- /O(w)/. Set focus to the workspace with index 'i'. -- /O(w)/. Set focus to the workspace with index 'i'.
-- If the index is out of range, return the original StackSet. -- If the index is out of range, return the original StackSet.
-- --
-- Xinerama: If the workspace is not visible on any Xinerama screen, it -- Xinerama: If the workspace is not visible on any Xinerama screen, it
-- is raised on the current screen. If it is already visible, focus is -- becomes the current screen. If it is in the visible list, it becomes
-- current.
-- is raised to the current screen. If it is already visible, focus is
-- just moved. -- just moved.
-- --
view :: Integral i => i -> StackSet i a s -> StackSet i a s view :: (Eq i, Eq a, Eq s, Integral i) => i -> StackSet i a s -> StackSet i a s
view i s@(StackSet sz (Workspace n _) _ _ scrs) view i s
| i >= 0 && i < sz | i < 0 && i >= size s || i == tag (workspace (current s)) = s -- out of bounds or current
= setCurrent $ if M.member i scrs
then s -- already visisble. just set current.
else case M.lookup n scrs of -- TODO current should always be valid
Nothing -> error "xmonad:view: No physical screen"
Just sc -> s { screens = M.insert i sc (M.delete n scrs) }
| otherwise = s
-- actually moving focus is easy: | Just x <- L.find ((i==).tag.workspace) (visible s)
where setCurrent x = foldr traverse x [1..abs (i-n)] -- if it is visible, it is just raised
= s { current = x, visible = current s : L.delete x (visible s) }
-- work out which direction to move | Just x <- L.find ((i==).tag) (hidden s)
traverse _ = if signum (i-n) >= 0 then viewRight else viewLeft -- if it was hidden, it is raised on the xine screen currently used
= s { current = Screen x (screen (current s))
, hidden = workspace (current s) : L.delete x (hidden s) }
-- /O(1)/. Move workspace focus left or right one node, a la Huet. | otherwise = error "Inconsistent StackSet: workspace not found"
viewLeft (StackSet m t (l:ls) rs sc) = StackSet m l ls (t:rs) sc
viewLeft t = t -- 'Catch'ing this might be hard. Relies on monotonically increasing
viewRight (StackSet m t ls (r:rs) sc) = StackSet m r (t:ls) rs sc -- workspace tags defined in 'new'
viewRight t = t
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- Xinerama operations -- Xinerama operations
@@ -190,7 +196,7 @@ view i s@(StackSet sz (Workspace n _) _ _ scrs)
-- | Find the tag of the workspace visible on Xinerama screen 'sc'. -- | Find the tag of the workspace visible on Xinerama screen 'sc'.
-- Nothing if screen is out of bounds. -- Nothing if screen is out of bounds.
lookupWorkspace :: Eq s => s -> StackSet i a s -> Maybe i lookupWorkspace :: Eq s => s -> StackSet i a s -> Maybe i
lookupWorkspace sc w = listToMaybe [ i | (i,s) <- M.assocs (screens w), s == sc ] lookupWorkspace sc w = listToMaybe [ tag i | Screen i s <- current w : visible w, s == sc ]
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- Operations on the current stack -- Operations on the current stack
@@ -202,7 +208,7 @@ lookupWorkspace sc w = listToMaybe [ i | (i,s) <- M.assocs (screens w), s == sc
-- returning the result. It is like 'maybe' for the focused workspace. -- returning the result. It is like 'maybe' for the focused workspace.
-- --
with :: b -> (Stack a -> b) -> StackSet i a s -> b with :: b -> (Stack a -> b) -> StackSet i a s -> b
with dflt f s = case stack (current s) of Empty -> dflt; v -> f v with dflt f s = case stack (workspace (current s)) of Empty -> dflt; v -> f v
-- TODO: ndm: a 'catch' proof here that 'f' only gets Node -- TODO: ndm: a 'catch' proof here that 'f' only gets Node
-- constructors, hence all 'f's are safe below? -- constructors, hence all 'f's are safe below?
@@ -210,7 +216,8 @@ with dflt f s = case stack (current s) of Empty -> dflt; v -> f v
-- Apply a function, and a default value for Empty, to modify the current stack. -- Apply a function, and a default value for Empty, to modify the current stack.
-- --
modify :: Stack a -> (Stack a -> Stack a) -> StackSet i a s -> StackSet i a s modify :: Stack a -> (Stack a -> Stack a) -> StackSet i a s -> StackSet i a s
modify d f s = s { current = (current s) { stack = with d f s } } modify d f s = s { current = (current s)
{ workspace = (workspace (current s)) { stack = with d f s }}}
-- --
-- /O(1)/. Extract the focused element of the current stack. -- /O(1)/. Extract the focused element of the current stack.
@@ -248,10 +255,10 @@ focusRight = modify Empty $ \c -> case c of
Node t ls [] -> Node x [] (xs ++ [t]) where (x:xs) = reverse ls Node t ls [] -> Node x [] (xs ++ [t]) where (x:xs) = reverse ls
-- --
-- | /O(1) on current window, O(n) in general/. Focus the window 'w'. If the -- | /O(1) on current window, O(n) in general/. Focus the window 'w',
-- workspace 'w' is on is not visible, 'view' that workspace first. -- and set its workspace as current.
-- --
focusWindow :: (Integral i, Eq a) => a -> StackSet i a s -> StackSet i a s focusWindow :: (Integral i, Eq s, Eq a) => a -> StackSet i a s -> StackSet i a s
focusWindow w s | Just w == peek s = s focusWindow w s | Just w == peek s = s
| otherwise = maybe s id $ do | otherwise = maybe s id $ do
n <- findIndex w s n <- findIndex w s
@@ -270,7 +277,8 @@ member a s = maybe False (const True) (findIndex a s)
-- Return Just the workspace index of the given window, or Nothing -- Return Just the workspace index of the given window, or Nothing
-- if the window is not in the StackSet. -- if the window is not in the StackSet.
findIndex :: Eq a => a -> StackSet i a s -> Maybe i findIndex :: Eq a => a -> StackSet i a s -> Maybe i
findIndex a s = listToMaybe [ tag w | w <- current s : prev s ++ next s, has a (stack w) ] findIndex a s = listToMaybe
[ tag w | w <- workspace (current s) : map workspace (visible s) ++ hidden s, has a (stack w) ]
where has _ Empty = False where has _ Empty = False
has x (Node t l r) = x `elem` (t : l ++ r) has x (Node t l r) = x `elem` (t : l ++ r)
@@ -314,9 +322,9 @@ insertLeft a s = if member a s then s else insert
-- * deleting the master window resets it to the newly focused window -- * deleting the master window resets it to the newly focused window
-- * otherwise, delete doesn't affect the master. -- * otherwise, delete doesn't affect the master.
-- --
delete :: (Integral i, Eq a) => a -> StackSet i a s -> StackSet i a s delete :: (Integral i, Eq a, Eq s) => a -> StackSet i a s -> StackSet i a s
delete w s | Just w == peek s = remove s -- common case. delete w s | Just w == peek s = remove s -- common case.
| otherwise = maybe s (removeWindow . tag . current $ s) (findIndex w s) | otherwise = maybe s (removeWindow.tag.workspace.current $ s) (findIndex w s)
where where
-- find and remove window script -- find and remove window script
removeWindow o n = foldr ($) s [view o,remove ,until ((Just w ==) . peek) focusLeft,view n] removeWindow o n = foldr ($) s [view o,remove ,until ((Just w ==) . peek) focusLeft,view n]
@@ -351,8 +359,9 @@ swap = modify Empty $ \c -> case c of
-- workspace. The actual focused workspace doesn't change. If there is -- workspace. The actual focused workspace doesn't change. If there is
-- no element on the current stack, the original stackSet is returned. -- no element on the current stack, the original stackSet is returned.
-- --
shift :: (Eq a, Integral i) => i -> StackSet i a s -> StackSet i a s shift :: (Eq a, Eq s, Integral i) => i -> StackSet i a s -> StackSet i a s
shift n s = if and [n >= 0,n < size s,n /= tag (current s)] then maybe s go (peek s) else s shift n s = if and [n >= 0,n < size s,n /= tag (workspace (current s))]
where go w = foldr ($) s [view (tag (current s)),insertLeft w,view n,delete w] then maybe s go (peek s) else s
where go w = foldr ($) s [view (tag (workspace (current s))),insertLeft w,view n,delete w]
-- ^^ poor man's state monad :-) -- ^^ poor man's state monad :-)

View File

@@ -84,7 +84,7 @@ fromList (o,m,fs,xs) =
type T = StackSet Int Char Int type T = StackSet Int Char Int
-- Useful operation, the non-local workspaces -- Useful operation, the non-local workspaces
hidden x = [ w | w <- prev x ++ next x ] -- the hidden workspaces hidden_spaces x = map workspace (visible x) ++ hidden x
-- Basic data invariants of the StackSet -- Basic data invariants of the StackSet
-- --
@@ -105,27 +105,23 @@ invariant (s :: T) = and
[ noDuplicates [ noDuplicates
-- all this xinerama stuff says we don't have the right structure -- all this xinerama stuff says we don't have the right structure
, currentIsVisible -- , validScreens
, validScreens -- , validWorkspaces
, validWorkspaces -- , inBounds
, inBounds
] ]
where where
ws = [ focus t : left t ++ right t ws = [ focus t : left t ++ right t
| w <- current s : prev s ++ next s, let t = stack w, t /= Empty ] | w <- workspace (current s) : map workspace (visible s) ++ hidden s
, let t = stack w, t /= Empty ]
noDuplicates = nub ws == ws noDuplicates = nub ws == ws
-- xinerama invariants: -- validScreens = monotonic . sort . M. . (W.current s : W.visible : W$ s
currentIsVisible = M.member (tag (current s)) (screens s) -- validWorkspaces = and [ w `elem` allworkspaces | w <- (M.keys . screens) s ]
-- where allworkspaces = map tag $ current s : prev s ++ next s
validScreens = monotonic . sort . M.elems . screens $ s -- inBounds = and [ w >=0 && w < size s | (w,sc) <- M.assocs (screens s) ]
validWorkspaces = and [ w `elem` allworkspaces | w <- (M.keys . screens) s ]
where allworkspaces = map tag $ current s : prev s ++ next s
inBounds = and [ w >=0 && w < size s | (w,sc) <- M.assocs (screens s) ]
monotonic [] = True monotonic [] = True
monotonic (x:[]) = True monotonic (x:[]) = True
@@ -149,7 +145,7 @@ prop_focusRight_I (n :: NonNegative Int) (x :: T) =
prop_focus_I (n :: NonNegative Int) (x :: T) = prop_focus_I (n :: NonNegative Int) (x :: T) =
case peek x of case peek x of
Nothing -> True Nothing -> True
Just _ -> let w = focus . stack . current $ foldr (const focusLeft) x [1..n] Just _ -> let w = focus . stack . workspace . current $ foldr (const focusLeft) x [1..n]
in invariant $ focusWindow w x in invariant $ focusWindow w x
prop_insertLeft_I n (x :: T) = invariant $ insertLeft n x prop_insertLeft_I n (x :: T) = invariant $ insertLeft n x
@@ -171,13 +167,14 @@ prop_shift_I (n :: NonNegative Int) (x :: T) =
-- empty StackSets have no windows in them -- empty StackSets have no windows in them
prop_empty (n :: Positive Int) prop_empty (n :: Positive Int)
(m :: Positive Int) = (m :: Positive Int) =
all (== Empty) [ stack w | w <- current x : prev x ++ next x ] all (== Empty) [ stack w | w <- workspace (current x)
: map workspace (visible x) ++ hidden x ]
where x = new (fromIntegral n) (fromIntegral m) :: T where x = new (fromIntegral n) (fromIntegral m) :: T
-- empty StackSets always have focus on workspace 0 -- empty StackSets always have focus on workspace 0
prop_empty_current (n :: Positive Int) prop_empty_current (n :: Positive Int)
(m :: Positive Int) = tag (current x) == 0 (m :: Positive Int) = tag (workspace $ current x) == 0
where x = new (fromIntegral n) (fromIntegral m) :: T where x = new (fromIntegral n) (fromIntegral m) :: T
-- no windows will be a member of an empty workspace -- no windows will be a member of an empty workspace
@@ -189,7 +186,7 @@ prop_member_empty i (n :: Positive Int) (m :: Positive Int)
-- view sets the current workspace to 'n' -- view sets the current workspace to 'n'
prop_view_current (x :: T) (n :: NonNegative Int) = i < size x ==> prop_view_current (x :: T) (n :: NonNegative Int) = i < size x ==>
tag (current (view i x)) == i tag (workspace $ current (view i x)) == i
where where
i = fromIntegral n i = fromIntegral n
@@ -199,14 +196,15 @@ prop_view_local (x :: T) (n :: NonNegative Int) = i < size x ==>
workspaces x == workspaces (view i x) workspaces x == workspaces (view i x)
where where
workspaces a = sortBy (\s t -> tag s `compare` tag t) $ workspaces a = sortBy (\s t -> tag s `compare` tag t) $
current a : prev a ++ next a workspace (current a)
: map workspace (visible a) ++ hidden a
i = fromIntegral n i = fromIntegral n
-- view should result in a visible xinerama screen -- view should result in a visible xinerama screen
prop_view_xinerama (x :: T) (n :: NonNegative Int) = i < size x ==> -- prop_view_xinerama (x :: T) (n :: NonNegative Int) = i < size x ==>
M.member i (screens (view i x)) -- M.member i (screens (view i x))
where -- where
i = fromIntegral n -- i = fromIntegral n
-- view is idempotent -- view is idempotent
prop_view_idem (x :: T) r = prop_view_idem (x :: T) r =
@@ -214,21 +212,27 @@ prop_view_idem (x :: T) r =
sz = size x sz = size x
in view i (view i x) == (view i x) in view i (view i x) == (view i x)
-- view is reversible -- view is reversible, though shuffles the order of hidden/visible
prop_view_reversible r (x :: T) = view n (view i x) == x prop_view_reversible r (x :: T) = normal (view n (view i x)) == normal x
where n = tag (current x) where n = tag (workspace $ current x)
sz = size x sz = size x
i = fromIntegral $ r `mod` sz i = fromIntegral $ r `mod` sz
-- normalise workspace list
normal s = s { hidden = sortBy g (hidden s), visible = sortBy f (visible s) }
where
f = \a b -> tag (workspace a) `compare` tag (workspace b)
g = \a b -> tag a `compare` tag b
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- Xinerama -- Xinerama
-- every screen should yield a valid workspace -- every screen should yield a valid workspace
prop_lookupWorkspace (n :: NonNegative Int) (x :: T) = -- prop_lookupWorkspace (n :: NonNegative Int) (x :: T) =
s < M.size (screens x) ==> -- s < M.size (screens x) ==>
fromJust (lookupWorkspace s x) `elem` (map tag $ current x : prev x ++ next x) -- fromJust (lookupWorkspace s x) `elem` (map tag $ current x : prev x ++ next x)
where -- where
s = fromIntegral n -- s = fromIntegral n
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- peek/index -- peek/index
@@ -249,33 +253,12 @@ prop_index_length (x :: T) =
Empty -> length (index x) == 0 Empty -> length (index x) == 0
Node {} -> length (index x) == length list Node {} -> length (index x) == length list
where where
it = stack . current $ x it = stack . workspace . current $ x
list = focus it : left it ++ right it list = focus it : left it ++ right it
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- rotating focus -- rotating focus
-- --
-- Unfortunately, in the presence of wrapping of focus, we don't have a
-- simple identity where focusLeft . focusRight == id, as the focus
-- operations repartition the structure on wrapping.
--
-- Note the issue with equality on Stacks given the wrapping semantics.
--
-- [1,2,3] ++ [4] ++ [5]
--
-- should be equivalent to:
--
-- [] ++ [4] ++ [5,1,2,3]
--
-- However, we can simply normalise the list, taking focus as the head,
-- and the items should be the same.
-- So we normalise the stack on the current workspace.
-- We normalise by moving everything to the 'left' of the focused item,
-- to the right.
-- normal (x :: T) = modify Empty (\c -> case c of
-- Node t ls rs -> Node t [] (rs ++ reverse ls)) x
normal = id
-- master/focus -- master/focus
-- --
@@ -293,8 +276,8 @@ prop_focusWindow_master (n :: NonNegative Int) (x :: T) =
in index (focusWindow (s !! i) x) == index x in index (focusWindow (s !! i) x) == index x
-- shifting focus is trivially reversible -- shifting focus is trivially reversible
prop_focus_left (x :: T) = normal (focusLeft (focusRight x)) == normal x prop_focus_left (x :: T) = (focusLeft (focusRight x)) == x
prop_focus_right (x :: T) = normal (focusRight (focusLeft x)) == normal x prop_focus_right (x :: T) = (focusRight (focusLeft x)) == x
-- focusWindow actually leaves the window focused... -- focusWindow actually leaves the window focused...
prop_focusWindow_works (n :: NonNegative Int) (x :: T) = prop_focusWindow_works (n :: NonNegative Int) (x :: T) =
@@ -302,26 +285,26 @@ prop_focusWindow_works (n :: NonNegative Int) (x :: T) =
Nothing -> True Nothing -> True
Just _ -> let s = index x Just _ -> let s = index x
i = fromIntegral n `mod` length s i = fromIntegral n `mod` length s
in (focus . stack . current) (focusWindow (s !! i) x) == (s !! i) in (focus . stack . workspace . current) (focusWindow (s !! i) x) == (s !! i)
-- rotation through the height of a stack gets us back to the start -- rotation through the height of a stack gets us back to the start
prop_focus_all_l (x :: T) = normal (foldr (const focusLeft) x [1..n]) == normal x prop_focus_all_l (x :: T) = (foldr (const focusLeft) x [1..n]) == x
where n = length (index x) where n = length (index x)
prop_focus_all_r (x :: T) = normal (foldr (const focusRight) x [1..n]) == normal x prop_focus_all_r (x :: T) = (foldr (const focusRight) x [1..n]) == x
where n = length (index x) where n = length (index x)
-- prop_rotate_all (x :: T) = f (f x) == f x -- prop_rotate_all (x :: T) = f (f x) == f x
-- f x' = foldr (\_ y -> rotate GT y) x' [1..n] -- f x' = foldr (\_ y -> rotate GT y) x' [1..n]
-- focus is local to the current workspace -- focus is local to the current workspace
prop_focus_local (x :: T) = hidden (focusRight x) == hidden x prop_focus_local (x :: T) = hidden_spaces (focusRight x) == hidden_spaces x
prop_focusWindow_local (n :: NonNegative Int) (x::T ) = prop_focusWindow_local (n :: NonNegative Int) (x::T ) =
case peek x of case peek x of
Nothing -> True Nothing -> True
Just _ -> let s = index x Just _ -> let s = index x
i = fromIntegral n `mod` length s i = fromIntegral n `mod` length s
in hidden (focusWindow (s !! i) x) == hidden x in hidden_spaces (focusWindow (s !! i) x) == hidden_spaces x
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- member/findIndex -- member/findIndex
@@ -332,7 +315,7 @@ prop_focusWindow_local (n :: NonNegative Int) (x::T ) =
-- --
prop_findIndex (x :: T) = prop_findIndex (x :: T) =
and [ tag w == fromJust (findIndex i x) and [ tag w == fromJust (findIndex i x)
| w <- current x : prev x ++ next x | w <- workspace (current x) : map workspace (visible x) ++ hidden x
, let t = stack w , let t = stack w
, t /= Empty , t /= Empty
, i <- focus (stack w) : left (stack w) ++ right (stack w) , i <- focus (stack w) : left (stack w) ++ right (stack w)
@@ -352,7 +335,7 @@ prop_insert_idem i (x :: T) = insertLeft i x == insertLeft i (insertLeft i x)
prop_insert_duplicate i (x :: T) = member i x ==> insertLeft i x == x prop_insert_duplicate i (x :: T) = member i x ==> insertLeft i x == x
-- push shouldn't change anything but the current workspace -- push shouldn't change anything but the current workspace
prop_insert_local (x :: T) i = not (member i x) ==> hidden x == hidden (insertLeft i x) prop_insert_local (x :: T) i = not (member i x) ==> hidden_spaces x == hidden_spaces (insertLeft i x)
-- Inserting a (unique) list of items into an empty stackset should -- Inserting a (unique) list of items into an empty stackset should
-- result in the last inserted element having focus. -- result in the last inserted element having focus.
@@ -402,7 +385,7 @@ prop_delete_insert (x :: T) =
prop_delete_local (x :: T) = prop_delete_local (x :: T) =
case peek x of case peek x of
Nothing -> True Nothing -> True
Just i -> hidden x == hidden (delete i x) Just i -> hidden_spaces x == hidden_spaces (delete i x)
-- --------------------------------------------------------------------- -- ---------------------------------------------------------------------
-- swap: setting the master window -- swap: setting the master window
@@ -414,10 +397,10 @@ prop_delete_local (x :: T) =
prop_swap_focus (x :: T) prop_swap_focus (x :: T)
= case peek x of = case peek x of
Nothing -> True Nothing -> True
Just f -> focus (stack (current (swap x))) == f Just f -> focus (stack (workspace $ current (swap x))) == f
-- swap is local -- swap is local
prop_swap_local (x :: T) = hidden x == hidden (swap x) prop_swap_local (x :: T) = hidden_spaces x == hidden_spaces (swap x)
-- TODO swap is reversible -- TODO swap is reversible
-- swap is reversible, but involves moving focus back the window with -- swap is reversible, but involves moving focus back the window with
@@ -441,10 +424,10 @@ prop_swap_idempotent (x :: T) = swap (swap x) == swap x
prop_shift_reversible (r :: Int) (x :: T) = prop_shift_reversible (r :: Int) (x :: T) =
let i = fromIntegral $ r `mod` sz let i = fromIntegral $ r `mod` sz
sz = size y sz = size y
n = tag (current y) n = tag (workspace $ current y)
in case peek y of in case peek y of
Nothing -> True Nothing -> True
Just _ -> (view n . shift n . view i . shift i) y == y Just _ -> normal ((view n . shift n . view i . shift i) y) == normal y
where where
y = swap x y = swap x
@@ -499,11 +482,11 @@ main = do
,("view : invariant" , mytest prop_view_I) ,("view : invariant" , mytest prop_view_I)
,("view sets current" , mytest prop_view_current) ,("view sets current" , mytest prop_view_current)
,("view idempotent" , mytest prop_view_idem) ,("view idempotent" , mytest prop_view_idem)
,("view reviersible" , mytest prop_view_reversible) ,("view reversible" , mytest prop_view_reversible)
,("view / xinerama" , mytest prop_view_xinerama) -- ,("view / xinerama" , mytest prop_view_xinerama)
,("view is local" , mytest prop_view_local) ,("view is local" , mytest prop_view_local)
,("valid workspace xinerama", mytest prop_lookupWorkspace) -- ,("valid workspace xinerama", mytest prop_lookupWorkspace)
,("peek/member " , mytest prop_member_peek) ,("peek/member " , mytest prop_member_peek)