Postscript: not that many roundtrips!
It may seem that our code makes a lot of roundtrips between our process and the compositor. We're fetching globals, creating a memory pool, allocating a buffer in it, constructing various objects such as the surface
and the shell_surface
and calling methods on them. If each action required a roundtrip to complete, we'd be making about 12 roundtrips (and that's if the compositor only advertised the globals that we need) before the user sees the black square. Imagine how slow that would be. Actually, if you have used X.Org, you don't even have to imagine. Wayland is much better than that.
Let's see what's actually going on in our black square program.
Wayland-client library has built-in logging functionality that you can enable by setting WAYLAND_DEBUG
environment variable to 1
:
$ WAYLAND_DEBUG=1 ./runme
[1610518.311] -> [email protected]_registry(new id wl_registry@2)
[1610518.358] -> [email protected](new id wl_callback@3)
[1610518.488] [email protected]_id(3)
[1610518.502] [email protected](1, "wl_drm", 2)
[1610518.511] [email protected](2, "wl_compositor", 3)
[1610518.521] -> [email protected](2, "wl_compositor", 3, new id [unknown]@4)
[1610518.536] [email protected](3, "wl_shm", 1)
[1610518.545] -> [email protected](3, "wl_shm", 1, new id [unknown]@5)
[1610518.555] [email protected](4, "wl_output", 2)
[1610518.563] [email protected](5, "wl_output", 2)
[1610518.570] [email protected](6, "wl_data_device_manager", 3)
[1610518.586] [email protected](7, "gtk_primary_selection_device_manager", 1)
[1610518.604] [email protected](8, "zxdg_shell_v6", 1)
[1610518.612] [email protected](9, "wl_shell", 1)
[1610518.631] -> [email protected](9, "wl_shell", 1, new id [unknown]@6)
[1610518.652] [email protected](10, "gtk_shell1", 1)
[1610518.658] [email protected](11, "wl_subcompositor", 1)
[1610518.676] [email protected](12, "zwp_pointer_gestures_v1", 1)
[1610518.694] [email protected](13, "zwp_tablet_manager_v2", 1)
[1610518.718] [email protected](14, "wl_seat", 5)
[1610518.728] [email protected](15, "zwp_relative_pointer_manager_v1", 1)
[1610518.737] [email protected](16, "zwp_pointer_constraints_v1", 1)
[1610518.745] [email protected](17, "zxdg_exporter_v1", 1)
[1610518.755] [email protected](18, "zxdg_importer_v1", 1)
[1610518.763] [email protected](16657)
[1610518.769] -> [email protected]_surface(new id wl_surface@3)
[1610518.775] -> [email protected]_shell_surface(new id wl_shell_surface@7, wl_surface@3)
[1610518.784] -> [email protected]_toplevel()
[1610518.805] -> [email protected]_pool(new id wl_shm_pool@8, fd 5, 160000)
[1610518.818] -> [email protected]_buffer(new id wl_buffer@9, 0, 200, 200, 800, 1)
[1610518.835] -> [email protected](wl_buffer@9, 0, 0)
[1610518.844] -> [email protected]()
^C
Here, requests are prefixed by the ->
arrow. These are the actual Wayland requests and events made, so you won't see wayland-client's additional stuff like wl_display_connect
and wl_display_roundtrip
here (the latter actually wraps wl_display.sync
and wl_callback.done
, and you do see those).
Recall that neither requests nor events require a roundtrip to complete. For example, all the requests on the bottom, starting from wl_compositor.create_surface
are made in quick succession without ever waiting for the compositor to respond.
One caveat when counting roundtrips based on wayland-client logs is that wayland-client logs method calls immediately as they are made (and scheduled for sending), not when they are actually sent. This matters because the asynchronous nature of Wayland allows queuing up lots of method calls and sending them all at once only when we're done and want to wait for response.
To get another picture of what's going on, let's dive down to the syscall layer:
$ strace -e trace=network ./runme
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0) = 3
connect(3, {sa_family=AF_UNIX, sun_path="/run/user/1000/wayland-0"}, 27) = 0
sendmsg(3, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\1\0\0\0\1\0\f\0\2\0\0\0\1\0\0\0\0\0\f\0\3\0\0\0", iov_len=24}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, MSG_DONTWAIT|MSG_NOSIGNAL) = 24
recvmsg(3, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\2\0\0\0\0\0\34\0\1\0\0\0\7\0\0\0wl_drm\0\0\2\0\0\0\2\0\0\0"..., iov_len=4096}], msg_iovlen=1, msg_controllen=0, msg_flags=MSG_CMSG_CLOEXEC}, MSG_DONTWAIT|MSG_CMSG_CLOEXEC) = 720
sendmsg(3, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\2\0\0\0\0\0(\0\2\0\0\0\16\0\0\0wl_compositor\0\0\0"..., iov_len=220}], msg_iovlen=1, msg_control=[{cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, cmsg_data=[5]}], msg_controllen=20, msg_flags=0}, MSG_DONTWAIT|MSG_NOSIGNAL) = 220
recvmsg(3, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\5\0\0\0\0\0\f\0\0\0\0\0\5\0\0\0\0\0\f\0\1\0\0\0\t\0\0\0\0\0\10\0", iov_len=3376}, {iov_base="", iov_len=720}], msg_iovlen=2, msg_controllen=0, msg_flags=MSG_CMSG_CLOEXEC}, MSG_DONTWAIT|MSG_CMSG_CLOEXEC) = 32
recvmsg(3, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\7\0\0\0\1\0\24\0\0\0\0\0\310\0\0\0\310\0\0\0", iov_len=3344}, {iov_base="", iov_len=752}], msg_iovlen=2, msg_controllen=0, msg_flags=MSG_CMSG_CLOEXEC}, MSG_DONTWAIT|MSG_CMSG_CLOEXEC) = 20
^C
We can see our program creating a new Unix domain stream socket, connecting to /run/user/1000/wayland-0
, then sending the initial message (that encodes wl_display.get_registry
and wl_display.sync
requests). It then waits (because we've called wl_display_roundtrip()
, which is also responsible for that wl_display.sync
) for the response. The compositor responds with the wl_registry.global
, wl_display.delete_id
and wl_callback.done
events, all in one message. This is our first roundtrip.
Our program then proceeds by sending a message encoding all the rest of requests it does, including the three calls of wl_registry.bind
.
And that's actually it. That's enough for the black square to be shown on the screen. The two other messages from the compositor that you see at the end are the events the compositor is sending to us after the window is created (we don't see them in the wayland-client log because we haven't set up the listeners for them).
One and a half roundtrips is what it takes to display a simple window in Wayland.