MultiToggle with WorkspaceCursors

Hi, I'm trying to get MultiToggle and WorkspaceCursors to play nicely with each other. Ordinarily, MultiToggle lets you apply a layout transformation to the current workspace without affecting the layouts of other workspaces. E.g. if I toggle workspace 1 to Full layout, it won't impact the layouts being used on workspaces 2, 3, etc. I've started using WorkspaceCursors (XMonad.Actions.WorkspaceCursors) to manage independent groups of workspaces, and noticed that if I use it's functions (e.g. modifyLayer) to navigate between workspaces, layout toggle states seem to bleed across workspaces rather than remaining independent per workspace. For example, starting on workspace 1 with my regular Tall layout, I navigate to workspace 2 and toggle it to Full layout. Then I go back to workspace 1 and see that it, too, has been toggled to Full layout. I can't figure out exactly what's causing this, or how to fix it so that workspace layouts toggle independently. I'm hoping someone here sees what I'm missing. I put together a minimal config to reproduce the problem: https://github.com/ivanbrennan/xmonad-testing/commit/2e9541b0937eee31ae7f300... The config provides two groups (A and B) of nine workspaces. group A: 1A 2A 3A 4A 5A 6A 7A 8A 9A group B: 1B 2B 3B 4B 5B 6B 7B 8B 9B Its layoutHook consists of: myLayoutHook = workspaceCursors cursors . avoidStruts . mkToggle1 FULL $ Tall 1 (3/100) (1/2) Keys super+1 .. super+9 use WorkspaceCursors functions to switch between workspaces within the currently active group. Keys super+ctrl+1 .. super+ctrl+2 use WorkspaceCursors functions to switch between groups A and B. Additionally, keys super+meta+1 .. super+meta+9 use traditional StackSet functions to switch between workspaces 1A .. 9A. I added these for comparison, showing that MultiToggle state is recognized per-workspace when using this form of navigation. I can't figure out the root cause. I suspect the most relevant pieces of code from WorkspaceCursors and MultiToggle are the following: https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daa... https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daa... Does anyone know what I might be missing, or how I could debug further to get to the root of the problem? Thanks! Ivan

My guess is that MultiToggle doesn't and can't know about
WorkspaceCursors, and WorkspaceCursors doesn't and can't know that it
needs to duplicate the layout state for each group it introduces, so
any layout state change applies to all groups.
On Fri, Mar 24, 2023 at 11:22 AM ivan
Hi, I'm trying to get MultiToggle and WorkspaceCursors to play nicely with each other.
Ordinarily, MultiToggle lets you apply a layout transformation to the current workspace without affecting the layouts of other workspaces. E.g. if I toggle workspace 1 to Full layout, it won't impact the layouts being used on workspaces 2, 3, etc.
I've started using WorkspaceCursors (XMonad.Actions.WorkspaceCursors) to manage independent groups of workspaces, and noticed that if I use it's functions (e.g. modifyLayer) to navigate between workspaces, layout toggle states seem to bleed across workspaces rather than remaining independent per workspace.
For example, starting on workspace 1 with my regular Tall layout, I navigate to workspace 2 and toggle it to Full layout. Then I go back to workspace 1 and see that it, too, has been toggled to Full layout.
I can't figure out exactly what's causing this, or how to fix it so that workspace layouts toggle independently. I'm hoping someone here sees what I'm missing.
I put together a minimal config to reproduce the problem: https://github.com/ivanbrennan/xmonad-testing/commit/2e9541b0937eee31ae7f300...
The config provides two groups (A and B) of nine workspaces.
group A: 1A 2A 3A 4A 5A 6A 7A 8A 9A group B: 1B 2B 3B 4B 5B 6B 7B 8B 9B
Its layoutHook consists of:
myLayoutHook = workspaceCursors cursors . avoidStruts . mkToggle1 FULL $ Tall 1 (3/100) (1/2)
Keys super+1 .. super+9 use WorkspaceCursors functions to switch between workspaces within the currently active group.
Keys super+ctrl+1 .. super+ctrl+2 use WorkspaceCursors functions to switch between groups A and B.
Additionally, keys super+meta+1 .. super+meta+9 use traditional StackSet functions to switch between workspaces 1A .. 9A. I added these for comparison, showing that MultiToggle state is recognized per-workspace when using this form of navigation.
I can't figure out the root cause. I suspect the most relevant pieces of code from WorkspaceCursors and MultiToggle are the following:
https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daa...
https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daa...
Does anyone know what I might be missing, or how I could debug further to get to the root of the problem?
Thanks! Ivan _______________________________________________ xmonad mailing list xmonad@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad
-- brandon s allbery kf8nh allbery.b@gmail.com

