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.
_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to