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