/** * DreamStack Bitstream Protocol — C port for ESP32-P4 * * Ported from engine/ds-stream/src/protocol.rs * Header format and frame types for the thin client. */ #pragma once #include // ─── Magic bytes ─── #define DS_MAGIC_0 0xD5 #define DS_MAGIC_1 0x7A // ─── Header size ─── #define DS_HEADER_SIZE 16 // ─── Frame types (output: source → receiver) ─── #define DS_FRAME_PIXELS 0x01 // Raw RGBA pixel data #define DS_FRAME_COMPRESSED 0x02 // PNG/WebP compressed #define DS_FRAME_DELTA 0x03 // XOR delta from previous #define DS_FRAME_SIGNAL_SYNC 0x10 // Full signal state (JSON) #define DS_FRAME_SIGNAL_DIFF 0x11 // Changed signals (JSON) #define DS_FRAME_SCHEMA 0x12 // Schema announcement #define DS_FRAME_KEYFRAME 0xF0 // Reset state #define DS_FRAME_PING 0xFE // Heartbeat #define DS_FRAME_END 0xFF // Stream end // ─── Input types (receiver → source) ─── #define DS_INPUT_POINTER 0x01 #define DS_INPUT_KEY 0x02 #define DS_INPUT_SCROLL 0x03 #define DS_INPUT_TOUCH 0x04 #define DS_INPUT_GAMEPAD_AXIS 0x10 #define DS_INPUT_GAMEPAD_BTN 0x11 #define DS_INPUT_TEXT 0x20 // ─── Flags ─── #define DS_FLAG_INPUT 0x01 #define DS_FLAG_KEYFRAME 0x02 #define DS_FLAG_COMPRESSED 0x04 // ─── Frame header (16 bytes, little-endian) ─── typedef struct __attribute__((packed)) { uint8_t magic[2]; // DS_MAGIC_0, DS_MAGIC_1 uint8_t frame_type; // DS_FRAME_* or DS_INPUT_* uint8_t flags; // DS_FLAG_* uint16_t seq; // Sequence number uint32_t timestamp; // Milliseconds uint16_t width; // Frame width (pixels) uint16_t height; // Frame height (pixels) uint16_t payload_len; // Following payload length } ds_header_t; // ─── Touch event (6 bytes) ─── typedef struct __attribute__((packed)) { uint8_t id; // Touch identifier (multi-touch) uint16_t x; // X coordinate uint16_t y; // Y coordinate uint8_t phase; // 0=start/move, 1=end, 2=cancel } ds_touch_event_t; /** * Parse a header from raw bytes. * Returns 0 on success, -1 if magic doesn't match. */ static inline int ds_parse_header(const uint8_t *buf, ds_header_t *hdr) { if (buf[0] != DS_MAGIC_0 || buf[1] != DS_MAGIC_1) return -1; *hdr = *(const ds_header_t *)buf; return 0; } /** * Encode a touch input message into a buffer. * Buffer must be at least DS_HEADER_SIZE + sizeof(ds_touch_event_t) = 22 bytes. * Returns total message size. */ static inline size_t ds_encode_touch(uint8_t *buf, uint16_t seq, uint32_t timestamp, const ds_touch_event_t *touch) { ds_header_t *hdr = (ds_header_t *)buf; hdr->magic[0] = DS_MAGIC_0; hdr->magic[1] = DS_MAGIC_1; hdr->frame_type = DS_INPUT_TOUCH; hdr->flags = DS_FLAG_INPUT; hdr->seq = seq; hdr->timestamp = timestamp; hdr->width = 0; hdr->height = 0; hdr->payload_len = sizeof(ds_touch_event_t); *(ds_touch_event_t *)(buf + DS_HEADER_SIZE) = *touch; return DS_HEADER_SIZE + sizeof(ds_touch_event_t); }