Hi! WorkspaceCursors doesn't introduce any groups, it's just a glorified workspace switcher - underneath all the chrome it uses the standard flat workspaces list. In essense it just defines some helper functions that allow user to imagine that workspaces are arranged as a multidimensional cube and navigate along axes of said cube. But it still does it using the usual StackSet.greedyView function. MultiToggle is irrelevant in this case, the same effect can be seen with the boring Choose layout combinator (i.e. `Tall ||| Full`) - the layout state is "spilling" over to other workspaces. It seems that the problem arises because WorspaceCursors calls `windows $ greedyView` inside LayoutClass.handleMessage function - current workspace is switched at the same time as layout is updated, and Xmonad assigns the updated layout to the new workspace. Here's a reproducer (should work on all configs that have a workspace with id "4"): ``` -- necessary language pragmas {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-} -- imports import qualified XMonad.StackSet as W import XMonad.Layout.LayoutModifier(ModifiedLayout(..), LayoutModifier(handleMess)) import XMonad(Message, X, windows, sendMessage, fromMessage) -- Example LayoutModifier that showcases the issue. -- It does nothing except calling `greedyView "4"` upon reciept of SwitchWorkspace message data BadLayoutModifier a = BadLayoutModifier deriving (Read, Show) data SwitchWorkspace = SwitchWorkspace instance Message SwitchWorkspace instance LayoutModifier BadLayoutModifier a where handleMess BadLayoutModifier m = do case fromMessage m of Just SwitchWorkspace -> do windows $ W.greedyView "4" return $ Just BadLayoutModifier Nothing -> return Nothing -- add to keys (tweak the keybinding to your taste) , ((modm, xK_v), sendMessage SwitchWorkspace) -- prepend BadLayoutModifier to layoutHook layoutHook = ModifiedLayout BadLayoutModifier $ (Tall 1 (3/100) (1/2) ||| Full) ``` Steps to reproduce: 1. Recompile and restart Xmonad. 2. Toggle workspace "2" to "Tall" layout, toggle workspace "4" to "Full" layout. 3. Switch to workspace "2". 4. Press keybinding to send the SwitchWorkspace message (Super-v in my case). 5. Observe that Xmonad switches to workspace "4", and layout on that workspace is now "Tall" instead of "Full" as set up originally. In my opinion WorkspaceCursors doesn't need to be a LayoutModifier at all - current state can be derived from the list of cursors and currently focused workspace, no need to store it somewhere. This will sidestep the problem of calling `greedyView` during layout update. -- Best regards, Platon Pronko PGP 2A62D77A7A2CB94E On 2023-03-24 23:27, Brandon Allbery wrote:
My guess is that MultiToggle doesn't and can't know about WorkspaceCursors, and WorkspaceCursors doesn't and can't know that it needs to duplicate the layout state for each group it introduces, so any layout state change applies to all groups.
On Fri, Mar 24, 2023 at 11:22 AM ivan
wrote: Hi, I'm trying to get MultiToggle and WorkspaceCursors to play nicely with each other.
Ordinarily, MultiToggle lets you apply a layout transformation to the current workspace without affecting the layouts of other workspaces. E.g. if I toggle workspace 1 to Full layout, it won't impact the layouts being used on workspaces 2, 3, etc.
I've started using WorkspaceCursors (XMonad.Actions.WorkspaceCursors) to manage independent groups of workspaces, and noticed that if I use it's functions (e.g. modifyLayer) to navigate between workspaces, layout toggle states seem to bleed across workspaces rather than remaining independent per workspace.
For example, starting on workspace 1 with my regular Tall layout, I navigate to workspace 2 and toggle it to Full layout. Then I go back to workspace 1 and see that it, too, has been toggled to Full layout.
I can't figure out exactly what's causing this, or how to fix it so that workspace layouts toggle independently. I'm hoping someone here sees what I'm missing.
I put together a minimal config to reproduce the problem: https://github.com/ivanbrennan/xmonad-testing/commit/2e9541b0937eee31ae7f300...
The config provides two groups (A and B) of nine workspaces.
group A: 1A 2A 3A 4A 5A 6A 7A 8A 9A group B: 1B 2B 3B 4B 5B 6B 7B 8B 9B
Its layoutHook consists of:
myLayoutHook = workspaceCursors cursors . avoidStruts . mkToggle1 FULL $ Tall 1 (3/100) (1/2)
Keys super+1 .. super+9 use WorkspaceCursors functions to switch between workspaces within the currently active group.
Keys super+ctrl+1 .. super+ctrl+2 use WorkspaceCursors functions to switch between groups A and B.
Additionally, keys super+meta+1 .. super+meta+9 use traditional StackSet functions to switch between workspaces 1A .. 9A. I added these for comparison, showing that MultiToggle state is recognized per-workspace when using this form of navigation.
I can't figure out the root cause. I suspect the most relevant pieces of code from WorkspaceCursors and MultiToggle are the following:
https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daa...
https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daa...
Does anyone know what I might be missing, or how I could debug further to get to the root of the problem?
Thanks! Ivan _______________________________________________ xmonad mailing list xmonad@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad

