[Xmonad] help with layout modifiers

David Roundy daveroundy at gmail.com
Wed Aug 29 12:16:35 EDT 2007

On Wed, Aug 29, 2007 at 04:53:24PM +0200, Andrea Rossato wrote:
> On Wed, Aug 29, 2007 at 10:27:48AM -0400, David Roundy wrote:
> > Your doLay is buggy:  when dl returns a modified layout, your mouseFocus
> > "falls off".  Similarly with ml.  I would use LayoutHelpers:
> > 
> > mouseFocus = layoutModifer idModDo modLay
> >   where modLay sm | Just e <- fromMessage sm = handle_event e >> return Nothing
> >         handle_event e@(CrossingEvent {ev_window = w, ev_event_type = t})
> >                | t == enterNotify && ev_mode   e == notifyNormal
> >                                   && ev_detail e /= notifyInferior
> >                    = focus w -- what should I do to XState after this?
> >         handle_event _ = return ()
> This was my first attempt, actually, but it doesn't work as expected.
> For instance, if I use it with DragPane, doClick (mouse dragging)
> stops working. Moreover if I use mod-l or mod-h to resize the panes,
> and after that I change the focus with the mouse - which works as
> expected, as in the previous code -, the decorations will not be
> destroyed anymore if I resize once again. The same with Tabbed.

I see.  The problem is that (focus w) itself calls broadcastMessage, and
somehow the nesting of changes to layouts interacts in a bad way.  :( And I
don't see any way around this (in the few minutes I've devoted to it).
Something's definitely screwy, and it's the layouts code that is
responsible.  :( The trouble is that I don't see any clean way to handle
self-modifying layouts that also want to do strange things in the X monad
(as yours does).  In your case, you've created a layout that needs to
modify other layouts, and we just don't have the infrastructure for this.
One solution would be to introduce a sort of event queue, and your code
could queue up a "focus window" event, which would be handled on later,
when we're not in the process of modifying the layouts.

You can see the problem (yes, I'm spending even more time on this...) in

sendMessage :: Message a => a -> X ()
sendMessage a = do n <- (W.tag . W.workspace . W.current) `fmap` gets windowset
                   Just (l,ls) <- M.lookup n `fmap` gets layouts
                   ml' <- modifyLayout l (SomeMessage a) `catchX` return (Just l)
                   whenJust ml' $ \l' -> do modify $ \s -> s { layouts = M.insert n (l',ls) (layouts s) }

We read the current layout l, then we call modifyLayout, and if that gives
us a new layout, we proceed to update the layout using the return value of
modifyLayout.  But if modifyLayout itself *already* changed the layout,
that change will be lost!

So what we've got is a rather complicated and unchecked constraint on
layout behavior.  :(

My best guess as to a solution would be to enable a simple locking
mechanism such that we can keep layouts from being modified or used while
they're already being modified.
David Roundy

More information about the Xmonad mailing list