Wayland xdg-shell Startup: Commit an Empty Surface Before Attaching the First Buffer
A native Wayland HMI creates a toplevel and immediately attaches its first image. Instead of showing the window, the compositor reports an unconfigured-buffer error. The missing step is the initial shell negotiation: creating the role does not yet authorize the first buffer-bearing update.
This walkthrough pins the stable xdg-shell XML in wayland-protocols 1.36. It is intended for a native client or custom platform backend. Ordinary Qt application code normally delegates this handshake to its Wayland platform integration rather than issuing these protocol requests itself.
Start with an unbuffered surface and a role
An xdg_surface provides shell-related state for a wl_surface, but creating it does not assign a specific role. The client must create a role-specific object, such as xdg_toplevel, before other requests on the xdg_surface.
The underlying surface must not already have a buffer attached or committed when the xdg_surface is created. Attempts to attach or manipulate a buffer before the first xdg_surface.configure are also errors. Keeping a ready-to-render image in application memory is fine; publishing it into the protocol too early is the problem.
After creating the role and setting initial state, make an initial wl_surface.commit with no buffer attached. This is a real protocol transition even though there are no pixels to display. It gives the compositor the role and initial state needed to send its configuration sequence.
Accumulate the complete configure sequence
Role-specific configuration events can carry state before the final xdg_surface.configure. That final event ends the sequence and supplies its serial. Treat the preceding role state and this serial as one configuration record instead of independently reacting to each intermediate field as if it were a complete transaction.
Once the initial configuration arrives, acknowledge its serial using xdg_surface.ack_configure. Then attach the prepared response buffer and commit the surface state that responds to that configuration. The acknowledgement itself neither attaches pixels nor makes the surface visible.
A successful mapping requires a role, committed shell and role-dependent state, and a committed buffer. Even a mapped surface is only eligible to be shown by the compositor; mapping is not a guarantee that it is currently visible on an output.

Keep serials tied to their surface and state
The acknowledgement serial is not a locally generated frame number. It must refer to a configure event actually received for that xdg_surface. An arbitrary serial, or one copied from a different surface’s bookkeeping, does not establish a valid response.
Acknowledging a configure consumes its serial and all earlier configure serials for that surface. A duplicate acknowledgement of the same configure is invalid. A later attempt to acknowledge an older configure than the last one already acknowledged is invalid too.
For a synthetic example, suppose configurations with serials 41 and 42 arrive in that order before the client can respond. The client may discard the intermediate state, acknowledge 42, and commit a response to that latest configuration. It must not subsequently acknowledge 41 or repeat the acknowledgement of 42. The numbers here are illustrative labels, not observed compositor output.
Do not implement ordering by assuming serial numbers are globally consecutive application counters. Retain the received sequence and each record’s consumed state for the appropriate surface. The protocol relationship is which configure event was issued and acknowledged, not whether a guessed number looks plausible.
Avoid an unnecessary one-ack, one-commit rule
The protocol does not require an immediate commit after every acknowledgement. A client may acknowledge several received configurations before committing; the last acknowledgement before that commit identifies the configuration to which the commit responds.
Likewise, receiving several configuration sequences before rendering does not require producing a buffer for every intermediate size or state. The client can respond to the latest one. A rigid queue that insists on rendering all obsolete states can add work while making serial handling harder to audit.
This flexibility does not remove the initial handshake or permit reusing a consumed serial. Keep “a response is still pending” separate from “this serial remains available to acknowledge.” Those are distinct fields in a useful client state machine.
Reenter the handshake after unmapping
Committing a null buffer unmaps the toplevel. The role may remain, but the documentation treats the toplevel’s attributes as reset and requires the initial empty-commit sequence again before attaching a buffer to remap it.
Therefore, a remap path cannot simply attach the last image and reuse the previous acknowledged serial. Restore the intended initial role state, make a new bufferless commit, wait for configuration and handle the new response normally. Review hide-and-remap paths alongside first startup instead of treating them as unrelated lifecycle code.
A proposed protocol trace should include valid startup, early attachment, duplicate acknowledgement, acknowledgement of an older consumed configure and remapping without a new handshake. No compositor connection was made here. The acceptance target is a state machine whose requests follow the received configure sequence, with mapping and visibility recorded as separate outcomes.