Sub-surface protocol

Bill Spitzak spitzak at gmail.com
Wed Dec 5 22:45:14 PST 2012


Pekka Paalanen wrote:

> I am currently looking into sub-surfaces, first to sketch the protocol
> extension, and I have some open questions. I decided to write an
> exhaustive document, so we would all be on the same page, and also to
> clarify my own thoughts.

Glad to see this is being worked on. I have been trying to figure this 
out as well, so I have a number of comments and ideas:

The main thing I would do is merge this with the popup/menu/transient 
windows. Subsurfaces are simply child windows that are "stuck" to the 
main one: the shell is not allowed to place another window between them 
and their parent. The reason for this is that everything done to make 
subsurfaces not blink or move incorrectly is just as useful for floating 
menus and transient windows, and everything for handling events and 
grabs for transient windows is useful for subwindows.

In my design this is part of wl_shell_surface, not the wl_surface. There 
is no difference between children and main surfaces, in fact they can 
change from one to the other. The new request would be called set_parent 
and it is done to the "child" wl_shell_surface. The request has the 
following arguments: (all the binary ones would be bits in a single 
integer):

  parent: the parent surface, or None to make this a top-level surface 
(which make it ignore all the other controls). Parent probably has to 
belong to the same client as child. If a loop is made the results are 
undefined but the shell must not crash. It may also be possible to set 
the parent to a "process" object which acts like None but is used by 
panels to group windows into a single indicator.

  below: if true the shell must always make this surface be below the 
parent, if false then the shell must always make this surface be above 
the parent.

  subsurface: if true then the shell must not allow any surface that has 
a different parent from being between this surface and the parent.

  transform: transform between parent surface and this one

  locked: if true then changes to the transforms of the parent are 
applied to this one, so they always move in unison

  adjust: various flags to tell the shell to adjust the transform so the 
child is not clipped by panels and outputs. Matches the current popup 
request.

  grab: various flags to emulate the current popup grab request.

  probably lots of other flags, these can be added without changing the 
protocol version as long as there are less than 32 of them!

The child (and any necessary children of it) is moved up/down in the 
stacking order until it reaches the first position that agrees with the 
rules. The window stacking would not change until a commit is done to 
the parent (or the child if parent is None). If there was an old parent 
then a commit has to be done to the old parent too.

A "raise" of a surface would find every "above" child plus every "below" 
&& "subsurface" child, and put them all as high in the main surface's 
layer as possible without changing their current order and without 
changing the order of any other surfaces.

"Raise" is a request from the client (the shell can only send a 
raise-requested event to the client). The effects of raise are deferred 
until a commit on the surface, and are mixed with any set_parent 
requests the client does before the commit. The main purpose is so that 
a transient window can appear to belong to more than one main window, by 
the client changing it's parent to the raised one. This seems to be far 
easier than supporting an arbitrary DAG of child windows.

> Clipping

Subsurfaces are exactly like transient windows and thus are not clipped 
by the parent, exactly like you propose.

> For instance, drawing window decorations in four sub-surfaces around
> the main content would not only waste memory, but also not solve the
> problem of GL applications, where one needs to reserve a margin for the
> decorations. (Hello, window decoration libraries? :-)

Did you make a typo? It sounds like 4 surrounding surfaces *will* solve 
the problems for GL applications, right?

The shell needs to pretend the window is all of these together. I think 
this has some implications for how the current maximize and fullscreen 
work. My preference is that the client can just tell it to act like the 
main window is a particular size, while the buffer is a smaller 
rectangle. I also want this to allow maximize and docking to clip off 
"edges" but not require the client to allocate buffer space or draw the 
clipped edges.

> Committing changes

I think it may work that a commit on a parent is an implied commit on 
all the children. To make a set of child surfaces all resize in unison, 
change them all but don't call commit on any, and call commit on the 
main window after all are updated.

An async video player process could call commit on the child surface for 
it's changes as the frames play. It could even change the resolution by 
changing the local transform of the surface, though it has to result in 
the same-sized rectangle as before.

It is possible it may be very difficult for toolkits to be written to 
defer the commits for the window pieces. In that case some of your ideas 
may be necessary.

> Map and unmap

I unmap children when the parent is destroyed or unmapped. Although 
"unmapped state" is necessary, it is entirely a shell internal setting. 
Clients can only make windows disappear by destroying surfaces.

If a client wants a transient child to remain when the parent window 
goes away, it sets the parent to None *before* destroying the parent.

If a client wants an unmapped surface to reappear, it can set the parent 
to a mapped surface or None.

> Sub-surface configuration/geometry control
> 
> All operations about positioning and sizing sub-surfaces take place in
> the parent surface's coordinate frame, and are limited to integer
> pixels. No fractional pixels are allowed, and no transformations apart
> from translation and scaling(?) are supported.

I suspect 90 degree rotations and reflections will be required to get 
some video decoders to display on current wayland output configurations. 
But I see no reason not to support arbitrary transforms. If they are 
slow then clients won't use them but the compositor already handles them 
so this saves no code.

> We probably need a wl_subsurface.set_position(x, y) request.

This is done by the transform in a set-parent request. All the arguments 
except the transform are the same.

> Should we have a request, that can set a different size for a
> sub-surface, apart from the wl_buffer size?

This would be handled by the scale in the transform.

> Stacking order

Stacking order is controlled by making children of children.

If more than one child has the same parent, this is an indication that 
the client does not care about the order. For subsurfaces this is 
probably because they do not overlap. For transient windows this means 
they can be raised above each other.

Because set-parent can be called at any time the stacking order can 
change dynamically.

> Protocol sketch
> 
> My current idea is below. The wl_subsurface object is an additional
> interface to the wl_surface object it was created for (not the parent).
> It is similar to how wl_shell_surface is an additional interface into
> wl_surface.

In my proposal it *is* a wl_shell_surface and this is done by the shell.

> If the parent wl_surface is destroyed, the sub-surfaces will become
> inert. The only effective request for inert objects is destroy.

In my proposal you can get the subsurfaces back by setting the parent to 
a mapped surface or to None.

> What should happen on wl_subsurface.get_subsurface if:

> - the parent is already a sub-surface itself?
This is how I was controlling stacking order.

> - the surface is already a sub-surface of a different parent?
> - the surface is already a sub-surface of the same parent?
Such rearrangement must be allowed or it will be impossible to make some 
ui effects without the surface "blinking".

> - the surface has already been given a role, like pointer image or
>   top-level window?
The role is forced to be that of the parent surface.

> Should these be fatal errors, non-fatal errors, or allowed use?
> Non-fatal errors would be signalled with an additional event in
> wl_subsurface, that says it is now inert due to an error.

The only error I can think of with mine is if the client makes a loop of 
parents. This probably should cause a non-fatal error and be fixed by 
using None instead of the parent passed to the set-parent.

> I have not even thought about sub-surfaces' implications to input
> handling or the shell yet. Sub-surfaces probably need to be able to
> receive input. The shell perhaps needs a bounding box of the set of
> surfaces to be able to pick an initial position for the window, etc.

Everything done for transient windows (such as grabs) would be reused.


More information about the wayland-devel mailing list