Surfaces and buffers
Nowhere in the Wayland protocol you will find the word "window"; instead, you'll hear the word "surface" a lot. And if you think about, it makes sense: what do those rectangular things on the screen have in common with those holes in walls that let you see what's outside from the inside? You can, however, easily imagine "windows" as surfaces, sheets of paper-like material, floating above the desktop wallpaper.
But surfaces in Wayland are not only about desktop windows. There are other "floating things" that are surfaces in Wayland as well: popups such as menus, the cursor/pointer, the thing you drag'n'drop, etc. Whether a surface is a window, a cursor, or a drag'n'drop icon is called (and determined by) its role, and we'll get to the roles later. But no matter the role, all the surfaces have a lot of functionality in common: they can display and update their contents, as well as change their size.
The important thing to understand is that you don't draw on a surface directly. You render something to a buffer and then attach that buffer to a surface. If you want to update or re-render something, you render the new image to another buffer (actually, sometimes you can reuse the same one) and then attach that another buffer to the surface; and you repeat these steps for every frame you want to render and display. This brings several advantages compared to rendering directly to the surface: for example, you can render frames in advance or in parallel and then display them at just the right moments. The other big one is that with Wayland, every frame is perfect: you only attach a buffer after the frame is fully rendered; as a result, you don't get any screen tearing.
To create a surface, use the wl_compositor.create_surface
request, and to attach a buffer, use wl_surface.attach
followed by wl_surface.commit
:
struct wl_compositor *compositor = ...;
struct wl_surface *surface = wl_compositor_create_surface(compositor);
struct wl_buffer *buffer = ...;
wl_surface_attach(surface, buffer, 0, 0);
wl_surface_commit(surface);
Here, we use the compositor global object we acquired from the registry. We'll talk about creating the buffer in a second, but for now I'd like to point out that creating a surface is not enough to have it shown on the screen; you need to assign it a role first, and we'll get to that later.