Window stacking / raising design
spitzak at gmail.com
Mon Jan 16 19:14:20 PST 2012
Pekka Paalanen suggested I come up with a design for the Wayland
compositor to control window stacking and raising.
I am pretty familiar with the failures of existing window management in
this area and would certainly like to see Wayland do this correctly, and
not copy the mistakes of the past.
I also more fully understand the design goals of Kristian, and so I hope
I have a better intermediate solution, as opposed to my original goal of
putting the client in absolute control. This seems to be a good
compromise that will actually deliver correctly working user interfaces
with overlapping windows, while allowing the compositor final say on things.
The basic idea is that clients must request raises of their windows and
that windows are not reordered except when this happens. However the
raise request contains multiple windows. The compositor can still
overrule this and has a clearer idea what the client is requesting.
COMPOSITOR RAISING WINDOWS WILL NOT WORK
It appears the Wayland compositor is raising windows immediately on
clicks. There are many reasons this is unworkable, at least if Wayland
is to deliver the "glitch free" api that supposedly is wanted:
1. Modern drag & drop api requires that the window is not raised when
the user starts a drag. A timeout or other kludge in the compositor to
detect if a drag starts is pretty ugly, and would delay the raise
response to clicks by this timeout period.
2. Dialog boxes / modal windows / control panels want to remain above
raised windows. The usual attempt to solve this is for the client to
communicate the desired stacking, but existing solutions only allow a
limited and static description. The failure of Gimp to work with
overlapping control panels is entirely due to the inability to
communicate it's desired stacking to the window manager. What is
actually needed is an arbitrary directed acyclic graph of every window
owned by the client, and this graph must be *synchronously* updated in
response to events, including the very click that raises a window. This
is unworkably complex, and the synchronization would produce annoying
slowdown in responses to clicks.
3. The user does not want the windows to raise on some clicks. Currently
overlapping windows are quite useless because the user cannot do the
most trivial operations, such as push a button, without the window
raising. For api's based on X, the select + middle click is really
identical to drag & drop, and therefore selection should also not raise
the window. I think the proper behavior of clients is that they should
*not* raise on click unless the click is in the window border, in a
"dead" area where the click serves no purpose, or it is the start of a
"paint" operation. My design does not enforce this, but it allows it.
I hope it is clear why I am convinced that any plan that involves raises
happening without the approval of the client is unacceptable.
THE FULLSCREEN + PANEL PROBLEM
There is a desire for "fullscreen" windows to obscure "panels". The
current proposal is for setting a window to fullscreen to raise it,
which has the exact same problems as any automatic click to raise.
My proposal is that instead the compositor will *lower* the panels to be
below the topmost fullscreen window. Thus the fullscreen window, and all
windows that are above it, will be above the panel.
In normal use a client will also raise the window and dialog boxes. So
the user will just see the panel and other windows vanish below the
fullscreen window and it's dialog boxes, but will not see any dialog
More formally: all windows have a "role", such as "desktop", "window",
"panel" and "cursor". The compositor is not allowed to change the order
of windows with the same role, except in response to a raise request.
However it is allowed to enforce any rule for "shuffling" these roles
together into the actual window list.
MAPPED / UNMAPPED
I am unclear if Wayland will provide any atomic grouping of client
requests so that the user never sees any intermediate state of these
If not, it looks like the ability to make an "unmapped" window is
needed. This is because the code to add a surface to wayland is complex
and it would otherwise have to be embedded into the "raise" request, so
that new dialogs can be added as part of the raise and not temporarily
appear in the wrong order.
Setting up a surface "unmapped" would have no visual effect until that
window id is used in a raise request.
RAISE NOTIFY EVENT
The compositor can send this to clients to indicate it wants to raise a
particular window for some reason other than a mouse click.
Notice that mouse clicks are just sent normally to the window. The
client decides whether or not it is a raise.
This message from client to compositor indicates that the client wants
windows reordered. It contains the following information:
WINDOW ID: this is the window that is being raised, or null if this
is only a request to obey the DAG.
EVENT ID: the event that this is in response to. It should be a mouse
click or a raise notify. The compositor might ignore the WINDOW ID if it
is not these events or if it predates later responses, however it *must*
still obey the WINDOW LIST.
WINDOW LIST: A list of window id's, all must belong to the client and
be the same "role". This indicates a stacking order from bottom to top.
The compositor *must* end up with the windows in the order specified,
though other windows may be shuffled between or above these.
This list may actually define a Directed Acyclic Graph (DAG) by the
following scheme: if a window id repeats, it indicates a new "branch"
off that window. No ordering is enforced between two branches, then can
be above/below or shuffled together.
For instance if there is a main window W and three dialogs A,B,C, and
the client wants the dialogs to remain above W but does not care what
order they are in, the list would read:
W, A, W, B, W, C
While this indiates that C must be above B:
W, A, W, B, C
FLAGS: binary flags of the same size as the WINDOWS list. Modifies
interpretation of this list (more may be added):
STICK - Indicates that no window can be placed between this window and
the one before it. The purpose is to stick a video playback window
behind a "frame" window and not let other windows mistakenly appear
between them, so the user is unaware there are multiple windows.
UNMAP - Window should vanish from the screen, and the next window in
the list is placed above the previous non-unmapped window
Note that it may be possible to do "STICK" in another call, such as
when one of the surfaces is assigned. If atomic request grouping is
done, it also removes the need for UNMAP. Therefore it may be possible
to eliminate the flags.
More information about the wayland-devel