    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Actions.OnScreen
    -- Description :  Control workspaces on different screens (in xinerama mode).
    -- Copyright   :  (c) 2009 Nils Schweinsberg
    -- License     :  BSD3-style (see LICENSE)
    --
    -- Maintainer  :  Nils Schweinsberg <mail@n-sch.de>
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- Control workspaces on different screens (in xinerama mode).
    --
    -----------------------------------------------------------------------------

    module XMonad.Actions.OnScreen (
        -- * Usage
        -- $usage
          onScreen
        , onScreen'
        , Focus(..)
        , viewOnScreen
        , greedyViewOnScreen
        , onlyOnScreen
        , toggleOnScreen
        , toggleGreedyOnScreen
        ) where

    import XMonad
    import XMonad.Prelude (fromMaybe, guard, empty)
    import XMonad.StackSet hiding (new)

    -- | Focus data definitions
    data Focus = FocusNew                       -- ^ always focus the new screen
               | FocusCurrent                   -- ^ always keep the focus on the current screen
               | FocusTag WorkspaceId           -- ^ always focus tag i on the new stack
               | FocusTagVisible WorkspaceId    -- ^ focus tag i only if workspace with tag i is visible on the old stack

    -- | Run any function that modifies the stack on a given screen. This function
    -- will also need to know which Screen to focus after the function has been
    -- run.
    onScreen :: (WindowSet -> WindowSet) -- ^ function to run
             -> Focus                    -- ^ what to do with the focus
             -> ScreenId                 -- ^ screen id
             -> WindowSet                -- ^ current stack
             -> WindowSet
    onScreen :: (WindowSet -> WindowSet)
    -> Focus -> ScreenId -> WindowSet -> WindowSet
    onScreen WindowSet -> WindowSet
    f Focus
    foc ScreenId
    sc WindowSet
    st = WindowSet -> Maybe WindowSet -> WindowSet
    forall a. a -> Maybe a -> a
    fromMaybe WindowSet
    st (Maybe WindowSet -> WindowSet) -> Maybe WindowSet -> WindowSet
    forall a b. (a -> b) -> a -> b
    $ do
        WorkspaceId
    ws <- ScreenId -> WindowSet -> Maybe WorkspaceId
    forall s i l a sd. Eq s => s -> StackSet i l a s sd -> Maybe i
    lookupWorkspace ScreenId
    sc WindowSet
    st

        let fStack :: WindowSet
    fStack      = WindowSet -> WindowSet
    f (WindowSet -> WindowSet) -> WindowSet -> WindowSet
    forall a b. (a -> b) -> a -> b
    $ WorkspaceId -> WindowSet -> WindowSet
    forall s i l a sd.
    (Eq s, Eq i) =>
    i -> StackSet i l a s sd -> StackSet i l a s sd
    view WorkspaceId
    ws WindowSet
    st

        WindowSet -> Maybe WindowSet
    forall a. a -> Maybe a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (WindowSet -> Maybe WindowSet) -> WindowSet -> Maybe WindowSet
    forall a b. (a -> b) -> a -> b
    $ Focus -> WindowSet -> WindowSet -> WindowSet
    setFocus Focus
    foc WindowSet
    st WindowSet
    fStack

    -- set focus for new stack
    setFocus :: Focus
             -> WindowSet -- ^ old stack
             -> WindowSet -- ^ new stack
             -> WindowSet
    setFocus :: Focus -> WindowSet -> WindowSet -> WindowSet
    setFocus Focus
    FocusNew WindowSet
    _ WindowSet
    new             = WindowSet
    new
    setFocus Focus
    FocusCurrent WindowSet
    old WindowSet
    new        =
        case ScreenId -> WindowSet -> Maybe WorkspaceId
    forall s i l a sd. Eq s => s -> StackSet i l a s sd -> Maybe i
    lookupWorkspace (Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
    -> ScreenId
    forall i l a sid sd. Screen i l a sid sd -> sid
    screen (Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
     -> ScreenId)
    -> Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
    -> ScreenId
    forall a b. (a -> b) -> a -> b
    $ WindowSet
    -> Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
    forall i l a sid sd. StackSet i l a sid sd -> Screen i l a sid sd
    current WindowSet
    old) WindowSet
    new of
             Maybe WorkspaceId
    Nothing -> WindowSet
    new
             Just WorkspaceId
    i -> WorkspaceId -> WindowSet -> WindowSet
    forall s i l a sd.
    (Eq s, Eq i) =>
    i -> StackSet i l a s sd -> StackSet i l a s sd
    view WorkspaceId
    i WindowSet
    new
    setFocus (FocusTag WorkspaceId
    i) WindowSet
    _ WindowSet
    new         = WorkspaceId -> WindowSet -> WindowSet
    forall s i l a sd.
    (Eq s, Eq i) =>
    i -> StackSet i l a s sd -> StackSet i l a s sd
    view WorkspaceId
    i WindowSet
    new
    setFocus (FocusTagVisible WorkspaceId
    i) WindowSet
    old WindowSet
    new =
        if WorkspaceId
    i WorkspaceId -> [WorkspaceId] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` (Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
     -> WorkspaceId)
    -> [Screen
          WorkspaceId (Layout Window) Window ScreenId ScreenDetail]
    -> [WorkspaceId]
    forall a b. (a -> b) -> [a] -> [b]
    map (Workspace WorkspaceId (Layout Window) Window -> WorkspaceId
    forall i l a. Workspace i l a -> i
    tag (Workspace WorkspaceId (Layout Window) Window -> WorkspaceId)
    -> (Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
        -> Workspace WorkspaceId (Layout Window) Window)
    -> Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
    -> WorkspaceId
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
    -> Workspace WorkspaceId (Layout Window) Window
    forall i l a sid sd. Screen i l a sid sd -> Workspace i l a
    workspace) (WindowSet
    -> [Screen
          WorkspaceId (Layout Window) Window ScreenId ScreenDetail]
    forall i l a sid sd. StackSet i l a sid sd -> [Screen i l a sid sd]
    visible WindowSet
    old)
           then Focus -> WindowSet -> WindowSet -> WindowSet
    setFocus (WorkspaceId -> Focus
    FocusTag WorkspaceId
    i) WindowSet
    old WindowSet
    new
           else Focus -> WindowSet -> WindowSet -> WindowSet
    setFocus Focus
    FocusCurrent WindowSet
    old WindowSet
    new

    -- | A variation of @onScreen@ which will take any @X ()@ function and run it
    -- on the given screen.
    -- Warning: This function will change focus even if the function it's supposed
    -- to run doesn't succeed.
    onScreen' :: X ()       -- ^ X function to run
              -> Focus      -- ^ focus
              -> ScreenId   -- ^ screen id
              -> X ()
    onScreen' :: X () -> Focus -> ScreenId -> X ()
    onScreen' X ()
    x Focus
    foc ScreenId
    sc = do
        WindowSet
    st <- (XState -> WindowSet) -> X WindowSet
    forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
    gets XState -> WindowSet
    windowset
        case ScreenId -> WindowSet -> Maybe WorkspaceId
    forall s i l a sd. Eq s => s -> StackSet i l a s sd -> Maybe i
    lookupWorkspace ScreenId
    sc WindowSet
    st of
             Maybe WorkspaceId
    Nothing -> () -> X ()
    forall a. a -> X a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
             Just WorkspaceId
    ws -> do
                 (WindowSet -> WindowSet) -> X ()
    windows ((WindowSet -> WindowSet) -> X ())
    -> (WindowSet -> WindowSet) -> X ()
    forall a b. (a -> b) -> a -> b
    $ WorkspaceId -> WindowSet -> WindowSet
    forall s i l a sd.
    (Eq s, Eq i) =>
    i -> StackSet i l a s sd -> StackSet i l a s sd
    view WorkspaceId
    ws
                 X ()
    x
                 (WindowSet -> WindowSet) -> X ()
    windows ((WindowSet -> WindowSet) -> X ())
    -> (WindowSet -> WindowSet) -> X ()
    forall a b. (a -> b) -> a -> b
    $ Focus -> WindowSet -> WindowSet -> WindowSet
    setFocus Focus
    foc WindowSet
    st

    -- | Switch to workspace @i@ on screen @sc@. If @i@ is visible use @view@ to
    -- switch focus to the workspace @i@.
    viewOnScreen :: ScreenId    -- ^ screen id
                 -> WorkspaceId -- ^ index of the workspace
                 -> WindowSet   -- ^ current stack
                 -> WindowSet
    viewOnScreen :: ScreenId -> WorkspaceId -> WindowSet -> WindowSet
    viewOnScreen ScreenId
    sid WorkspaceId
    i =
        (WindowSet -> WindowSet)
    -> Focus -> ScreenId -> WindowSet -> WindowSet
    onScreen (WorkspaceId -> WindowSet -> WindowSet
    forall s i l a sd.
    (Eq s, Eq i) =>
    i -> StackSet i l a s sd -> StackSet i l a s sd
    view WorkspaceId
    i) (WorkspaceId -> Focus
    FocusTag WorkspaceId
    i) ScreenId
    sid

    -- | Switch to workspace @i@ on screen @sc@. If @i@ is visible use @greedyView@
    -- to switch the current workspace with workspace @i@.
    greedyViewOnScreen :: ScreenId    -- ^ screen id
                       -> WorkspaceId -- ^ index of the workspace
                       -> WindowSet   -- ^ current stack
                       -> WindowSet
    greedyViewOnScreen :: ScreenId -> WorkspaceId -> WindowSet -> WindowSet
    greedyViewOnScreen ScreenId
    sid WorkspaceId
    i =
        (WindowSet -> WindowSet)
    -> Focus -> ScreenId -> WindowSet -> WindowSet
    onScreen (WorkspaceId -> WindowSet -> WindowSet
    forall s i l a sd.
    (Eq s, Eq i) =>
    i -> StackSet i l a s sd -> StackSet i l a s sd
    greedyView WorkspaceId
    i) (WorkspaceId -> Focus
    FocusTagVisible WorkspaceId
    i) ScreenId
    sid

    -- | Switch to workspace @i@ on screen @sc@. If @i@ is visible do nothing.
    onlyOnScreen :: ScreenId    -- ^ screen id
                 -> WorkspaceId -- ^ index of the workspace
                 -> WindowSet   -- ^ current stack
                 -> WindowSet
    onlyOnScreen :: ScreenId -> WorkspaceId -> WindowSet -> WindowSet
    onlyOnScreen ScreenId
    sid WorkspaceId
    i =
        (WindowSet -> WindowSet)
    -> Focus -> ScreenId -> WindowSet -> WindowSet
    onScreen (WorkspaceId -> WindowSet -> WindowSet
    forall s i l a sd.
    (Eq s, Eq i) =>
    i -> StackSet i l a s sd -> StackSet i l a s sd
    view WorkspaceId
    i) Focus
    FocusCurrent ScreenId
    sid

    -- | @toggleOrView@ as in "XMonad.Actions.CycleWS" for @onScreen@ with view
    toggleOnScreen :: ScreenId    -- ^ screen id
                   -> WorkspaceId -- ^ index of the workspace
                   -> WindowSet   -- ^ current stack
                   -> WindowSet
    toggleOnScreen :: ScreenId -> WorkspaceId -> WindowSet -> WindowSet
    toggleOnScreen ScreenId
    sid WorkspaceId
    i =
        (WindowSet -> WindowSet)
    -> Focus -> ScreenId -> WindowSet -> WindowSet
    onScreen ((WorkspaceId -> WindowSet -> WindowSet)
    -> WorkspaceId -> WindowSet -> WindowSet
    toggleOrView' WorkspaceId -> WindowSet -> WindowSet
    forall s i l a sd.
    (Eq s, Eq i) =>
    i -> StackSet i l a s sd -> StackSet i l a s sd
    view WorkspaceId
    i) Focus
    FocusCurrent ScreenId
    sid

    -- | @toggleOrView@ from "XMonad.Actions.CycleWS" for @onScreen@ with greedyView
    toggleGreedyOnScreen :: ScreenId    -- ^ screen id
                         -> WorkspaceId -- ^ index of the workspace
                         -> WindowSet   -- ^ current stack
                         -> WindowSet
    toggleGreedyOnScreen :: ScreenId -> WorkspaceId -> WindowSet -> WindowSet
    toggleGreedyOnScreen ScreenId
    sid WorkspaceId
    i =
        (WindowSet -> WindowSet)
    -> Focus -> ScreenId -> WindowSet -> WindowSet
    onScreen ((WorkspaceId -> WindowSet -> WindowSet)
    -> WorkspaceId -> WindowSet -> WindowSet
    toggleOrView' WorkspaceId -> WindowSet -> WindowSet
    forall s i l a sd.
    (Eq s, Eq i) =>
    i -> StackSet i l a s sd -> StackSet i l a s sd
    greedyView WorkspaceId
    i) Focus
    FocusCurrent ScreenId
    sid

    -- a \"pure\" version of X.A.CycleWS.toggleOrDoSkip
    toggleOrView' :: (WorkspaceId -> WindowSet -> WindowSet)   -- ^ function to run
                  -> WorkspaceId                               -- ^ tag to look for
                  -> WindowSet                                 -- ^ current stackset
                  -> WindowSet
    toggleOrView' :: (WorkspaceId -> WindowSet -> WindowSet)
    -> WorkspaceId -> WindowSet -> WindowSet
    toggleOrView' WorkspaceId -> WindowSet -> WindowSet
    f WorkspaceId
    i WindowSet
    st = WindowSet -> Maybe WindowSet -> WindowSet
    forall a. a -> Maybe a -> a
    fromMaybe (WorkspaceId -> WindowSet -> WindowSet
    f WorkspaceId
    i WindowSet
    st) (Maybe WindowSet -> WindowSet) -> Maybe WindowSet -> WindowSet
    forall a b. (a -> b) -> a -> b
    $ do
        let st' :: [Workspace WorkspaceId (Layout Window) Window]
    st' = WindowSet -> [Workspace WorkspaceId (Layout Window) Window]
    forall i l a sid sd. StackSet i l a sid sd -> [Workspace i l a]
    hidden WindowSet
    st
        -- make sure we actually have to do something
        Bool -> Maybe ()
    forall (f :: * -> *). Alternative f => Bool -> f ()
    guard (Bool -> Maybe ()) -> Bool -> Maybe ()
    forall a b. (a -> b) -> a -> b
    $ WorkspaceId
    i WorkspaceId -> WorkspaceId -> Bool
    forall a. Eq a => a -> a -> Bool
    == (Workspace WorkspaceId (Layout Window) Window -> WorkspaceId
    forall i l a. Workspace i l a -> i
    tag (Workspace WorkspaceId (Layout Window) Window -> WorkspaceId)
    -> (Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
        -> Workspace WorkspaceId (Layout Window) Window)
    -> Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
    -> WorkspaceId
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
    -> Workspace WorkspaceId (Layout Window) Window
    forall i l a sid sd. Screen i l a sid sd -> Workspace i l a
    workspace (Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
     -> WorkspaceId)
    -> Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
    -> WorkspaceId
    forall a b. (a -> b) -> a -> b
    $ WindowSet
    -> Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail
    forall i l a sid sd. StackSet i l a sid sd -> Screen i l a sid sd
    current WindowSet
    st)
        case [Workspace WorkspaceId (Layout Window) Window]
    st' of
          []      -> Maybe WindowSet
    forall a. Maybe a
    forall (f :: * -> *) a. Alternative f => f a
    empty
          (Workspace WorkspaceId (Layout Window) Window
    h : [Workspace WorkspaceId (Layout Window) Window]
    _) -> WindowSet -> Maybe WindowSet
    forall a. a -> Maybe a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (WindowSet -> Maybe WindowSet) -> WindowSet -> Maybe WindowSet
    forall a b. (a -> b) -> a -> b
    $ WorkspaceId -> WindowSet -> WindowSet
    f (Workspace WorkspaceId (Layout Window) Window -> WorkspaceId
    forall i l a. Workspace i l a -> i
    tag Workspace WorkspaceId (Layout Window) Window
    h) WindowSet
    st  -- finally, toggle!

    -- $usage
    --
    -- This module provides an easy way to control, what you see on other screens in
    -- xinerama mode without having to focus them. Put this into your
    -- @xmonad.hs@:
    --
    -- > import XMonad.Actions.OnScreen
    --
    -- Then add the appropriate keybindings, for example replace your current keys
    -- to switch the workspaces with this at the bottom of your keybindings:
    --
    -- >     ++
    -- >     [ ((m .|. modm, k), windows (f i))
    -- >       | (i, k) <- zip (workspaces conf) ([xK_1 .. xK_9] ++ [xK_0])
    -- >       , (f, m) <- [ (viewOnScreen 0, 0)
    -- >                   , (viewOnScreen 1, controlMask)
    -- >                   , (greedyView, controlMask .|. shiftMask) ]
    -- >     ]
    --
    -- This will provide you with the following keybindings:
    --
    --      * modkey + 1-0:
    --      Switch to workspace 1-0 on screen 0
    --
    --      * modkey + control + 1-0:
    --      Switch to workspace 1-0 on screen 1
    --
    --      * modkey + control + shift + 1-0:
    --      Default greedyView behaviour
    --
    --
    -- A more basic version inside the default keybindings would be:
    --
    -- >        , ((modm .|. controlMask, xK_1), windows (viewOnScreen 0 "1"))
    --
    -- where 0 is the first screen and \"1\" the workspace with the tag \"1\".
    --
    -- For detailed instructions on editing your key bindings, see
    -- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.