Amazing! Thank you so much for the clear explanation.
On Sat, Mar 25, 2023 at 4:26 AM Platon Pronko
Hi!
WorkspaceCursors doesn't introduce any groups, it's just a glorified workspace switcher - underneath all the chrome it uses the standard flat workspaces list. In essense it just defines some helper functions that allow user to imagine that workspaces are arranged as a multidimensional cube and navigate along axes of said cube. But it still does it using the usual StackSet.greedyView function.
MultiToggle is irrelevant in this case, the same effect can be seen with the boring Choose layout combinator (i.e. `Tall ||| Full`) - the layout state is "spilling" over to other workspaces.
It seems that the problem arises because WorspaceCursors calls `windows $ greedyView` inside LayoutClass.handleMessage function - current workspace is switched at the same time as layout is updated, and Xmonad assigns the updated layout to the new workspace.
Here's a reproducer (should work on all configs that have a workspace with id "4"):
``` -- necessary language pragmas {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
-- imports import qualified XMonad.StackSet as W import XMonad.Layout.LayoutModifier(ModifiedLayout(..), LayoutModifier(handleMess)) import XMonad(Message, X, windows, sendMessage, fromMessage)
-- Example LayoutModifier that showcases the issue. -- It does nothing except calling `greedyView "4"` upon reciept of SwitchWorkspace message data BadLayoutModifier a = BadLayoutModifier deriving (Read, Show)
data SwitchWorkspace = SwitchWorkspace instance Message SwitchWorkspace
instance LayoutModifier BadLayoutModifier a where handleMess BadLayoutModifier m = do case fromMessage m of Just SwitchWorkspace -> do windows $ W.greedyView "4" return $ Just BadLayoutModifier Nothing -> return Nothing
-- add to keys (tweak the keybinding to your taste) , ((modm, xK_v), sendMessage SwitchWorkspace)
-- prepend BadLayoutModifier to layoutHook layoutHook = ModifiedLayout BadLayoutModifier $ (Tall 1 (3/100) (1/2) ||| Full) ```
Steps to reproduce:
1. Recompile and restart Xmonad. 2. Toggle workspace "2" to "Tall" layout, toggle workspace "4" to "Full" layout. 3. Switch to workspace "2". 4. Press keybinding to send the SwitchWorkspace message (Super-v in my case). 5. Observe that Xmonad switches to workspace "4", and layout on that workspace is now "Tall" instead of "Full" as set up originally.
In my opinion WorkspaceCursors doesn't need to be a LayoutModifier at all - current state can be derived from the list of cursors and currently focused workspace, no need to store it somewhere. This will sidestep the problem of calling `greedyView` during layout update.
-- Best regards, Platon Pronko PGP 2A62D77A7A2CB94E
My guess is that MultiToggle doesn't and can't know about WorkspaceCursors, and WorkspaceCursors doesn't and can't know that it needs to duplicate the layout state for each group it introduces, so any layout state change applies to all groups.
On Fri, Mar 24, 2023 at 11:22 AM ivan
wrote: Hi, I'm trying to get MultiToggle and WorkspaceCursors to play nicely
with each other.
Ordinarily, MultiToggle lets you apply a layout transformation to the
current workspace without affecting the layouts of other workspaces. E.g. if I toggle workspace 1 to Full layout, it won't impact the layouts being used on workspaces 2, 3, etc.
I've started using WorkspaceCursors (XMonad.Actions.WorkspaceCursors)
to manage independent groups of workspaces, and noticed that if I use it's functions (e.g. modifyLayer) to navigate between workspaces, layout toggle states seem to bleed across workspaces rather than remaining independent
For example, starting on workspace 1 with my regular Tall layout, I
I can't figure out exactly what's causing this, or how to fix it so
On 2023-03-24 23:27, Brandon Allbery wrote: per workspace. navigate to workspace 2 and toggle it to Full layout. Then I go back to workspace 1 and see that it, too, has been toggled to Full layout. that workspace layouts toggle independently. I'm hoping someone here sees what I'm missing.
I put together a minimal config to reproduce the problem:
https://github.com/ivanbrennan/xmonad-testing/commit/2e9541b0937eee31ae7f300...
The config provides two groups (A and B) of nine workspaces.
group A: 1A 2A 3A 4A 5A 6A 7A 8A 9A group B: 1B 2B 3B 4B 5B 6B 7B 8B 9B
Its layoutHook consists of:
myLayoutHook = workspaceCursors cursors . avoidStruts . mkToggle1 FULL $ Tall 1 (3/100) (1/2)
Keys super+1 .. super+9 use WorkspaceCursors functions to switch
between workspaces within the currently active group.
Keys super+ctrl+1 .. super+ctrl+2 use WorkspaceCursors functions to
switch between groups A and B.
Additionally, keys super+meta+1 .. super+meta+9 use traditional
StackSet functions to switch between workspaces 1A .. 9A. I added these for comparison, showing that MultiToggle state is recognized per-workspace when using this form of navigation.
I can't figure out the root cause. I suspect the most relevant pieces
of code from WorkspaceCursors and MultiToggle are the following:
https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daa...
https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daa...
Does anyone know what I might be missing, or how I could debug further
to get to the root of the problem?
Thanks! Ivan _______________________________________________ xmonad mailing list xmonad@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad

