frame callbacks and sub_surfaces

Pekka Paalanen ppaalanen at
Fri Oct 28 08:27:42 UTC 2022

On Wed, 26 Oct 2022 20:56:14 -0700
Joel Winarske <joel.winarske at> wrote:

> When using sub_surfaces what is the expected behavior for frame callbacks
> on a given surface?


frame callbacks will trigger at the compositor's discretion after the
wl_surface update with that frame callback request is applied, and if
the wl_surface is mapped. A wl_surface update is applied on
wl_surface.commit, unless that is postponed by some other extension,
e.g. the wl_surface is a sub-surface in synchronized mode, in which case
the update gets applied when the next parent wl_surface update
committed after the sub-surface update gets applied.

> Say I have the following:
> 1. base surface
> 2. xdg surface referencing base surface
> 3. wl surface A
> 4. subsurface from a and base surface (default settings - none applied)
> 5. wl surface B (set for sync)
> 6. subsurface from wl surface B and wl surface A (above/sync setting)

In other words, you have:
- wl_surface "base", e.g. a top-level
- wl_surface "A" is a sub-surface of "base", synchronized (default by the spec)
- wl_surface "B" is a sub-surface of "A", synchronized

Above/below makes no difference for this, unless it causes one of the
wl_surfaces to be fully occluded. Fully occluded wl_surfaces might not
trigger frame callbacks, because the content would not be seen anyway.

> I find if I use wl_callback_add_listener using "surface A" when working
> with "surface B" I get frame event callbacks of "surface A".  If I use
> wl_callback_add_listener with "surface B" I never see a frame callback.  Do
> I need to explicitly set the state of each subsurface in the stack to
> match, and not rely on "default settings"?
> I tried setting "subsurface from B and surface B" as de-sync and I
> experienced no change in behavior.
> I am at the point of mocking up a minimum repro case to exercise this
> further.  I'm hoping someone can enlighten me.

I did not fully understand what your program is doing, so it's hard to
say. The exact sequence of requests is crucial.

Mind, that for frame callbacks to trigger, the wl_surface must be
mapped first.

If you ask for a frame callback, that frame callback will trigger when
the compositor thinks it's a good time to draw again. Setting a frame
callback is "double-buffered state", which means that for
non-sub-surfaces you must send wl_surface.commit before the
wl_surface.frame request will be acted on.

On synchronized sub-surfaces, you will need a wl_surface.commit on the
sub-surface first, *and* a wl_surface.commit on the parent surface
next. It's actually recursive, so if the parent surface is a
synchronized sub-surface for another wl_surface, then that other wl_surface
needs a commit after the earlier ones as well.

So in your case with the surfaces
  base -> A -> B

You need this sequence to be able to get a frame callback triggered on
wl_surface B:


And also all three surfaces must be already mapped or become mapped in
that sequence.

set_desync() is specified to apply immediately, so this should work too:


But it is still conditional on all three surfaces getting mapped. A
sub-surface cannot be visible unless all its ancestors are visible too.
A surface that is not visible (not mapped, or is fully occluded) likely
does not trigger frame callbacks.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 833 bytes
Desc: OpenPGP digital signature
URL: <>

More information about the wayland-devel mailing list