如何使用 IndependantScreens 布局在所有显示器上切换工作区

How do I switch workspace on all monitors with IndependantScreens layout

我正在尝试复制 PopOs 在多显示器设置中处理工作区的方式

我通过使用 XMonad.Layout.IndependentScreens 和每个屏幕有 10 个工作区

的 XMonad 处理多显示器系统的默认方式部分地到达那里

这有点管用,但现在每个监视器都有彼此独立的工作区。我想更改它,以便当我转到监视器 A 或 B 上的工作区 1 时,其他监视器也转到具有该编号的工作区

我该怎么做?

哎呀,结果有点复杂。没有很好的开箱即用的方法来做到这一点。这是我会怎么做。首先,我想办法在每个屏幕上执行一些操作:

traverseScreens :: Applicative f =>
    (Screen   i l a sid sd -> f (Screen   i l a sid' sd')) ->
    (StackSet i l a sid sd -> f (StackSet i l a sid' sd'))
traverseScreens f (StackSet cur vis hid flt) = pure StackSet
    <*> f cur
    <*> traverse f vis
    <*> pure hid
    <*> pure flt

我们要采取的行动是将该屏幕上的当前工作区换成具有相同 ScreenId 但不同 VirtualWorkspace.

的工作区
-- oof, what is currently named PhysicalWorkspace should really have
-- been named PhysicalWorkspaceId (and similarly for VirtualWorkspace)
type PhysicalWorkspace' = Workspace PhysicalWorkspace (Layout Window) Window
type PhysicalScreen = Screen PhysicalWorkspace (Layout Window) Window ScreenId ScreenDetail

swapVirtualWorkspace ::
    VirtualWorkspace ->
    PhysicalScreen ->
    MaybeT (State [PhysicalWorkspace']) PhysicalScreen
swapVirtualWorkspace vIdNew scr
    | vIdOld == vIdNew = return scr
    | otherwise = do
    (pre, here:post) <- gets (break pIdMatches)
    put (pre ++ [workspace scr] ++ post)
    return scr { workspace = here }
    where
    (sId, vIdOld) = unmarshall . tag . workspace $ scr
    pIdMatches ws = unmarshall (tag ws) == (sId, vIdNew)

(如果你只是想要代码而不在乎理解它,你可以跳过这一段。)这个动作跟踪隐藏工作区的缓慢变化的集合,并且可能会失败(如果没有工作区与当前屏幕请求 VirtualWorkspace)。为了帮助您阅读操作:gets (break pIdMatches) 在当前的隐藏工作区集合中搜索 ScreenIdVirtualWorkspace 与提供的屏幕 ID 和提供的虚拟工作区相匹配的工作区——即 Workspace 我们要放在当前屏幕上。 put (pre ++ [workspace scr] ++ post) 将当前屏幕上的工作区放入隐藏工作区列表,return scr { workspace = here } 将我们找到的工作区放在当前屏幕上;这两个一起有效地在当前屏幕和隐藏工作区列表之间交换工作区。最后,作为一种特殊情况,如果我们已经在查看具有适当 VirtualWorkspace 的工作区,我们(成功地)不会更改任何内容。 (如果没有这个额外的案例,我们可能找不到合适的隐藏工作区,因此将其标记为失败更新。)

现在我们可以编写一个 PhysicalWindowSpace 操作,为每个屏幕调用一次 swapVirtualWorkspace。我们需要小心地将我们以这种方式计算出的隐藏工作区的新集合放回窗口空间。

viewOnAllScreens :: VirtualWorkspace -> WindowSet -> WindowSet
viewOnAllScreens vId ws = case mws' of
    Nothing -> ws
    Just ws' -> ws' { hidden = hidden' }
    where
    swapVirtualWorkspaces = traverseScreens (swapVirtualWorkspace vId) ws
    (mws', hidden') = runState (runMaybeT swapVirtualWorkspaces) (hidden ws)

如果任何交换失败,这会使整个 WindowSet 保持不变——我认为这是一个明智的默认设置,在大多数情况下你不应该出现这种情况。

现在您可以更新您的键绑定以使用 viewOnAllScreensIndependentScreens 文档中的指南如下所示:

keyBindings conf = let modm = modMask conf in fromList $
    {- lots of other keybindings -}
    [((m .|. modm, k), windows $ onCurrentScreen f i)
        | (i, k) <- zip (workspaces' conf) [xK_1 .. xK_9]
        , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]

一个可能的更新可能如下所示:

keyBindings conf = let modm = modMask conf in fromList $
    {- lots of other keybindings -}
    [ ((m .|. modm, k), windows f)
    | (i, k) <- zip (workspaces' conf) [xK_1 .. xK_9]
    , (f, m) <- [ (viewOnAllScreens i, 0)
                , (viewOnAllScreens i . onCurrentScreen shift i, shiftMask)
                ]
    ]

注意事项:我已经检查了上面的类型检查,但还没有测试它的行为。这肯定不是我第一次编写正确类型的错误...

为了完整起见,这里是我用来对上面的内容进行类型检查的导入:

import Control.Monad.State
import Control.Monad.Trans.Maybe
import Data.List
import Data.Map (fromList)
import XMonad hiding (Screen)
import XMonad.StackSet
import XMonad.Layout.IndependentScreens