Following up on this: > In my opinion WorkspaceCursors doesn't need to be a LayoutModifier at all - current state can be derived from the list of cursors and currently focused workspace, no need to store it somewhere. I've been using WorkspaceCursors to switch between (simulated) groups of workspaces, relying on the Cursors state to remember which workspace has focus within each group. So switching to group G will focus whichever workspace was focused last time I visited that group. (Or, if it's the first time I've visited that group, its first workspace gets focus). The Cursors state feels like a natural way to implement that, but it's not the only way. My initial implementation treated StackSet.workspaces as a recency list, and switching to group G meant finding the first workspace in that list that was a member of G. When I started using Cursors state instead, I ran into the problem you explained. For now, I'll revisit my initial approach. Thanks again! On Sat, Mar 25, 2023 at 8:15 AM ivanwrote: > Amazing! Thank you so much for the clear explanation. > > On Sat, Mar 25, 2023 at 4:26 AM Platon Pronko > wrote: > >> Hi! >> >> WorkspaceCursors doesn't introduce any groups, it's just a glorified >> workspace switcher - underneath all the chrome it uses the standard flat >> workspaces list. In essense it just defines some helper functions that >> allow user to imagine that workspaces are arranged as a multidimensional >> cube and navigate along axes of said cube. But it still does it using the >> usual StackSet.greedyView function. >> >> MultiToggle is irrelevant in this case, the same effect can be seen with >> the boring Choose layout combinator (i.e. `Tall ||| Full`) - the layout >> state is "spilling" over to other workspaces. >> >> It seems that the problem arises because WorspaceCursors calls `windows $ >> greedyView` inside LayoutClass.handleMessage function - current workspace >> is switched at the same time as layout is updated, and Xmonad assigns the >> updated layout to the new workspace. >> >> Here's a reproducer (should work on all configs that have a workspace >> with id "4"): >> >> ``` >> -- necessary language pragmas >> {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-} >> >> -- imports >> import qualified XMonad.StackSet as W >> import XMonad.Layout.LayoutModifier(ModifiedLayout(..), >> LayoutModifier(handleMess)) >> import XMonad(Message, X, windows, sendMessage, fromMessage) >> >> -- Example LayoutModifier that showcases the issue. >> -- It does nothing except calling `greedyView "4"` upon reciept of >> SwitchWorkspace message >> data BadLayoutModifier a = BadLayoutModifier deriving (Read, Show) >> >> data SwitchWorkspace = SwitchWorkspace >> instance Message SwitchWorkspace >> >> instance LayoutModifier BadLayoutModifier a where >> handleMess BadLayoutModifier m = do >> case fromMessage m of >> Just SwitchWorkspace -> do >> windows $ W.greedyView "4" >> return $ Just BadLayoutModifier >> Nothing -> return Nothing >> >> -- add to keys (tweak the keybinding to your taste) >> , ((modm, xK_v), sendMessage SwitchWorkspace) >> >> -- prepend BadLayoutModifier to layoutHook >> layoutHook = >> ModifiedLayout BadLayoutModifier $ >> (Tall 1 (3/100) (1/2) ||| Full) >> ``` >> >> Steps to reproduce: >> >> 1. Recompile and restart Xmonad. >> 2. Toggle workspace "2" to "Tall" layout, toggle workspace "4" to "Full" >> layout. >> 3. Switch to workspace "2". >> 4. Press keybinding to send the SwitchWorkspace message (Super-v in my >> case). >> 5. Observe that Xmonad switches to workspace "4", and layout on that >> workspace is now "Tall" instead of "Full" as set up originally. >> >> In my opinion WorkspaceCursors doesn't need to be a LayoutModifier at all >> - current state can be derived from the list of cursors and currently >> focused workspace, no need to store it somewhere. This will sidestep the >> problem of calling `greedyView` during layout update. >> >> -- >> Best regards, >> Platon Pronko >> PGP 2A62D77A7A2CB94E >> >> On 2023-03-24 23:27, Brandon Allbery wrote: >> > My guess is that MultiToggle doesn't and can't know about >> > WorkspaceCursors, and WorkspaceCursors doesn't and can't know that it >> > needs to duplicate the layout state for each group it introduces, so >> > any layout state change applies to all groups. >> > >> > On Fri, Mar 24, 2023 at 11:22 AM ivan wrote: >> >> >> >> Hi, I'm trying to get MultiToggle and WorkspaceCursors to play nicely >> with each other. >> >> >> >> Ordinarily, MultiToggle lets you apply a layout transformation to the >> current workspace without affecting the layouts of other workspaces. E.g. >> if I toggle workspace 1 to Full layout, it won't impact the layouts being >> used on workspaces 2, 3, etc. >> >> >> >> I've started using WorkspaceCursors (XMonad.Actions.WorkspaceCursors) >> to manage independent groups of workspaces, and noticed that if I use it's >> functions (e.g. modifyLayer) to navigate between workspaces, layout toggle >> states seem to bleed across workspaces rather than remaining independent >> per workspace. >> >> >> >> For example, starting on workspace 1 with my regular Tall layout, I >> navigate to workspace 2 and toggle it to Full layout. Then I go back to >> workspace 1 and see that it, too, has been toggled to Full layout. >> >> >> >> I can't figure out exactly what's causing this, or how to fix it so >> that workspace layouts toggle independently. I'm hoping someone here sees >> what I'm missing. >> >> >> >> I put together a minimal config to reproduce the problem: >> >> >> https://github.com/ivanbrennan/xmonad-testing/commit/2e9541b0937eee31ae7f300e130dc55a9c5933af#diff-61bfccbc988708bd118b33f9299c64aa8b3e532e25cc8eaa3b716f53215fb196 >> >> >> >> The config provides two groups (A and B) of nine workspaces. >> >> >> >> group A: 1A 2A 3A 4A 5A 6A 7A 8A 9A >> >> group B: 1B 2B 3B 4B 5B 6B 7B 8B 9B >> >> >> >> Its layoutHook consists of: >> >> >> >> myLayoutHook = >> >> workspaceCursors cursors >> >> . avoidStruts >> >> . mkToggle1 FULL >> >> $ Tall 1 (3/100) (1/2) >> >> >> >> Keys super+1 .. super+9 use WorkspaceCursors functions to switch >> between workspaces within the currently active group. >> >> >> >> Keys super+ctrl+1 .. super+ctrl+2 use WorkspaceCursors functions to >> switch between groups A and B. >> >> >> >> Additionally, keys super+meta+1 .. super+meta+9 use traditional >> StackSet functions to switch between workspaces 1A .. 9A. I added these for >> comparison, showing that MultiToggle state is recognized per-workspace when >> using this form of navigation. >> >> >> >> I can't figure out the root cause. I suspect the most relevant pieces >> of code from WorkspaceCursors and MultiToggle are the following: >> >> >> >> >> https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daae5d02dbf4f/XMonad/Actions/WorkspaceCursors.hs#L204-L215 >> >> >> >> >> https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daae5d02dbf4f/XMonad/Layout/MultiToggle.hs#L193-L218 >> >> >> >> Does anyone know what I might be missing, or how I could debug further >> to get to the root of the problem? >> >> >> >> Thanks! >> >> Ivan >> >> _______________________________________________ >> >> xmonad mailing list >> >> xmonad@haskell.org >> >> http://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad >> > >> > >> > >> >

