3 Concepts
dxtr edited this page 2026-10-12 01:27:12 +02:00

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 with set-event-handlers or (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