Cycling through workspaces in multi monitor setup in XMonad

1.3k Views Asked by At

I'm currently using alt + ctrl + left and alt + ctrl + right to cycle between workspaces:

...

    , ((altModMask .|. controlMask, xK_Left),
      prevWS)
    , ((altModMask .|. controlMask, xK_Right),
      nextWS)

This works fine for single monitor setup. However it is a bit confusing when using a dual monitor setup. This is because the workspace to be shown will change screen if currently visible on another screen. For instance if I have ws 1 on screen 0 and ws 2 on screen 1 and have the focus on screen 0:

1:term (2:web) 3:txt

When I now do a nextWS, ws 2, that was currently on screen 1 will be drawn to screen 0, while screen 1 will show ws 1.

(1:term) 2:web 3:txt

What I would like is a behaviour where prexWS and nextWS will jump over the workspace that is currently shown on the other monitor and only select a workspace that is currently not shown.

Are there such commands already or is there some xmonad.hs example that implements this?

2

There are 2 best solutions below

1
On BEST ANSWER

Use XMonad.Actions.DynamicWorkspaceOrder from the xmonad-contrib:

import qualified XMonad.Actions.DynamicWorkspaceOrder as DO

...

    , ((altModMask .|. controlMask, xK_Left),
      DO.moveTo Prev HiddenNonEmptyWS)
    , ((altModMask .|. controlMask, xK_Right),
      DO.moveTo Next HiddenNonEmptyWS)
0
On

Not quite what you asked for, but here's a version of cycleRecentNonEmptyWS that skips over the workspaces that are visible on the other monitors:

import Data.Maybe (isJust)
import XMonad
import qualified XMonad.StackSet as W
import XMonad.Actions.CycleRecentWS (cycleWindowSets, recentWS)

cycleRecentHiddenNonEmptyWS :: [KeySym] -> KeySym -> KeySym -> X ()
cycleRecentHiddenNonEmptyWS mods keyNext keyPrev = do
  pred <- getPred
  cycleWindowSets (recentWS pred) mods keyNext keyPrev
  where
    -- Based on private function @wsTypeToPred@ in
    -- @Xmonad.Actions.CycleWS@. Select workspaces that are non-empty
    -- and not on another screen.
    getPred :: X (WindowSpace -> Bool)
    getPred = withWindowSet $ \ws -> do
      -- The hidden windows don't include the currently focused
      -- window, which means we can't abort once we start cycling. So,
      -- instead, we define "hidden" as "focused or not visible". The
      -- @W.visible@ screens are the non-focused but visible ones.
      let visibles = map (W.tag . W.workspace) $ W.visible ws
      let hidden w = W.tag w `notElem` visibles
      let nonEmpty w = isJust (W.stack w)
      return (\w -> hidden w && nonEmpty w)

You can use it just like cycleRecentNonEmptyWS, e.g. by adding this to your key map:

             -- The 'xK_grave' is backtick ("`"), so tab cycles
             -- forward, and backtick cycles back.
             , ((modm, xK_Tab), cycleRecentHiddenNonEmptyWS [xK_Super_L] xK_Tab xK_grave)