Table of Contents
Concepts
This page explains the Wayland vocabulary as cl-wl uses it. If you know Wayland from C, skim the tables. They show where each C idea lives in cl-wl.
Display
A display is a connection to one compositor. connect makes one from
a socket name (default: $WAYLAND_DISPLAY, else wayland-0).
connect-to-fd makes one from a socket you already have. disconnect
closes it, and with-display does both around a body.
The display is also the protocol's wl_display object. Its class,
cl-wl/wayland:wl-display, is a subclass of cl-wl:display, so you pass
the display directly to wl_display requests such as wl-display-sync.
A display holds the connection state: its proxies, the queue of decoded events, the registry and the globals it has announced, and the foreign tables that describe interfaces to libwayland.
Proxy
A proxy is the client's handle on one protocol object: a surface, a
seat, a callback. Every interface has a proxy class named after it, a
subclass of cl-wl:proxy. wl_surface becomes wl-surface.
A proxy knows:
- its display (
proxy-display); - the interface version it was created with (
proxy-version); - its object id (
proxy-id), which is reused after destruction; - its interface's protocol name (
proxy-interface-name, such as"wl_surface"); - whether it is still alive (
proxy-live-p); - its event handlers.
destroy-proxy destroys any proxy. It sends the interface's destructor
request if the proxy's version has one, and otherwise destroys the proxy
on the client side only. A proxy is never freed behind your back: once it
is destroyed, using it signals destroyed-proxy-error instead of touching
freed memory.
Requests and events
Messages go both ways:
- A request goes from client to compositor. Each one is a function,
such as
wl-surface-attach, that takes the proxy and the request's arguments. A request that creates an object (wl-compositor-create-surface) returns the new proxy. - An event goes from compositor to client. Each one is a keyword, such
as
:configure, and you attach a handler for it to a proxy withset-event-handlersor(setf event-handler).
Requests are buffered. They reach the compositor on flush, dispatch,
read-events or roundtrip. Events are read and handled only when you
dispatch. Nothing happens in the background.
Some messages end their object. A destructor request, such as
wl-surface-destroy, destroys the proxy it is sent on. A destructor
event, such as wl_callback's :done, destroys its proxy once the
handler has run.
Globals and the registry
The compositor announces its services as globals: one wl_compositor,
one wl_shm, a wl_seat per seat, a wl_output per monitor, and one
for each extension it supports. A client binds a global to get a proxy
for it.
In C you create a wl_registry, listen for global events, match
interface names and call wl_registry_bind. cl-wl does this for you:
bind takes the proxy class you want and returns the bound proxy.
Behind the scenes each display keeps one registry and tracks the globals
it announces and removes. See Globals.
Versions
Every interface has a version, and new versions add requests, events and enum entries. Three versions matter:
| Function | The version… |
|---|---|
(wl:supported-version 'wl-seat) |
the loaded bindings support at most |
(wl:global-version display 'wl-seat) |
the compositor offers |
(wl:proxy-version seat) |
this object was created with |
bind picks the highest version that both sides support, capped by
:version if you pass one. Objects created from a proxy inherit its
version. A request newer than its proxy's version signals version-error
before anything is sent. Events newer than the proxy's version simply
never arrive. destroy-proxy takes the version into account for you: a
version-2 wl_output has no release request, so it is destroyed on the
client side only.
Cap versions you have not tested. If you bind wl_seat without
:version, a newer compositor may send events your code has never seen.
Since cl-wl drops events without handlers, that is harmless for new
events. A changed meaning is a different matter, as when version 10 of
wl_keyboard makes the compositor repeat keys itself. Production code
usually passes :version.
Names
Protocol names become Lisp names mechanically:
| Protocol | Lisp | Example |
|---|---|---|
interface wl_surface |
proxy class | wl-surface |
request wl_surface.attach |
function | wl-surface-attach |
event wl_surface.enter |
keyword | :enter |
enum wl_surface.error |
type | wl-surface-error |
enum entry invalid_scale |
keyword | :invalid-scale |
enum entry 90 (wl_output.transform) |
keyword | `: |
argument surface_x |
parameter | surface-x |
Underscores become hyphens and names are downcased. A trailing underscore
that a protocol adds to avoid a C keyword (class_) is dropped.
find-proxy-class maps a protocol name to its class, and
interface-wire-name maps a class back.
Values
Each wire type has a Lisp value. The full table, with the edge cases, is in Requests and Events. In short:
| Wire type | In events | In requests |
|---|---|---|
int, uint |
integer | integer |
| enum | keyword; list of keywords for a bitfield | keyword, keyword list or integer |
fixed |
single-float |
any real |
string |
string, or NIL if nullable | string, or NIL if nullable |
object |
proxy, or NIL if nullable | proxy, or NIL if nullable |
new_id |
the new proxy | (returned by the request) |
array |
octet vector, integer vector or keyword list | the same |
fd |
integer, now yours to close with close-fd |
integer, still yours |
The C API, mapped
| libwayland-client | cl-wl |
|---|---|
wl_display_connect |
connect, with-display |
wl_display_connect_to_fd |
connect-to-fd |
wl_display_disconnect |
disconnect |
wl_display_get_fd |
display-fd |
wl_display_dispatch |
dispatch |
wl_display_dispatch_pending |
dispatch-pending |
wl_display_roundtrip |
roundtrip |
wl_display_flush |
flush |
wl_display_prepare_read + read_events |
read-events |
wl_display_get_error, get_protocol_error |
display-error, and conditions |
wl_registry + global listener + wl_registry_bind |
bind, bind-all, follow-globals |
wl_proxy_add_listener |
set-event-handlers, (setf event-handler) |
wl_proxy_destroy, or the destructor request |
destroy-proxy, which picks by version |
wl_proxy_get_version, get_id, get_class |
proxy-version, proxy-id, proxy-interface-name |
wl_surface_attach(...) etc. |
wl-surface-attach etc., generated |
memfd_create + ftruncate + mmap for wl_shm |
make-shared-memory, write-shared-memory |
mmap of the keymap fd |
read-keymap |
close of an fd an event delivered |
close-fd |
struct wl_proxy *, struct wl_display * |
proxy-pointer (escape hatch) |
struct wl_interface |
interface-pointer (escape hatch) |
event queues (wl_event_queue) |
not exposed; one queue per display |