Yes, you are right, my original analysis was incorrect. WorkspaceCursors does indeed store a tree of cursors, and that is not derivable from the current workspace id. However I still think that storing cursors state in LayoutModifier is incorrect - layout information is local for every workspace, while cursors state should be global, not duplicated per every workspace (I can't prove it, but I definitely feel that there are bugs lurking in WorkspaceCursors because of that duplication). I think you can achieve what you need without using WorkspaceCursors as a layout modifier. You can store cursors state in a global variable and work with it yourself. Something like: cursors = unsafePerformIO $ newIORef $ makeCursors [nominalIds, groupIds] (if you don't like unsafePerformIO, you also store cursors inside XMonad's XState.extensibleState) Unfortunately you won't be able to reuse most of WorkspaceCursors stack management functions, because they are either private or call modifyCursors directly or indirectly. You'll have to copy them and adapt them to work on your cursors state. -- Best regards, Platon Pronko PGP 2A62D77A7A2CB94E On 2023-03-26 09:49, ivan wrote: > Following up on this: > >> In my opinion WorkspaceCursors doesn't need to be a LayoutModifier at all > - current state can be derived from the list of cursors and currently > focused workspace, no need to store it somewhere. > > I've been using WorkspaceCursors to switch between (simulated) groups of > workspaces, relying on the Cursors state to remember which workspace has > focus within each group. So switching to group G will focus whichever > workspace was focused last time I visited that group. (Or, if it's the > first time I've visited that group, its first workspace gets focus). > > The Cursors state feels like a natural way to implement that, but it's not > the only way. My initial implementation treated StackSet.workspaces as a > recency list, and switching to group G meant finding the first workspace in > that list that was a member of G. When I started using Cursors state > instead, I ran into the problem you explained. > > For now, I'll revisit my initial approach. > > Thanks again! > > On Sat, Mar 25, 2023 at 8:15 AM ivanwrote: > >> Amazing! Thank you so much for the clear explanation. >> >> On Sat, Mar 25, 2023 at 4:26 AM Platon Pronko >> wrote: >> >>> Hi! >>> >>> WorkspaceCursors doesn't introduce any groups, it's just a glorified >>> workspace switcher - underneath all the chrome it uses the standard flat >>> workspaces list. In essense it just defines some helper functions that >>> allow user to imagine that workspaces are arranged as a multidimensional >>> cube and navigate along axes of said cube. But it still does it using the >>> usual StackSet.greedyView function. >>> >>> MultiToggle is irrelevant in this case, the same effect can be seen with >>> the boring Choose layout combinator (i.e. `Tall ||| Full`) - the layout >>> state is "spilling" over to other workspaces. >>> >>> It seems that the problem arises because WorspaceCursors calls `windows $ >>> greedyView` inside LayoutClass.handleMessage function - current workspace >>> is switched at the same time as layout is updated, and Xmonad assigns the >>> updated layout to the new workspace. >>> >>> Here's a reproducer (should work on all configs that have a workspace >>> with id "4"): >>> >>> ``` >>> -- necessary language pragmas >>> {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-} >>> >>> -- imports >>> import qualified XMonad.StackSet as W >>> import XMonad.Layout.LayoutModifier(ModifiedLayout(..), >>> LayoutModifier(handleMess)) >>> import XMonad(Message, X, windows, sendMessage, fromMessage) >>> >>> -- Example LayoutModifier that showcases the issue. >>> -- It does nothing except calling `greedyView "4"` upon reciept of >>> SwitchWorkspace message >>> data BadLayoutModifier a = BadLayoutModifier deriving (Read, Show) >>> >>> data SwitchWorkspace = SwitchWorkspace >>> instance Message SwitchWorkspace >>> >>> instance LayoutModifier BadLayoutModifier a where >>> handleMess BadLayoutModifier m = do >>> case fromMessage m of >>> Just SwitchWorkspace -> do >>> windows $ W.greedyView "4" >>> return $ Just BadLayoutModifier >>> Nothing -> return Nothing >>> >>> -- add to keys (tweak the keybinding to your taste) >>> , ((modm, xK_v), sendMessage SwitchWorkspace) >>> >>> -- prepend BadLayoutModifier to layoutHook >>> layoutHook = >>> ModifiedLayout BadLayoutModifier $ >>> (Tall 1 (3/100) (1/2) ||| Full) >>> ``` >>> >>> Steps to reproduce: >>> >>> 1. Recompile and restart Xmonad. >>> 2. Toggle workspace "2" to "Tall" layout, toggle workspace "4" to "Full" >>> layout. >>> 3. Switch to workspace "2". >>> 4. Press keybinding to send the SwitchWorkspace message (Super-v in my >>> case). >>> 5. Observe that Xmonad switches to workspace "4", and layout on that >>> workspace is now "Tall" instead of "Full" as set up originally. >>> >>> In my opinion WorkspaceCursors doesn't need to be a LayoutModifier at all >>> - current state can be derived from the list of cursors and currently >>> focused workspace, no need to store it somewhere. This will sidestep the >>> problem of calling `greedyView` during layout update. >>> >>> -- >>> Best regards, >>> Platon Pronko >>> PGP 2A62D77A7A2CB94E >>> >>> On 2023-03-24 23:27, Brandon Allbery wrote: >>>> My guess is that MultiToggle doesn't and can't know about >>>> WorkspaceCursors, and WorkspaceCursors doesn't and can't know that it >>>> needs to duplicate the layout state for each group it introduces, so >>>> any layout state change applies to all groups. >>>> >>>> On Fri, Mar 24, 2023 at 11:22 AM ivan wrote: >>>>> >>>>> Hi, I'm trying to get MultiToggle and WorkspaceCursors to play nicely >>> with each other. >>>>> >>>>> Ordinarily, MultiToggle lets you apply a layout transformation to the >>> current workspace without affecting the layouts of other workspaces. E.g. >>> if I toggle workspace 1 to Full layout, it won't impact the layouts being >>> used on workspaces 2, 3, etc. >>>>> >>>>> I've started using WorkspaceCursors (XMonad.Actions.WorkspaceCursors) >>> to manage independent groups of workspaces, and noticed that if I use it's >>> functions (e.g. modifyLayer) to navigate between workspaces, layout toggle >>> states seem to bleed across workspaces rather than remaining independent >>> per workspace. >>>>> >>>>> For example, starting on workspace 1 with my regular Tall layout, I >>> navigate to workspace 2 and toggle it to Full layout. Then I go back to >>> workspace 1 and see that it, too, has been toggled to Full layout. >>>>> >>>>> I can't figure out exactly what's causing this, or how to fix it so >>> that workspace layouts toggle independently. I'm hoping someone here sees >>> what I'm missing. >>>>> >>>>> I put together a minimal config to reproduce the problem: >>>>> >>> https://github.com/ivanbrennan/xmonad-testing/commit/2e9541b0937eee31ae7f300e130dc55a9c5933af#diff-61bfccbc988708bd118b33f9299c64aa8b3e532e25cc8eaa3b716f53215fb196 >>>>> >>>>> The config provides two groups (A and B) of nine workspaces. >>>>> >>>>> group A: 1A 2A 3A 4A 5A 6A 7A 8A 9A >>>>> group B: 1B 2B 3B 4B 5B 6B 7B 8B 9B >>>>> >>>>> Its layoutHook consists of: >>>>> >>>>> myLayoutHook = >>>>> workspaceCursors cursors >>>>> . avoidStruts >>>>> . mkToggle1 FULL >>>>> $ Tall 1 (3/100) (1/2) >>>>> >>>>> Keys super+1 .. super+9 use WorkspaceCursors functions to switch >>> between workspaces within the currently active group. >>>>> >>>>> Keys super+ctrl+1 .. super+ctrl+2 use WorkspaceCursors functions to >>> switch between groups A and B. >>>>> >>>>> Additionally, keys super+meta+1 .. super+meta+9 use traditional >>> StackSet functions to switch between workspaces 1A .. 9A. I added these for >>> comparison, showing that MultiToggle state is recognized per-workspace when >>> using this form of navigation. >>>>> >>>>> I can't figure out the root cause. I suspect the most relevant pieces >>> of code from WorkspaceCursors and MultiToggle are the following: >>>>> >>>>> >>> https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daae5d02dbf4f/XMonad/Actions/WorkspaceCursors.hs#L204-L215 >>>>> >>>>> >>> https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daae5d02dbf4f/XMonad/Layout/MultiToggle.hs#L193-L218 >>>>> >>>>> Does anyone know what I might be missing, or how I could debug further >>> to get to the root of the problem? >>>>> >>>>> Thanks! >>>>> Ivan >>>>> _______________________________________________ >>>>> xmonad mailing list >>>>> xmonad@haskell.org >>>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad >>>> >>>> >>>> >>> >> >

That makes sense, thanks again. I've revisited my initial approach, but might also take a stab at storing a cursors-like tree in extensibleState. In case it's of interest, this is a somewhat minimal config demonstrating the approach I'm revisiting: https://github.com/ivanbrennan/xmonad-testing/commit/8ff9e594d55885b0b2915fee71b1cd5f61598aa0#diff-74b618a2356f7943b297a5769de732a94848295a4bd563c68f998eafb843dd36 On Sun, Mar 26, 2023 at 12:21 AM Platon Pronkowrote: > Yes, you are right, my original analysis was incorrect. WorkspaceCursors > does indeed store a tree of cursors, and that is not derivable from the > current workspace id. > > However I still think that storing cursors state in LayoutModifier is > incorrect - layout information is local for every workspace, while cursors > state should be global, not duplicated per every workspace (I can't prove > it, but I definitely feel that there are bugs lurking in WorkspaceCursors > because of that duplication). > > I think you can achieve what you need without using WorkspaceCursors as a > layout modifier. You can store cursors state in a global variable and work > with it yourself. Something like: > > cursors = unsafePerformIO $ newIORef $ makeCursors [nominalIds, groupIds] > > (if you don't like unsafePerformIO, you also store cursors inside XMonad's > XState.extensibleState) > > Unfortunately you won't be able to reuse most of WorkspaceCursors stack > management functions, because they are either private or call modifyCursors > directly or indirectly. You'll have to copy them and adapt them to work on > your cursors state. > > -- > Best regards, > Platon Pronko > PGP 2A62D77A7A2CB94E > > On 2023-03-26 09:49, ivan wrote: > > Following up on this: > > > >> In my opinion WorkspaceCursors doesn't need to be a LayoutModifier at > all > > - current state can be derived from the list of cursors and currently > > focused workspace, no need to store it somewhere. > > > > I've been using WorkspaceCursors to switch between (simulated) groups of > > workspaces, relying on the Cursors state to remember which workspace has > > focus within each group. So switching to group G will focus whichever > > workspace was focused last time I visited that group. (Or, if it's the > > first time I've visited that group, its first workspace gets focus). > > > > The Cursors state feels like a natural way to implement that, but it's > not > > the only way. My initial implementation treated StackSet.workspaces as a > > recency list, and switching to group G meant finding the first workspace > in > > that list that was a member of G. When I started using Cursors state > > instead, I ran into the problem you explained. > > > > For now, I'll revisit my initial approach. > > > > Thanks again! > > > > On Sat, Mar 25, 2023 at 8:15 AM ivan wrote: > > > >> Amazing! Thank you so much for the clear explanation. > >> > >> On Sat, Mar 25, 2023 at 4:26 AM Platon Pronko > >> wrote: > >> > >>> Hi! > >>> > >>> WorkspaceCursors doesn't introduce any groups, it's just a glorified > >>> workspace switcher - underneath all the chrome it uses the standard > flat > >>> workspaces list. In essense it just defines some helper functions that > >>> allow user to imagine that workspaces are arranged as a > multidimensional > >>> cube and navigate along axes of said cube. But it still does it using > the > >>> usual StackSet.greedyView function. > >>> > >>> MultiToggle is irrelevant in this case, the same effect can be seen > with > >>> the boring Choose layout combinator (i.e. `Tall ||| Full`) - the layout > >>> state is "spilling" over to other workspaces. > >>> > >>> It seems that the problem arises because WorspaceCursors calls > `windows $ > >>> greedyView` inside LayoutClass.handleMessage function - current > workspace > >>> is switched at the same time as layout is updated, and Xmonad assigns > the > >>> updated layout to the new workspace. > >>> > >>> Here's a reproducer (should work on all configs that have a workspace > >>> with id "4"): > >>> > >>> ``` > >>> -- necessary language pragmas > >>> {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-} > >>> > >>> -- imports > >>> import qualified XMonad.StackSet as W > >>> import XMonad.Layout.LayoutModifier(ModifiedLayout(..), > >>> LayoutModifier(handleMess)) > >>> import XMonad(Message, X, windows, sendMessage, fromMessage) > >>> > >>> -- Example LayoutModifier that showcases the issue. > >>> -- It does nothing except calling `greedyView "4"` upon reciept of > >>> SwitchWorkspace message > >>> data BadLayoutModifier a = BadLayoutModifier deriving (Read, Show) > >>> > >>> data SwitchWorkspace = SwitchWorkspace > >>> instance Message SwitchWorkspace > >>> > >>> instance LayoutModifier BadLayoutModifier a where > >>> handleMess BadLayoutModifier m = do > >>> case fromMessage m of > >>> Just SwitchWorkspace -> do > >>> windows $ W.greedyView "4" > >>> return $ Just BadLayoutModifier > >>> Nothing -> return Nothing > >>> > >>> -- add to keys (tweak the keybinding to your taste) > >>> , ((modm, xK_v), sendMessage SwitchWorkspace) > >>> > >>> -- prepend BadLayoutModifier to layoutHook > >>> layoutHook = > >>> ModifiedLayout BadLayoutModifier $ > >>> (Tall 1 (3/100) (1/2) ||| Full) > >>> ``` > >>> > >>> Steps to reproduce: > >>> > >>> 1. Recompile and restart Xmonad. > >>> 2. Toggle workspace "2" to "Tall" layout, toggle workspace "4" to > "Full" > >>> layout. > >>> 3. Switch to workspace "2". > >>> 4. Press keybinding to send the SwitchWorkspace message (Super-v in my > >>> case). > >>> 5. Observe that Xmonad switches to workspace "4", and layout on that > >>> workspace is now "Tall" instead of "Full" as set up originally. > >>> > >>> In my opinion WorkspaceCursors doesn't need to be a LayoutModifier at > all > >>> - current state can be derived from the list of cursors and currently > >>> focused workspace, no need to store it somewhere. This will sidestep > the > >>> problem of calling `greedyView` during layout update. > >>> > >>> -- > >>> Best regards, > >>> Platon Pronko > >>> PGP 2A62D77A7A2CB94E > >>> > >>> On 2023-03-24 23:27, Brandon Allbery wrote: > >>>> My guess is that MultiToggle doesn't and can't know about > >>>> WorkspaceCursors, and WorkspaceCursors doesn't and can't know that it > >>>> needs to duplicate the layout state for each group it introduces, so > >>>> any layout state change applies to all groups. > >>>> > >>>> On Fri, Mar 24, 2023 at 11:22 AM ivan wrote: > >>>>> > >>>>> Hi, I'm trying to get MultiToggle and WorkspaceCursors to play nicely > >>> with each other. > >>>>> > >>>>> Ordinarily, MultiToggle lets you apply a layout transformation to the > >>> current workspace without affecting the layouts of other workspaces. > E.g. > >>> if I toggle workspace 1 to Full layout, it won't impact the layouts > being > >>> used on workspaces 2, 3, etc. > >>>>> > >>>>> I've started using WorkspaceCursors (XMonad.Actions.WorkspaceCursors) > >>> to manage independent groups of workspaces, and noticed that if I use > it's > >>> functions (e.g. modifyLayer) to navigate between workspaces, layout > toggle > >>> states seem to bleed across workspaces rather than remaining > independent > >>> per workspace. > >>>>> > >>>>> For example, starting on workspace 1 with my regular Tall layout, I > >>> navigate to workspace 2 and toggle it to Full layout. Then I go back to > >>> workspace 1 and see that it, too, has been toggled to Full layout. > >>>>> > >>>>> I can't figure out exactly what's causing this, or how to fix it so > >>> that workspace layouts toggle independently. I'm hoping someone here > sees > >>> what I'm missing. > >>>>> > >>>>> I put together a minimal config to reproduce the problem: > >>>>> > >>> > https://github.com/ivanbrennan/xmonad-testing/commit/2e9541b0937eee31ae7f300e130dc55a9c5933af#diff-61bfccbc988708bd118b33f9299c64aa8b3e532e25cc8eaa3b716f53215fb196 > >>>>> > >>>>> The config provides two groups (A and B) of nine workspaces. > >>>>> > >>>>> group A: 1A 2A 3A 4A 5A 6A 7A 8A 9A > >>>>> group B: 1B 2B 3B 4B 5B 6B 7B 8B 9B > >>>>> > >>>>> Its layoutHook consists of: > >>>>> > >>>>> myLayoutHook = > >>>>> workspaceCursors cursors > >>>>> . avoidStruts > >>>>> . mkToggle1 FULL > >>>>> $ Tall 1 (3/100) (1/2) > >>>>> > >>>>> Keys super+1 .. super+9 use WorkspaceCursors functions to switch > >>> between workspaces within the currently active group. > >>>>> > >>>>> Keys super+ctrl+1 .. super+ctrl+2 use WorkspaceCursors functions to > >>> switch between groups A and B. > >>>>> > >>>>> Additionally, keys super+meta+1 .. super+meta+9 use traditional > >>> StackSet functions to switch between workspaces 1A .. 9A. I added > these for > >>> comparison, showing that MultiToggle state is recognized per-workspace > when > >>> using this form of navigation. > >>>>> > >>>>> I can't figure out the root cause. I suspect the most relevant pieces > >>> of code from WorkspaceCursors and MultiToggle are the following: > >>>>> > >>>>> > >>> > https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daae5d02dbf4f/XMonad/Actions/WorkspaceCursors.hs#L204-L215 > >>>>> > >>>>> > >>> > https://github.com/xmonad/xmonad-contrib/blob/e60805bd45ca2feb9ef3335d023daae5d02dbf4f/XMonad/Layout/MultiToggle.hs#L193-L218 > >>>>> > >>>>> Does anyone know what I might be missing, or how I could debug > further > >>> to get to the root of the problem? > >>>>> > >>>>> Thanks! > >>>>> Ivan > >>>>> _______________________________________________ > >>>>> xmonad mailing list > >>>>> xmonad@haskell.org > >>>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad > >>>> > >>>> > >>>> > >>> > >> > > >
participants (3)
-
Brandon Allbery
-
ivan
-
Platon Pronko