Jérôme Belleman
Home  •  Tools  •  Posts  •  Talks  •  Travels  •  Graphics  •  About Me

Tiling Window Managers: Saving Window Positions

22 Jun 2013

Some stacking window managers restore the position of windows for some applications when they are restarted. What does this mean with tiling window managers?

1 Context

I marvelled at FVWM the day I saw it open all my Firefox windows exactly where I'd left them when I last exited it. They were all restored back into their respective desktops, at the right position, to the pixel. And it's something I immediately missed when I switched to xmonad, where window position aren't (x,y) coordinates but a space in a layout. So I was wondering, how hard could it be to translate – misuse, really – (x,y) coordinates that some programs such as Firefox record to a position in a workspace with a tiling window manager using layouts?

2 An attempt with dwm

2.1 The case of a dynamic tiling window manager such as dwm

In a dynamic tiling window manager, it wouldn't make sense to try and use the (x,y) coordinates to target a given area, container, frame since this has a different meaning according to the layout currently in use. Instead, the idea would be to use the (x,y) position as a code referring to a workspace or – in dwm parlance – tag, which is arguably not as good as what a stacking window manager would achieve in positioning the window to the pixel. But it's the best we can reasonably do.

2.2 Event Flow

It doesn't seem to be possible to record the position right before a window closes because it's already too late when it gets the event notifying the window is being closed. We're therefore better off setting (x,y) each time we receive a ConfigureNotify event to basically correct it throughout its life cycle. Setting (x,y) triggers another ConfigureNotify, so we need to do so sparingly and check if (x,y) isn't already set to something sensible before taking any action.

2.3 Tagging Code

As a first attempt, let's try to set x to the tag bit mask and y to some arbitrary value that says x is a tag (e.g. -666). If y≠-666 there's no tagging information and y should be set to -666 regardless. If y=6 and tags≠x, tags should be set to x because we're dealing with a new window which remembers how it's tagged. We don't want this to mean that the tagging has changed and x hasn't been updated yet as we'll be updating x in the same event whereby tags is updated.

Once y and/or tags have been modified, we probably need to somehow apply the change. It seems that XConfigureWindow(), or its little brother XMoveWindow(), both do the business. They also physically move the window, though, whether you XSync() or not, and that's a problem I've not solved yet.

3 An attempt with Notion

3.1 The case of a manual tiling window manager such as Notion

Unlike with a dynamic tiling window manager, where you have little control over what goes on with areas holding windows, a manual tiling window manager has static frames which you can uniquely refer to. Worse that could happen, is if the frame is destroyed, in which case we'll fall back to the dynamic tiling window manager use case whereby the window will be placed randomly in a specific workspace.

The only promising approach I can currently think of and which I'm trying to implement in Notion is to record the order in which Firefox windows are closed and use this order (or its reverse) to restore their locations. That's not perfect either when windows which are intentionally closed enter into the picture, in which case it's probably impossible to tell whether a window should be restored or whether it's a new one which should be opened in the currently-focused frame.

3.2 Basic Case: Windows Closing Because of a Firefox Shutdown

For instance, assume the following Firefox windows which have been recorded to open as follows:

Window Frame
w0 f0
w1 f1
w2 f2

Suppose, for starters, that Firefox was turned off. The windows will be recorded to have been turned off as follows:

Order Window Frame How?
1 w0 f0 Firefox termination
2 w1 f1 Firefox termination
3 w2 f2 Firefox termination

Next time Firefox is spawned, its windows will be restored in the following frames in this order (or the reverse, doesn't matter):

Order Window Frame
1 w0 f0
2 w1 f1
3 w2 f2

That's exactly what we want. But it's not the only case.

3.3 Harder Case: Windows Closing Intentionally or Because of a Firefox Shutdown

How to tell the difference between a window that closes because Firefox shut down or because we deliberately closed it, in which case we don't want it restored (there's nothing to restore)? Of course, the problem could be shifted by recording all the closes and drop the extra ones upon restoring if there aren't enough windows to restore, but then how do you know when you've restored the last window? In other words, how can you make sure that you won't try to restore a window that's actually new?

Order Window Frame How?
1 w0 f0 Specific close
2 w1 f1 Firefox termination
3 w2 f2 Firefox termination

When restoring, there should only be 2 windows restored. Assuming you'll first do w2, then w1, how do you decide that you shouldn't mistake any new window for w0 and place it in f0 instead of opening it in the currently-focused frame?

3.4 Possible Approximations

Clearly we'll have to approximate.

One might also be wondering whether we want restores to be manual because there would be cases where we simply don't want them e.g. on machines which are restarted once a day. But then that should be a host-specific setting.

Again, this assumes we're recording all closes – regardless of their types – and only sort things out upon restore. Using the above tricks would have worked well otherwise because while there are very few ways we use to spawn Firefox windows (e.g. from a terminal or a Notion key binding – even programs which open Web pages will only cause Firefox to open a new tab, not a new window), there are many ways of closing them (e.g. crashes).

4 References