help with layout modifiers

Hi, this is something I'm not able to solve without some help, I think. Suppose I want to implement the mouse focus with a layout modifier: now XMobar changes the focused window when you enter it with the pointer, and this is done in Main.handle. It would be nice to do that at the layout level, with modifyLayout returning a new layout when a crossing event is generated. In order not to bother layout writers, it should be possible to implement the mouse focus as a layout modifier, which add a hook to a specific layout event handler. While this approach (code below) works in general, it doesn't work with layouts that add other X event hooks, like Tabbed, or DragPane. The problem is that, when I modify the windows stack in the event handler below, I don't have a clear idea of how the XState should be modified. I think I just need some direction...;-) To test the code below you need to comment out from Main.hs this function: handle e@(CrossingEvent {ev_window = w, ev_event_type = t}) | t == enterNotify && ev_mode e == notifyNormal && ev_detail e /= notifyInferior = focus w And this is the example code. You use it like this, in your Config.hs: defaultLayouts = [ mouseFocus $ dragUpDownPane "dd" 0.1 0.5 , mouseFocus $ tabbed shrinkText defaultTConf , mouseFocus tiled ] mouseFocus :: Layout a -> Layout a mouseFocus cl@(Layout {doLayout = dl , modifyLayout = ml}) = Layout {doLayout = doLay , modifyLayout = modLay} where doLay r s = dl r s modLay sm | Just e <- fromMessage sm = do handle_event e ml sm -- right?? | otherwise = ml sm handle_event e@(CrossingEvent {ev_window = w, ev_event_type = t}) | t == enterNotify && ev_mode e == notifyNormal && ev_detail e /= notifyInferior = do focus w -- what should I do to XState after this? return () handle_event _ = return () Thanks for your kind attention. Andrea

On Wed, Aug 29, 2007 at 12:48:15PM +0200, Andrea Rossato wrote:
And this is the example code. You use it like this, in your Config.hs:
defaultLayouts = [ mouseFocus $ dragUpDownPane "dd" 0.1 0.5 , mouseFocus $ tabbed shrinkText defaultTConf , mouseFocus tiled ]
mouseFocus :: Layout a -> Layout a mouseFocus cl@(Layout {doLayout = dl , modifyLayout = ml}) = Layout {doLayout = doLay , modifyLayout = modLay} where doLay r s = dl r s modLay sm | Just e <- fromMessage sm = do handle_event e ml sm -- right?? | otherwise = ml sm
handle_event e@(CrossingEvent {ev_window = w, ev_event_type = t}) | t == enterNotify && ev_mode e == notifyNormal && ev_detail e /= notifyInferior = do focus w -- what should I do to XState after this? return () handle_event _ = return ()
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 () -- David Roundy http://www.darcs.net

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. In other words, Decoration stops working correctly. The problem is that this code produces the very same issue as the one I submitted before. I would like to understand where I'm doing something wrong. Thanks for your kind attention. Andrea

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) } refresh 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 http://www.darcs.net

On Wed, Aug 29, 2007 at 12:16:35PM -0400, David Roundy wrote:
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) } refresh
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.
I had the feeling that I was hitting a limit but I could not track down its origin. Now I understand the problem. Thanks for your time. I hoped it could be possible to modify the layout, refresh and return the modified layout *within* modifyLayout... I think I'll try to find a solution myself. I doubt I'll actually find it... Andrea
participants (2)
-
Andrea Rossato
-
David Roundy