The display protocol
How a client talks to the Fasadas compositor — surfaces, buffers, input and the pasteboard.
This is how a client talks to fasadasd, the Fasadas compositor and window server.
Most applications should use the AO toolkit instead. You
need this page if you are porting a toolkit, writing a backend, or debugging
something that draws its own windows. #include "fasadas-proto.h" gives you the
constants; libfasadas covers the rest.
Shape
An AF_UNIX SOCK_STREAM socket at /run/fasadas-0, self-framed: each
message is a 32-byte header (type, serial, generic arguments a–e, len)
followed by len payload bytes.
Two design decisions are worth knowing before anything else.
Buffers are never sent inline. They travel as a memfd over SCM_RIGHTS
and the compositor maps the same physical pages. There is no copy.
Surface ids are client-allocated, so creating a window needs no round trip.
The header is exactly 32 bytes and always will be. Framing is size-based, so growing the header would silently desynchronise every client not rebuilt in lockstep. New capability arrives as new opcodes, never as new header fields.
Declare opacity, or leak the window body
FZ_ATTACH_BUFFER carries a format in its payload, and picking the wrong one is
the single most common visible bug in a new client.
FZ_FORMAT_ARGB32— premultiplied 32-bit ARGB. The compositor alpha-blends your buffer over its grey window body. Any pixel you leave with alpha below 255 shows that grey, which reads on screen as white.FZ_FORMAT_XRGB32— opaque 32-bit. The alpha channel is ignored entirely.
A window that is never translucent must attach XRGB32. Otherwise a partial
or idle frame that leaves stale alpha anywhere bleeds the body colour through —
the "goes white when idle" class of bug.
libfasadas exposes this as fz_window_set_opaque(win, 1); the default is
ARGB32. The GDK backend picks it automatically from the window's visual depth.
Versions, and what an older client gets wrong
The compositor speaks one version to everyone, and says so loudly on the console when a client announces an older one — naming the concrete misreading rather than "version mismatch". It does not refuse the client: breaking a desktop over a scroll wheel is worse than a wrong scroll.
| Version | What it added | What a client below it misreads |
|---|---|---|
| v2 | Parenting and modality, real key modifiers and codepoints, size limits, cursors, the clipboard | Nothing — new opcodes, or fields that had always been sent as zero |
| v3 | Scroll events repacked; every pointer event gained a timestamp | A v2 client reads both scroll axes packed together — a plausible number that is wrong — and cannot tell a double-click from two clicks |
| v4 | All three pointer buttons; the implicit pointer grab; surface position; popup roles | A v3 client takes every right-click for a left-click, and one that unpacks coordinates as unsigned reads a small negative as about 65000 |
| v5 | Real unmap (hide), and maximise / fullscreen / restore | Nothing. A v4 client simply cannot hide without stranding the window, or go fullscreen |
| v6 | Frame callbacks become post-composite; real damage; configure back-pressure; buffer scale | Nothing — appended fields that were zero before |
| v7 | Zero-copy GPU window buffers over dma-buf, advertised as a feature flag | Nothing — a new opcode a client uses only when the flag is advertised |
| v8 | The work area: the screen minus the menu bar and the dock's shelf | Nothing on the wire, but a v7 client has no work area and must substitute the whole screen — which makes short popups near the bottom land under the dock instead of flipping above the widget that opened them |
| v9 | The typed pasteboard, sealed memfds in both directions; the old clipboard becomes the general board's text | Nothing — the new events go only to a client that subscribed or asked |
| v10 | Trackpad gestures and scroll phases, behind a subscription | Nothing — everyone else keeps receiving a trackpad's scrolling as ordinary scroll events |
| v11 | HiDPI: the real UI scale, but only for a client that declares it understands scaling | Nothing — a client that does not set the bit is told the scale is 1 and composited as a 1× client: right size, upsampled |
Pointer input
All three buttons
The button field is FZ_BTN_LEFT, FZ_BTN_RIGHT or FZ_BTN_MIDDLE. Check
it; do not assume it.
The numbers are the PS/2 mask order plus one. They are not X11's, where 2 is middle and 3 is right — a backend translating to X11 or GDK must map, not cast.
The implicit pointer grab
A press gives the pressed surface the pointer until the last button is released. Until then:
- every motion goes to that surface, in its coordinates, however far outside it the pointer travels — so coordinates may be negative or past the edge;
- no other surface is entered, and the grabbing surface is not sent a leave;
- the button-up goes to the surface that was pressed, never to whatever the pointer ended up over.
This is X11's and Wayland's semantic, and it is what makes drag-to-select, rubber-band selection, sliders and drag-and-drop work at all.
Sign-extend your coordinates. Motion coordinates are 32-bit; the button's packed pair is two 16-bit values. Unpacking either as unsigned turns a drag three pixels off the left edge into one 65533 pixels off the right.
Surface position
void fz_window_set_position(fz_window *, int x, int y); /* ask */
void fz_window_position(fz_window *, int *x, int *y); /* last reported */
/* fz_listener.position(win, x, y, user) on every move */
The coordinates are the content origin in root coordinates — the first pixel you drew, not the first pixel of the compositor's title bar. Reporting the frame origin instead would put every conversion out by the title height on a decorated window and be right on an undecorated one, which is the sort of bug that only shows up in one of your two window kinds.
Setting a position is a request. The compositor still places new windows.
The work area
The work area is the usable rectangle: the screen minus the global menu bar and the dock's shelf.
Use it for anything that must not be covered — popup placement above all. A popup positioned against the screen rather than the work area lands under the dock, and does so only near the bottom of the screen, which is why it survives casual testing.
Frame pacing and damage
From v6, a frame callback fires after the compositor has composited, not when it accepted your buffer. Pace your drawing on that, and you are pacing on what reached the panel.
Report real damage rather than the whole surface where you can; the compositor composites what you declare. Configure events apply back-pressure, so a client that draws faster than the screen is told to slow down rather than building a queue.
The typed pasteboard
From v9 the clipboard is a typed, system-wide pasteboard: declare types, provide data on demand, read, list types, and subscribe to changes. Data moves as sealed memfds in both directions, and the compositor keeps the bytes, so a copy survives the process that made it.
Through the toolkit this is AO_Pasteboard; see
the toolkit page for the API and for the one performance
trap in it.
HiDPI
A client declares that it understands scaling. Only then is it told the real UI scale and composited at it. A client that says nothing is told the scale is 1 and is composited as a 1× client — the right size on screen, upsampled.
This is deliberate: an old client on a HiDPI panel looks soft, which is a great deal better than looking quarter-sized.
Trackpad gestures
Scroll phases and gestures are delivered only to a client that subscribes. Everyone else continues to receive a trackpad's scrolling as ordinary scroll events, so nothing breaks by not opting in.
More documentation
- Booting the live image
- Installing to disk
- Packages and the filesystem
- How it boots
- The syscall ABI
- Hardware support
- ACPI
- UEFI boot entries
- NetBooting a Mac
- The AO toolkit
- Grafika — the graphics API
- Desktop effects
- The level bezel
- Notifications
- Rastinis — the file manager
- File operations
- Audio — Garsas
- Hardware video decode
- Networking — Tinklas
- Network shares — SMB and NFS
- Remote control
- The Win32 subsystem
- DirectX and OpenGL for Win32 programs
- Ports and packaging