Wayland Surface Roles and their differences
wl_surface, xdg_surface, xdg_toplevel, xdg_popup, wl_subsurface, aren't all of them just surfaces? What are surface roles and why they are essential to a Wayland compositor.

On This Page
Wayland
For an application to render its contents on your screen, it needs to use the GPUs capabilities. However, giving them direct access to the Kernel would be a questionable endeavor. There are a number of reasons for this, primarily revolving around security and convenience. To have a standardized API for interfacing with the GPU, Linux has a Direct Rendering Manager (DRM) subsystem. There are protocols that abstract this even further, removing the need for clients to interface with the DRM. Most widely adopted protocols as of 2026 are X11 and its successor—Wayland. Wayland servers (referred to as "compositors") are responsible for sending user inputs to appropriate clients, composing client buffers (2D array of pixels) on the correct output, tracking damages (which pixels changed since last frame and need to be re-drawn), and orchestrating clients such that they don't render frames with speed that exceeds system's capabilities.
Protocol Definition
Wayland protocol is defined in a collection of XML files in /usr/share/wayland and /usr/share/wayland-* directories. These files contain interface definitions, requests associated with each, arguments, events emitted, etc. For reference, here's the definition of wl_surface interface
<interface name="wl_surface" version="7">
<description summary="an onscreen surface">
...
</description>
<enum name="error">
...
</enum>
<request name="destroy">
<description summary="delete surface">
Deletes the surface and invalidates its object ID.
</description>
</request>
<!--rest of the requests-->
</interface>
Following this allows the protocol stay, well, a protocol (Meaning it is language independent and can have implementations in essentially any programming language). For further information, check out the high-level protocol.
Buffers and Surfaces
There are two fundamental components to Wayland's rendering mechanism. These are Buffers and Surfaces, governed respectively by wl_buffer and wl_surface interfaces. Buffers, as briefly mentioned above, are two dimensional representation of pixels in a given frame, rendered by the client. Think of buffers as your canvas that you (client) paint on. When your buffer is ready, it must be attached to a surface. Attaching the buffer to a surface is like framing your painting. This is necessary for the compositor to position a client's buffer adequately and track its location later on.
Surface Roles
In Wayland protocol, wl_surface must implement a "role". Most roles are assigned to a surface through the Cross Desktop Group (XDG) Shell mechanism, with the exception of one. There are requests in XDG Shell that are applicable to a surface with any role. Naturally, each role also has access to its exclusive requests. In XDG Shell, surfaces are "assigned" to the xdg_surface interface. You can think of this as a "base role" that is obtained by passing the wl_surface as an argument into get_xdg_surface from the xdg_wm_base interface. This interface provides requests that allow more specific roles to be assigned to a given surface.
Toplevel Surfaces
The "main" (AKA "toplevel") surfaces are frames that display client buffers. This is the role a surface must have to be positioned in the compositor's Space. Toplevel surfaces have access to requests that allow the compositor to move, resize, collapse, maximize and interact with the application window in similar fashion. Toplevel surfaces can optionally have decorations (sometimes, client-defined decorations) around them such as borders or topbars.
Popups
In GUI applications it is often the case that a secondary, smaller surface is used for various purposes. An example that comes to mind is a right-click menu. In Wayland, these surfaces are called "Popups" and are managed by XDG Shell, just like Toplevels. Something unique about popups is that they always have something called a positioner. The purpose of positioners is to make sure that a popup does not go outside of an output's (display's) boundaries. Let's go back to the right-click menu example. Try to right click closer to the top of this page. Notice, how the popup is expanded under the cursor. Now right click at the bottom. The popup is now expanded to the top of the cursor. This is the process that a positioner goes through to unconstrain the popup. Furthermore, popups seldom render without requesting exclusive access to the incoming inputs. This is called grabbing and it forces all input from a wl_seat to be forwarded to the popup until it is dismissed.
Subsurfaces
The last role that a surface can have is a wl_subsurface.
This role is managed outside of XDG Shell, in the core protocol. It is the only surface role
that is defined in the core Wayland Protocol (i.e. wayland.xml).
Subsurfaces are surfaces created by a client and have a buffer separate from the one attached to client's main surface. A common example for its use-case is playing a video. Without subsurfaces, the client would have to decode and bundle the frames of a video along with other elements in the buffer in a single commit, using the CPU resources to decode video pixels into the RAM, then copy them into a GPU-accessible memory where the compositor can access them. This is technically true for any damage in a buffer, however in the case of video players specifically, DMA-BUF (AKA "Buffer Sharing") subsurfaces allow their rendering to be more efficient by decoding the video frames directly to the GPU-accessible memory, skipping the copying step. Furthermore, having subsurfaces lets the compositor know the buffer region that is going to be damaged every frame, making damage tracking more efficient.
Continue Reading

Commit, Committed, or was Committed?
All the ways I have written my commits and the compromises I found
Read Article