aboutsummaryrefslogtreecommitdiff
path: root/src/Rahm
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2022-04-17 23:15:55 -0600
committerJosh Rahm <joshuarahm@gmail.com>2022-04-17 23:15:55 -0600
commit9dc562c177fef4ad3b25bfac348c21a6c57839f5 (patch)
tree592287a0d97ac6e6fef9c24846f7575873bf9a0c /src/Rahm
parent4114631cb78f5c21a70ae5fe697b8230e965c1fb (diff)
downloadrde-9dc562c177fef4ad3b25bfac348c21a6c57839f5.tar.gz
rde-9dc562c177fef4ad3b25bfac348c21a6c57839f5.tar.bz2
rde-9dc562c177fef4ad3b25bfac348c21a6c57839f5.zip
Starting to implement window management language
Diffstat (limited to 'src/Rahm')
-rw-r--r--src/Rahm/Desktop/Common.hs (renamed from src/Rahm/Desktop/Lib.hs)37
-rw-r--r--src/Rahm/Desktop/History.hs37
-rw-r--r--src/Rahm/Desktop/Hooks/WindowChange.hs16
-rw-r--r--src/Rahm/Desktop/Keys.hs70
-rw-r--r--src/Rahm/Desktop/Marking.hs127
-rw-r--r--src/Rahm/Desktop/Workspaces.hs38
6 files changed, 203 insertions, 122 deletions
diff --git a/src/Rahm/Desktop/Lib.hs b/src/Rahm/Desktop/Common.hs
index c7cfca4..926d5ff 100644
--- a/src/Rahm/Desktop/Lib.hs
+++ b/src/Rahm/Desktop/Common.hs
@@ -1,7 +1,8 @@
-module Rahm.Desktop.Lib where
+module Rahm.Desktop.Common where
import Prelude hiding ((!!))
+import Control.Monad.Trans.Maybe
import XMonad.Actions.DynamicWorkspaces
import XMonad.Util.Run
import XMonad.Prompt
@@ -14,7 +15,6 @@ import Data.Char
import Data.List hiding ((!!))
import Data.List.Safe ((!!))
import Data.Maybe
-import Rahm.Desktop.Marking
import Text.Printf
import XMonad hiding (workspaces, Screen)
import XMonad.StackSet hiding (filter, focus)
@@ -25,6 +25,23 @@ import Data.Ord (comparing)
import qualified XMonad.StackSet as S
import Rahm.Desktop.Windows
+-- A location is a workspace and maybe a window with that workspace.
+data Location = Location {
+ locationWorkspace :: WorkspaceId,
+ locationWindow :: Maybe Window
+ } deriving (Read, Show, Eq, Ord)
+
+focusLocation :: Location -> X ()
+focusLocation (Location ws Nothing) = windows $ S.greedyView ws
+focusLocation (Location _ (Just win)) = windows $ S.focusWindow win
+
+masterWindow :: MaybeT X Window
+masterWindow = MaybeT $ withWindowSet $ \ss ->
+ let windows = (S.integrate' . S.stack . S.workspace . S.current) ss
+ in case windows of
+ (a:_) -> return $ Just a
+ _ -> return Nothing
+
data WinPrompt = WinPrompt
instance XPrompt WinPrompt where
@@ -55,9 +72,15 @@ askWindowId = do
runDMenuPromptWithMap "Window" (Just "#f542f5") windowTitlesToWinId
windowJump :: X ()
-windowJump = do
- windowId <- askWindowId
+windowJump = mapM_ focus =<< askWindowId
+
+gotoWorkspace :: WorkspaceId -> X ()
+gotoWorkspace wid = do
+ addHiddenWorkspace wid
+ windows $ S.greedyView wid
+
+getCurrentWorkspace :: X WorkspaceId
+getCurrentWorkspace = withWindowSet $
+ \(S.StackSet (S.Screen (S.Workspace t _ _) _ _) _ _ _) -> do
+ return t
- case windowId of
- Nothing -> return ()
- Just wid -> focus wid
diff --git a/src/Rahm/Desktop/History.hs b/src/Rahm/Desktop/History.hs
index 5e15fe6..9195a92 100644
--- a/src/Rahm/Desktop/History.hs
+++ b/src/Rahm/Desktop/History.hs
@@ -9,10 +9,9 @@ import Data.Default
import qualified XMonad.Util.ExtensibleState as XS
import Data.Foldable (toList)
-import Rahm.Desktop.Workspaces (gotoWorkspace)
import Rahm.Desktop.Hooks.WindowChange
+import Rahm.Desktop.Common
import Rahm.Desktop.Logger
-import Rahm.Desktop.Marking
import Data.Sequence (Seq(..))
import qualified Data.Sequence as Seq
@@ -60,6 +59,20 @@ instance ExtensionClass History where
initialValue = def
-- extensionType = PersistentExtension
+pastHistory :: Int -> X (Maybe Location)
+pastHistory i = do
+ History (BoundedSeqZipper _ _ t) <- XS.get
+ return $ t Seq.!? i
+
+getMostRecentLocationInHistory :: X (Maybe Location)
+getMostRecentLocationInHistory = do
+ History z <- XS.get
+ case z of
+ (BoundedSeqZipper _ (_ :|> h) _) -> return $ Just h
+ (BoundedSeqZipper _ _ (t :<| _)) -> return $ Just t
+ _ -> return Nothing
+
+
historyBack :: X ()
historyBack = do
History z <- XS.get
@@ -74,19 +87,19 @@ historyForward = do
mapM_ focusLocation (getZipper z')
XS.put (History z')
-lastWindow :: X (Maybe Location)
-lastWindow = getZipper . zipperBack . currentZipper <$> XS.get
+lastLocation :: X (Maybe Location)
+lastLocation = getZipper . zipperBack . currentZipper <$> XS.get
-jumpToLastLocation :: X ()
-jumpToLastLocation = mapM_ focusLocation =<< lastWindow
+nextLocation :: X (Maybe Location)
+nextLocation = getZipper . zipperForward . currentZipper <$> XS.get
+jumpToLastLocation :: X ()
+jumpToLastLocation = mapM_ focusLocation =<< lastLocation
-historyHook :: Location -> Location -> X ()
-historyHook (Location ws _) l@(Location ws' _) | ws /= ws' = do
+historyHook :: Maybe Location -> Location -> X ()
+historyHook Nothing loc =
+ XS.modify $ \(History z) -> History (pushZipper loc z)
+historyHook (Just (Location ws _)) l@(Location ws' _) | ws /= ws' = do
XS.modify $ \(History z) -> History (pushZipper l z)
historyHook _ _ = return ()
-
-focusLocation :: Location -> X ()
-focusLocation (Location ws Nothing) = gotoWorkspace ws
-focusLocation (Location _ (Just win)) = windows $ W.focusWindow win
diff --git a/src/Rahm/Desktop/Hooks/WindowChange.hs b/src/Rahm/Desktop/Hooks/WindowChange.hs
index 0038f47..ec8e445 100644
--- a/src/Rahm/Desktop/Hooks/WindowChange.hs
+++ b/src/Rahm/Desktop/Hooks/WindowChange.hs
@@ -4,13 +4,10 @@ import XMonad
import Control.Monad
import qualified XMonad.Util.ExtensibleState as XS
import Data.Default
-import Rahm.Desktop.Workspaces
+import Rahm.Desktop.Common
import qualified XMonad.StackSet as W
-data Location = Location WorkspaceId (Maybe Window)
- deriving (Read, Show, Eq)
-
newtype LastLocation = LastLocation (Maybe Location)
deriving (Read, Show)
@@ -20,12 +17,14 @@ instance Default LastLocation where
instance ExtensionClass LastLocation where
initialValue = def
extensionType = PersistentExtension
-
+
-- Creates a log hook from the function provided.
--
-- The first argument to the function is the old window, the second argument in
-- the new window.
-withLocationChangeHook :: (Location -> Location -> X ()) -> XConfig l -> XConfig l
+--
+-- If the first window is Nothing, this is the first time XMonad started.
+withLocationChangeHook :: (Maybe Location -> Location -> X ()) -> XConfig l -> XConfig l
withLocationChangeHook fn config =
config {
logHook = do
@@ -36,9 +35,8 @@ withLocationChangeHook fn config =
LastLocation last <- XS.get
- whenJust last $ \lastLocation ->
- when (lastLocation /= currentLocation) $
- fn lastLocation currentLocation
+ when (last /= Just currentLocation) $
+ fn last currentLocation
XS.put $ LastLocation $ Just currentLocation
return ()
diff --git a/src/Rahm/Desktop/Keys.hs b/src/Rahm/Desktop/Keys.hs
index 74960df..1369a17 100644
--- a/src/Rahm/Desktop/Keys.hs
+++ b/src/Rahm/Desktop/Keys.hs
@@ -6,7 +6,6 @@ import Control.Monad.Fix (fix)
import Control.Monad.Loops (iterateWhile)
import Control.Monad.Reader
import Control.Monad.Trans.Class
-import Control.Monad.Trans.Maybe
import Control.Monad.Writer
import Data.Char
import Data.List hiding ((!!))
@@ -49,7 +48,7 @@ import Rahm.Desktop.Layout.Hole (toggleHole)
import Rahm.Desktop.Layout.List (toNextLayout, toPreviousLayout, toFirstLayout, toIndexedLayout)
import Rahm.Desktop.Layout.Pop (togglePop)
import Rahm.Desktop.Layout.Rotate (rotateLayout)
-import Rahm.Desktop.Lib
+import Rahm.Desktop.Common
import Rahm.Desktop.Logger
import Rahm.Desktop.Marking
import Rahm.Desktop.MouseMotion
@@ -142,30 +141,13 @@ keymap = runKeys $ do
bind xK_apostrophe $ do
justMod $
doc "Jumps between marks." $
- mapNextString $ \_ str ->
- case str of
- ['\''] -> jumpToLastLocation
- [ch] | isAlphaNum ch -> jumpToMark ch
- "[" -> historyBack
- "]" -> historyForward
- _ -> return ()
+ mapNextString $ const (mapM_ focusLocation <=< markToLocation)
shiftMod $
- doc "Move the marked windo to the current workspace." $
- mapNextString $ \_ str ->
- case str of
- [ch] | isAlphaNum ch -> do
- ws <- getCurrentWorkspace
- maybe (return ()) (windows . W.shiftWin ws) =<< markToWindow ch
- _ -> return ()
-
- controlMod $
- doc "Swap the current window with a mark." $
- mapNextString $ \_ str ->
- case str of
- -- ['\''] -> swapWithLastMark
- [ch] | isAlphaNum ch -> swapWithMark ch
- _ -> return ()
+ doc "Move the marked window to the current workspace." $
+ mapNextString $ \_ str -> do
+ mapM_ (\loc -> moveLocationToWorkspace loc <$> getCurrentWorkspace)
+ =<< markToLocation str
bind xK_BackSpace $ do
-- The only raw keybinding. Meant to get a terminal to unbrick XMonad if
@@ -275,23 +257,6 @@ keymap = runKeys $ do
sendMessage flipHorizontally
bind xK_g $ do
- let selectWorkspace :: (KeySym, String) -> Maybe (X WorkspaceId)
- selectWorkspace s = case s of
- (_, [ch]) | isAlphaNum ch || ch == '*' -> Just $ return [ch]
- (_, "]") -> Just $ adjacentWorkspaceNotVisible next
- =<< getCurrentWorkspace
- (_, "[") -> Just $ adjacentWorkspaceNotVisible prev
- =<< getCurrentWorkspace
- (_, ")") -> Just $ adjacentWorkspace next =<< getCurrentWorkspace
- (_, "(") -> Just $ adjacentWorkspace prev =<< getCurrentWorkspace
- (_, "}") -> Just $ adjacentScreen next
- (_, "{") -> Just $ adjacentScreen prev
- (_, "^") -> Just firstWorkspaceId
- (_, "$") -> Just lastWorkspaceId
- (_, "/") -> Just $ fromMaybe <$> getCurrentWorkspace <*> runMaybeT (
- (MaybeT . workspaceWithWindow) =<< MaybeT askWindowId)
- (_, " ") -> Just $ accompaningWorkspace <$> getCurrentWorkspace
- _ -> Nothing
justMod $
doc "Goto/Send/Etc To a workspace\n\n\t\
@@ -315,7 +280,7 @@ keymap = runKeys $ do
\Other keybindings starting with H-g\n\t\t\
\F1: display this help.\n\n\t" $
mapNextStringWithKeysym $ \_ keysym str ->
- case ((keysym, str), selectWorkspace (keysym, str)) of
+ case ((keysym, str), selectWorkspace str) of
(_, Just w) -> gotoWorkspace =<< w
-- Test binding. Tests that I can still submap keysyms alone (keys
-- where XLookupString won't return anything helpful.)
@@ -328,7 +293,7 @@ keymap = runKeys $ do
shiftMod $
doc "Move the currently focused window to another workspace" $
mapNextStringWithKeysym $ \_ keysym str ->
- case ((keysym, str), selectWorkspace (keysym, str)) of
+ case ((keysym, str), selectWorkspace str) of
(_, Just w) -> shiftToWorkspace =<< w
((_, "_"), _) -> CopyWindow.kill1
_ -> return ()
@@ -336,7 +301,7 @@ keymap = runKeys $ do
controlMod $
doc "Move the current focused window to another workspace and view that workspace" $
mapNextStringWithKeysym $ \_ keysym str ->
- case ((keysym, str), selectWorkspace (keysym, str)) of
+ case ((keysym, str), selectWorkspace str) of
(_, Just w) -> do
ws <- w
shiftToWorkspace ws
@@ -346,14 +311,14 @@ keymap = runKeys $ do
altMod $
doc "Copy a window to the given workspace" $
mapNextStringWithKeysym $ \_ keysym str ->
- case ((keysym, str), selectWorkspace (keysym, str)) of
+ case ((keysym, str), selectWorkspace str) of
(_, Just ws) -> windows . CopyWindow.copy =<< ws
_ -> return ()
shiftAltMod $
doc "Swap this workspace with another workspace (rename)." $
mapNextStringWithKeysym $ \_ keysym str ->
- case ((keysym, str), selectWorkspace (keysym, str)) of
+ case ((keysym, str), selectWorkspace str) of
(_, Just ws) -> swapWorkspace =<< ws
((_, "_"), _) ->
mapM_ (\w -> focus w >> CopyWindow.kill1) =<< windowsInCurrentWorkspace
@@ -419,7 +384,7 @@ keymap = runKeys $ do
doc "Mark the current window with the next typed character." $
mapNextString $ \_ str ->
case str of
- [ch] | isAlphaNum ch -> markCurrentWindow ch
+ [ch] | isAlpha ch -> markCurrentWindow str
_ -> return ()
bind xK_plus $ do
@@ -452,6 +417,17 @@ keymap = runKeys $ do
sendMessage rotateLayout
bind xK_s $ do
+ justMod $
+ mapNextString $ \_ mark -> do
+ loc' <- markToLocation mark
+ case loc' of
+ Nothing -> return ()
+ Just loc -> do
+ mapM_ setAlternateWindow (locationWindow loc)
+ mapNextString $ \_ ws -> do
+ mapM_ (moveLocationToWorkspace loc=<<) (selectWorkspace ws)
+
+
altMod $ spawnX "sudo -A systemctl suspend && xsecurelock"
bind xK_space $ do
diff --git a/src/Rahm/Desktop/Marking.hs b/src/Rahm/Desktop/Marking.hs
index b1783cc..5caaa3b 100644
--- a/src/Rahm/Desktop/Marking.hs
+++ b/src/Rahm/Desktop/Marking.hs
@@ -1,44 +1,50 @@
module Rahm.Desktop.Marking (
markCurrentWindow,
jumpToMark,
- swapWithMark, markToWindow
+ markToLocation,
+ moveLocationToWorkspace,
+ setAlternateWindow,
+ getAlternateWindow
) where
-
-import Rahm.Desktop.Windows (mapWindows, findWindow, getLocationWorkspace)
-import XMonad
-import XMonad.StackSet hiding (focus)
+import Data.Ord (Down(..))
+import Control.Exception
+import Control.Monad (when)
+import Control.Monad.Trans.Maybe
+import Data.Char (isAlpha, isDigit, ord)
import Data.IORef
+import Data.List (sortOn, sort, sortBy, find)
import Data.Map (Map)
-import Control.Monad (when)
-
+import Data.Maybe (catMaybes)
+import Data.Sequence (Seq(..))
+import Rahm.Desktop.Common
+import Rahm.Desktop.History
+import Rahm.Desktop.Hooks.WindowChange
+import Rahm.Desktop.Windows (mapWindows, findWindow, getLocationWorkspace)
+import Rahm.Desktop.Workspaces
+import System.Environment
import System.FilePath
import System.IO
-import Control.Exception
-import System.Environment
+import XMonad
+import XMonad.StackSet hiding (focus)
+import qualified Data.Map as Map
import qualified Data.Sequence as Seq
-import Data.Sequence (Seq(..))
-
import qualified XMonad.Util.ExtensibleState as XS
-import qualified Data.Map as Map
-
{- Submodule that handles marking windows so they can be jumped back to. -}
-type Mark = Char
+type Mark = String
data MarkState =
MarkState {
- markStateMap :: Map Mark Window
+ markStateMap :: Map Mark Location
+ , alternateWindow :: Maybe Window
} deriving (Read, Show)
instance ExtensionClass MarkState where
- initialValue = MarkState Map.empty
+ initialValue = MarkState Map.empty Nothing
extensionType = PersistentExtension
-withMaybeFocused :: (Maybe Window -> X a) -> X a
-withMaybeFocused f = withWindowSet $ f . peek
-
-- greedyFocus :: Window -> X ()
-- greedyFocus win = do
-- ws <- withWindowSet $ \ss ->
@@ -47,20 +53,34 @@ withMaybeFocused f = withWindowSet $ f . peek
-- mapM_ (windows . greedyView . tag) ws
-- focus win
+setAlternateWindow :: Window -> X ()
+setAlternateWindow win = XS.modify (\m -> m { alternateWindow = Just win })
+
+getAlternateWindow :: MaybeT X Window
+getAlternateWindow = MaybeT $ alternateWindow <$> XS.get
+
+withMaybeFocused :: (Maybe Window -> X a) -> X a
+withMaybeFocused f = withWindowSet $ f . peek
+
+getCurrentLocation :: X Location
+getCurrentLocation =
+ (\ws -> withMaybeFocused (return . Location ws)) =<< getCurrentWorkspace
+
+
markCurrentWindow :: Mark -> X ()
markCurrentWindow mark = do
+ ws <- getCurrentWorkspace
+
withFocused $ \win ->
XS.modify $ \state@MarkState {markStateMap = ms} ->
state {
- markStateMap = Map.insert mark win ms
+ markStateMap = Map.insert mark (Location ws $ Just win) ms
}
jumpToMark :: Mark -> X ()
jumpToMark mark = do
MarkState {markStateMap = m} <- XS.get
- case Map.lookup mark m of
- Nothing -> return ()
- Just w -> windows $ focusWindow w
+ mapM_ focusLocation $ Map.lookup mark m
setFocusedWindow :: a -> StackSet i l a s sd -> StackSet i l a s sd
setFocusedWindow
@@ -81,16 +101,51 @@ swapWithFocused winToSwap stackSet =
mapWindows (
\w -> if w == winToSwap then focused else w) stackSet
-markToWindow :: Mark -> X (Maybe Window)
-markToWindow m = do
- MarkState { markStateMap = mp } <- XS.get
- return $ Map.lookup m mp
-
-swapWithMark :: Mark -> X ()
-swapWithMark mark = do
- MarkState {markStateMap = m} <- XS.get
-
- case Map.lookup mark m of
- Nothing -> return ()
- Just winToSwap -> do
- windows $ swapWithFocused winToSwap
+moveLocationToWorkspace :: Location -> WorkspaceId -> X ()
+moveLocationToWorkspace (Location _ (Just win)) wid =
+ windows $ shiftWin wid win
+moveLocationToWorkspace _ _ = return ()
+
+windowRect :: Window -> X (Maybe Rectangle)
+windowRect win = withDisplay $ \dpy -> (do
+ (_, x, y, w, h, bw, _) <- io $ getGeometry dpy win
+ return $ Just $ Rectangle x y (w + 2 * bw) (h + 2 * bw))
+ `catchX` return Nothing
+
+getWindowsAndRects :: X [(Window, Rectangle)]
+getWindowsAndRects =
+ catMaybes <$> (mapM (\w -> fmap (w,) <$> windowRect w)
+ =<< withWindowSet (return . allWindows))
+
+windowLocation :: Window -> MaybeT X Location
+windowLocation win = do
+ tag <- MaybeT $ withWindowSet $ return . findTag win
+ return (Location tag (Just win))
+
+markToLocation :: Mark -> X (Maybe Location)
+markToLocation mark =
+ case mark of
+ [ch] | isAlpha ch -> Map.lookup mark . markStateMap <$> XS.get
+ "0" -> getMostRecentLocationInHistory
+ [ch] | isDigit ch -> pastHistory (ord ch - 0x30)
+ "." -> Just <$> getCurrentLocation
+ "\"" -> nextLocation
+ "'" -> lastLocation
+ "/" -> runMaybeT $ windowLocation =<< MaybeT askWindowId
+ "^" -> do
+ rects <- sortOn (\(_, Rectangle x _ _ _) -> x) <$> getWindowsAndRects
+ case rects of
+ ((w, _) : _) -> runMaybeT (windowLocation w)
+ _ -> return Nothing
+ "$" -> do
+ rects <- sortOn (Down . \(_, Rectangle x _ _ _) -> x)
+ <$> getWindowsAndRects
+ case rects of
+ ((w, _) : _) -> runMaybeT (windowLocation w)
+ _ -> return Nothing
+
+ "*" -> runMaybeT (windowLocation =<< masterWindow)
+
+ "@" -> runMaybeT (windowLocation =<< getAlternateWindow)
+
+ _ -> return Nothing
diff --git a/src/Rahm/Desktop/Workspaces.hs b/src/Rahm/Desktop/Workspaces.hs
index de481ac..3a26823 100644
--- a/src/Rahm/Desktop/Workspaces.hs
+++ b/src/Rahm/Desktop/Workspaces.hs
@@ -4,16 +4,19 @@ module Rahm.Desktop.Workspaces where
import Prelude hiding ((!!))
+import Control.Monad.Trans.Maybe
import Control.Arrow (second, (&&&))
import qualified XMonad.StackSet as W
import XMonad
import Data.List.Safe ((!!))
+import Rahm.Desktop.Common
+import Rahm.Desktop.History
import XMonad.Actions.DynamicWorkspaces
import Data.List (sortOn, sort, sortBy, find)
import Data.Maybe (mapMaybe, fromMaybe)
-import Data.Char (isUpper, toUpper, toLower)
+import Data.Char (isUpper, toUpper, toLower, isAlphaNum)
newtype Selector = Selector (forall a. (a -> Bool) -> [a] -> Maybe a)
@@ -55,11 +58,6 @@ firstWorkspaceId :: X WorkspaceId
firstWorkspaceId =
W.tag . snd . head <$> withWindowSet (return . getPopulatedWorkspaces)
-getCurrentWorkspace :: X WorkspaceId
-getCurrentWorkspace = withWindowSet $
- \(W.StackSet (W.Screen (W.Workspace t _ _) _ _) _ _ _) -> do
- return t
-
windowsInCurrentWorkspace :: X [Window]
windowsInCurrentWorkspace = withWindowSet $
\(W.StackSet (W.Screen (W.Workspace _ _ s) _ _) _ _ _) -> do
@@ -77,11 +75,6 @@ getHorizontallyOrderedScreens windowSet =
where
screens = (True, W.current windowSet) : map (False,) (W.visible windowSet)
-gotoWorkspace :: WorkspaceId -> X ()
-gotoWorkspace wid = do
- addHiddenWorkspace wid
- windows $ W.greedyView wid
-
shiftToWorkspace :: WorkspaceId -> X ()
shiftToWorkspace t = do
addHiddenWorkspace t
@@ -155,3 +148,26 @@ workspaceWithWindow wid = withWindowSet $ \(W.StackSet c v h _) ->
find (\(W.Workspace _ _ stack) -> wid `elem` W.integrate' stack)
(map W.workspace (c : v) ++ h)
+selectWorkspace :: String -> Maybe (X WorkspaceId)
+selectWorkspace s = case s of
+ [ch] | isAlphaNum ch || ch == '*' -> Just $ return [ch]
+ "]" -> Just $ adjacentWorkspaceNotVisible next
+ =<< getCurrentWorkspace
+ "[" -> Just $ adjacentWorkspaceNotVisible prev
+ =<< getCurrentWorkspace
+ ")" -> Just $ adjacentWorkspace next =<< getCurrentWorkspace
+ "(" -> Just $ adjacentWorkspace prev =<< getCurrentWorkspace
+ "}" -> Just $ adjacentScreen next
+ "{" -> Just $ adjacentScreen prev
+ "^" -> Just firstWorkspaceId
+ "'" -> Just $ do
+ l <- lastLocation
+ case l of
+ Just (Location ws _) -> return ws
+ Nothing -> getCurrentWorkspace
+ "." -> Just getCurrentWorkspace
+ "$" -> Just lastWorkspaceId
+ "/" -> Just $ fromMaybe <$> getCurrentWorkspace <*> runMaybeT (
+ (MaybeT . workspaceWithWindow) =<< MaybeT askWindowId)
+ " " -> Just $ accompaningWorkspace <$> getCurrentWorkspace
+ _ -> Nothing