Nixers Book Club - Book #3: The Wayland Book - Community & Forums Related Discussions

Users browsing this thread: 1 Guest(s)
venam
Administrators
Here are my notes/reviews of chapter 4-6.

In chapter 4 and 5 we're presented with the concept of global singleton
objects, core globals, that implement features requested by all other
objects, omnipresent in all connection. Basically what the "wayland
compositor" or server usually implements.

We learn that one of them is the object implementing the wl_display,
that has id 1, so it's easy to find it.
It implements a method called get_registry and is used to registering
new objects, removing them, and getting the list of them. So from it we
can learn about all that is living on the server.

For clients the connection to the wl_display is done by selecting a file
descriptor manually or through an environment variable.
On the server, it's about creating that file descriptor and running the
wayland internal loop. A lifecycle as follow:
  • wl_display_create
  • wl_display_add_socket_auto auto create the Unix socket file, we can also specific where the file is create.
  • wl_display_run (run libwayland's internal event loop and block until wl_display_terminate is called.)
  • wl_display_destroy

The server runs the wl_event_loop, obtained using wl_display_get_event_loop.
Then it's all event driven programming, we listen to events we configure
and act on them.
The events are received using a fd that is pollable so we can
possibly even do it ourselves manually using wl_event_loop_get_fd,
wl_event_loop_dispatch, and wl_display_flush_clients.

Clients don't have their own event loops, a simple wait/select on
events suffice.
Code:
while (wl_display_dispatch(display) != -1) {
    /* This space deliberately left blank */
}

When implementing an interface we have to implement its methods. On
the server side, for the wl_wayland, that means we have to handle the
registry to keep a list of the objects.

On the client side, when binding to the registry we have to announce
which interface we implement, so that others become aware of them. Then
the server emits a "global" event for all object, which when we catch will
have objects that exist on the server, their id and the interface and
version they implement.
With that we have the ids and we know which interfaces are implemented
by who, so we can interact with them.

I'm not sure why they go into dissecting the binary protocol. There should
be a parser for this to be in human-readable form, and it's available
by setting the env WAYLAND_DEBUG to 1.

So overall that gives a sort of loop to interact with the objects for clients::
- connect to display
- get wl_registry from wl_display singleton (id=1)
- add a listener for registry events "global" and "global_remove"
- let the handler print info when it receives global events such as
interface and object id (here it's 'name')

So far this example is pretty clean, I like the way the listeners are
defined through structures that have pointers to handlers, and how
everything is done locally through pollable unix socket as file descriptors.

Chapter 6 goes into the inner workings of the graphic part philosophy.
In wayland it's done through buffers (wl_buffer) and surfaces (wl_surface).
From what I understood, surfaces are sort of like windows, rectangular
areas (onscreen surface) that have roles and can receive user inputs, and
buffers are the pixels that will be displayed on the window.
We have to explicitly attach the buffer to the surface, using
wl_surface_attach, damage, and commit.

The buffers can be stored in a shared memory, mmap, and the file
descriptor given to the server, or through a GPU interface like DMA.

To make this possible, the server (the object implementing wl_display
I guess, so ID=1, or other that should be caught with "global" event)
implements two interfaces: one called
wl_compositor, that can be called to create new surfaces objects,
returning their ids, and another interface called wl_shm, that can be
called to create shared memory pools for clients to use as buffers for
their surfaces (wl_shm_pool).

It's interesting that then manipulating the mmap data is equivalent to
manipulating the pixels. Obviously, we have to define the pixel format
and all that.
The rendering technique seems very "raw" but I'm sure it's way more
optimized when using the GPU with dma instead of the CPU to manipulate the
pixels (through wl_egl_window_create and wl_egl_window, which apparently
aren't part of the standard wayland.xml description).

Quote:With the shared memory approach, sending buffers from the client to the
compositor in such cases is very inefficient, as the client has to read
their data from the GPU to the CPU, then the compositor has to read it
from the CPU back to the GPU to be rendered.

The chapter ends with a teaser about the role that the wl_surface can
play. The description in wayland.xml is interesting too.

Quote:A surface without a "role" is fairly useless: a compositor does
not know where, when or how to present it. The role is the
purpose of a wl_surface. Examples of roles are a cursor for a
pointer (as set by wl_pointer.set_cursor), a drag icon
(wl_data_device.start_drag), a sub-surface
(wl_subcompositor.get_subsurface), and a window as defined by a
shell protocol (e.g. wl_shell.get_shell_surface).

So far, I think the design thinking behind Wayland is still in touch with
my initial impression: very role-centric, or can be seen as a
decentralization/separation of concerns. I like that all the possible interactions,
requests and events are defined in XML format, that makes for a no-surprise
environment. However, I kind of know that this is also limiting when the basic
protocol doesn't implement all that you need. We'll see more of that later in
the book I guess.


Messages In This Thread
RE: Nixers Book Club - Book #3: The Wayland Book - by venam - 27-03-2021, 11:33 AM