Protocol Specification
Complete wire protocol documentation for the PlexyDesk display server. Everything you need to implement a client from scratch.
Overview
The PlexyDesk protocol is a binary protocol designed for minimal latency and maximum throughput between clients and the compositor.
Design Principles
-
Fixed-size headers. Every message starts with a 4-byte header containing opcode and length. No variable-length parsing required for routing.
-
Packed structures. All protocol messages use
#pragma pack(1)alignment for wire compatibility across compilers. -
Zero-copy rendering. Shared memory and DMA-BUF support for buffer sharing without copying pixel data.
-
Explicit acknowledgment. Configure events require acknowledgment before the compositor applies changes. This prevents race conditions.
Connection
Socket Path
Clients connect to the compositor via a Unix domain socket at the following path:
#define PLEXY_SOCKET_PATH "/tmp/plexy.sock"
#define PLEXY_PROTOCOL_VERSION 1
Handshake Sequence
Upon connection, clients must send a PLEXY_OP_HELLO message.
The compositor responds with PLEXY_OP_HELLO_REPLY containing
the client ID and display information.
Hello Message Structure
typedef struct {
PlexyHeader header; // opcode=1, length=sizeof(PlexyHello)
uint32_t version; // Protocol version (must be 1)
char name[64]; // Client application name (null-terminated)
} PlexyHello;
Hello Reply Structure
typedef struct {
PlexyHeader header; // opcode=2
uint32_t version; // Compositor protocol version
uint32_t client_id; // Unique client identifier
float ui_scale; // UI scale factor (e.g., 1.0, 1.5, 2.0)
uint32_t screen_width; // Screen width in pixels
uint32_t screen_height;// Screen height in pixels
} PlexyHelloReply;
Message Format
Every protocol message begins with a 4-byte header, followed by message-specific data.
Header Structure
typedef struct {
uint16_t opcode; // Message type identifier
uint16_t length; // Total message length including header
} PlexyHeader;
Wire Format
All multi-byte integers are little-endian. Strings are null-terminated with fixed maximum lengths.
Byte Order
The protocol uses little-endian byte order for all multi-byte integers. This matches the native byte order on x86/x86_64 and ARM (in default configuration).
Opcodes
Message types are identified by 16-bit opcodes. Opcodes are grouped by functionality.
Core Messages (1-29)
| Opcode | Name | Direction | Description |
|---|---|---|---|
| 1 | HELLO | C→S | Client handshake initiation |
| 2 | HELLO_REPLY | S→C | Server handshake response |
| 3 | CREATE_WINDOW | C→S | Request window creation |
| 4 | WINDOW_CREATED | S→C | Window creation confirmed |
| 5 | ATTACH_BUFFER | C→S | Attach SHM buffer to window |
| 6 | BUFFER_ATTACHED | S→C | Buffer attachment confirmed |
| 7 | COMMIT | C→S | Commit pending changes |
| 8 | FRAME_DONE | S→C | Frame presentation complete |
| 9 | CONFIGURE | S→C | Window configuration change |
| 10 | CONFIGURE_ACK | C→S | Acknowledge configuration |
| 11 | DESTROY_WINDOW | C→S | Request window destruction |
| 12 | WINDOW_CLOSED | S→C | Window closed by compositor |
Input Events (13-23)
| Opcode | Name | Description |
|---|---|---|
| 13 | POINTER_ENTER | Pointer entered window surface |
| 14 | POINTER_LEAVE | Pointer left window surface |
| 15 | POINTER_MOTION | Pointer position update |
| 16 | POINTER_BUTTON | Mouse button press/release |
| 17 | KEY | Keyboard key event |
| 18 | FOCUS_IN | Window gained keyboard focus |
| 19 | FOCUS_OUT | Window lost keyboard focus |
| 22 | POINTER_AXIS | Scroll wheel / touchpad axis |
| 23 | MODIFIERS | Keyboard modifier state change |
Layer Surfaces (30-39)
| Opcode | Name | Description |
|---|---|---|
| 30 | CREATE_LAYER_SURFACE | Create panel/dock/overlay |
| 31 | LAYER_SURFACE_CREATED | Layer surface confirmed |
| 32 | DESTROY_LAYER_SURFACE | Destroy layer surface |
DMA-BUF (80-89)
| Opcode | Name | Description |
|---|---|---|
| 80 | ATTACH_DMABUF | Attach DMA-BUF (zero-copy) |
| 81 | DMABUF_ATTACHED | DMA-BUF attachment confirmed |
Window Management
Creating a Window
typedef struct {
PlexyHeader header; // opcode=3
int32_t x; // Initial X position (-1 for auto)
int32_t y; // Initial Y position (-1 for auto)
uint32_t width; // Window width in pixels
uint32_t height; // Window height in pixels
uint32_t buffer_scale; // Buffer scale factor (1 or 2)
char title[128]; // Window title (null-terminated)
} PlexyCreateWindow;
Window Created Response
typedef struct {
PlexyHeader header; // opcode=4
uint32_t window_id; // Assigned window identifier
uint32_t width; // Actual configured width
uint32_t height; // Actual configured height
float scale_factor; // UI scale factor
uint32_t buffer_scale; // Buffer scale for HiDPI
} PlexyWindowCreated;
Configuration Events
The compositor sends CONFIGURE events when window geometry changes
(e.g., user resize, display scale change). Clients must acknowledge with
CONFIGURE_ACK before the changes take effect.
typedef struct {
PlexyHeader header; // opcode=9
uint32_t window_id;
uint32_t width; // New width
uint32_t height; // New height
float scale_factor; // New scale factor
uint32_t buffer_scale; // New buffer scale
uint32_t serial; // Configuration serial number
} PlexyConfigure;
typedef struct {
PlexyHeader header; // opcode=10
uint32_t window_id;
uint32_t serial; // Must match configure serial
} PlexyConfigureAck;
Important
After receiving a CONFIGURE event, the client should resize its
buffer to match the new dimensions, then send CONFIGURE_ACK with
the matching serial. The compositor will not display the new size until
acknowledgment is received.
Buffer Management
PlexyDesk supports two buffer types: shared memory (SHM) for CPU rendering and DMA-BUF for zero-copy GPU buffer sharing.
Shared Memory Buffers
typedef struct {
PlexyHeader header; // opcode=5
uint32_t window_id; // Target window
uint32_t width; // Buffer width
uint32_t height; // Buffer height
uint32_t stride; // Bytes per row
uint32_t format; // Pixel format (see below)
uint32_t shm_size; // Total shared memory size
} PlexyAttachBuffer;
// Pixel format constants
#define PLEXY_FORMAT_ARGB8888 0 // 32-bit with alpha
#define PLEXY_FORMAT_XRGB8888 1 // 32-bit no alpha
#define PLEXY_FORMAT_RGBA8888 2 // 32-bit RGBA order
DMA-BUF Buffers (Zero-Copy)
For GPU-accelerated clients, DMA-BUF allows direct buffer sharing without
copying. The file descriptor is passed via SCM_RIGHTS ancillary data.
typedef struct {
PlexyHeader header; // opcode=80
uint32_t window_id;
uint32_t width;
uint32_t height;
uint32_t stride;
uint32_t format; // DRM_FORMAT_* fourcc code
uint64_t modifier; // DRM format modifier
uint32_t offset; // Offset into buffer
uint32_t flags; // Reserved
} PlexyAttachDmaBuf;
Committing Changes
After drawing to the buffer, clients must commit to make changes visible. The commit message includes optional damage region and cursor hints.
typedef struct {
PlexyHeader header; // opcode=7
uint32_t window_id;
int32_t cursor_cell_x; // Cursor column (for terminals)
int32_t cursor_cell_y; // Cursor row (for terminals)
int32_t cursor_cols; // Grid columns (optional)
int32_t cursor_rows; // Grid rows (optional)
int32_t damage_x; // Damaged region X
int32_t damage_y; // Damaged region Y
int32_t damage_width; // Damaged region width
int32_t damage_height; // Damaged region height
} PlexyCommit;
Input Events
Input events are delivered to the focused window. Coordinates are in surface-local pixels.
Pointer Events
typedef struct {
PlexyHeader header; // opcode=15
uint32_t window_id;
int32_t x; // Surface-local X coordinate
int32_t y; // Surface-local Y coordinate
} PlexyPointerMotion;
typedef struct {
PlexyHeader header; // opcode=16
uint32_t window_id;
uint32_t button; // Linux input button code
uint32_t state; // 0=released, 1=pressed
int32_t x; // X at time of event
int32_t y; // Y at time of event
} PlexyPointerButton;
// Standard button codes
#define PLEXY_BTN_LEFT 0x110
#define PLEXY_BTN_RIGHT 0x111
#define PLEXY_BTN_MIDDLE 0x112
Scroll Events
typedef struct {
PlexyHeader header; // opcode=22
uint32_t window_id;
int32_t axis; // 0=vertical, 1=horizontal
int32_t value; // Scroll amount (1/256 pixel units)
int32_t discrete; // Discrete steps (for wheel), 0 for smooth
} PlexyPointerAxis;
Keyboard Events
typedef struct {
PlexyHeader header; // opcode=17
uint32_t window_id;
uint32_t keycode; // Linux input keycode
uint32_t state; // 0=released, 1=pressed
uint32_t modifiers; // Active modifier mask
} PlexyKey;
// Modifier flags
#define PLEXY_MOD_SHIFT (1 << 0)
#define PLEXY_MOD_CTRL (1 << 1)
#define PLEXY_MOD_ALT (1 << 2)
#define PLEXY_MOD_SUPER (1 << 3)
Modifier State
typedef struct {
PlexyHeader header; // opcode=23
uint32_t window_id;
uint32_t depressed; // Currently pressed modifiers
uint32_t latched; // Latched (sticky) modifiers
uint32_t locked; // Locked modifiers (Caps Lock, etc.)
uint32_t group; // Keyboard layout group
} PlexyModifiers;
Layer Surfaces
Layer surfaces are special surfaces for panels, docks, overlays, and desktop backgrounds. They have fixed positions and stacking order.
Layer Types
enum PlexyLayer {
PLEXY_LAYER_BACKGROUND = 0, // Desktop background
PLEXY_LAYER_BOTTOM = 1, // Below windows
PLEXY_LAYER_TOP = 2, // Above windows (panels)
PLEXY_LAYER_OVERLAY = 3 // Topmost (notifications)
};
Anchor Flags
enum PlexyAnchor {
PLEXY_ANCHOR_NONE = 0,
PLEXY_ANCHOR_TOP = 1,
PLEXY_ANCHOR_BOTTOM = 2,
PLEXY_ANCHOR_LEFT = 4,
PLEXY_ANCHOR_RIGHT = 8
};
// Example: dock at bottom
anchor = PLEXY_ANCHOR_BOTTOM | PLEXY_ANCHOR_LEFT | PLEXY_ANCHOR_RIGHT;
Creating a Layer Surface
typedef struct {
PlexyHeader header; // opcode=30
uint32_t layer; // PlexyLayer enum value
uint32_t anchor; // Anchor flags (OR'd together)
uint32_t width; // Surface width (0 for auto)
uint32_t height; // Surface height (0 for auto)
int32_t exclusive_zone; // Reserved screen space
uint32_t keyboard_interactivity; // Accept keyboard focus?
} PlexyCreateLayerSurface;
HiDPI Support
PlexyDesk provides comprehensive HiDPI support through buffer scaling and output scale factors.
Scale Factors
- ui_scale — Floating-point scale factor from the compositor (e.g., 1.0, 1.5, 2.0). Use for UI element sizing.
- buffer_scale — Integer scale for buffer dimensions. A buffer_scale of 2 means the client should allocate 2× pixels.
Output Information
typedef struct {
PlexyHeader header; // opcode=70
uint32_t output_id;
int32_t x; // Output position X
int32_t y; // Output position Y
uint32_t physical_width_mm; // Physical width in mm
uint32_t physical_height_mm; // Physical height in mm
uint32_t pixel_width; // Resolution width
uint32_t pixel_height; // Resolution height
float scale_factor; // Output scale factor
uint32_t subpixel; // Subpixel layout
uint32_t transform; // Output transform
char make[64]; // Monitor manufacturer
char model[64]; // Monitor model
} PlexyOutputInfo;
Preferred Scale Notification
typedef struct {
PlexyHeader header; // opcode=76
uint32_t window_id;
uint32_t preferred_scale; // Recommended integer scale
float exact_scale; // Exact fractional scale
} PlexyPreferredBufferScale;