Skip to main content
The engine emits input as a ZREV event batch—a binary format containing one or more input events. These batches flow from the native Zireael engine to the TypeScript core.

Event Types

ZREV batches contain these event record types:
RecordDescriptionFields
KeyKeyboard input with key code and modifierskeyCode, mods, text
MouseMouse events (move, drag, down, up, wheel)x, y, mouseKind, buttons, wheelX, wheelY, mods
ResizeTerminal size changecols, rows
TickAnimation timerdeltaNs

Binary Format

Header Structure

Every ZREV buffer begins with a 24-byte header. All fields are little-endian.
OffsetSizeTypeFieldDescription
04u32magic0x5645525A (ASCII ZREV as LE u32)
44u32versionFormat version (currently 1)
84u32header_sizeAlways 24
124u32total_sizeTotal byte length of entire buffer
164u32record_countNumber of event records
204u32reserved0Must be 0

Event Record Structure

After the header, event records are laid out contiguously. Each record is self-framed with a record header:
OffsetSizeTypeField
01u8kind (1=key, 2=text, 3=paste, 4=mouse, 5=resize, 6=tick)
11u8flags (reserved, must be 0)
22u16size (total bytes including this header)
Followed by kind-specific payload.

Key Events

Kind: 1
Total size: Variable (minimum 12 bytes)
OffsetSizeTypeField
01u8kind = 1
11u8flags = 0
22u16size
44u32keyCode (ZR_KEY_* constant)
84u32mods (bitmask of ZR_MOD_*)
12+variableUTF-8text (optional, when printable)

Key Codes

Special keys:
KeyCode
ZR_KEY_ESCAPE1
ZR_KEY_ENTER2
ZR_KEY_TAB3
ZR_KEY_BACKSPACE4
ZR_KEY_INSERT10
ZR_KEY_DELETE11
ZR_KEY_HOME12
ZR_KEY_END13
ZR_KEY_PAGE_UP14
ZR_KEY_PAGE_DOWN15
Arrow keys:
KeyCode
ZR_KEY_UP20
ZR_KEY_DOWN21
ZR_KEY_LEFT22
ZR_KEY_RIGHT23
Function keys: ZR_KEY_F1 (100) through ZR_KEY_F12 (111) Printable keys: Use ASCII codepoints (32-126)

Modifier Bitmask

const ZR_MOD_SHIFT = 1 << 0;  // 0x01
const ZR_MOD_CTRL  = 1 << 1;  // 0x02
const ZR_MOD_ALT   = 1 << 2;  // 0x04
const ZR_MOD_META  = 1 << 3;  // 0x08

Mouse Events

Kind: 4
Total size: 28 bytes
OffsetSizeTypeField
01u8kind = 4
11u8flags = 0
22u16size = 28
44i32x (column, 0-based)
84i32y (row, 0-based)
124u32mouseKind (1=move, 2=drag, 3=down, 4=up, 5=wheel)
164u32mods (modifier bitmask)
204u32buttons (button state bitmask)
242i16wheelX (horizontal scroll delta)
262i16wheelY (vertical scroll delta)

Mouse Kind Values

KindValueDescription
move1Mouse moved without button pressed
drag2Mouse moved with button pressed
down3Button pressed
up4Button released
wheel5Scroll wheel moved

Button Bitmask

const BUTTON_LEFT   = 1 << 0;  // 0x01
const BUTTON_MIDDLE = 1 << 1;  // 0x02
const BUTTON_RIGHT  = 1 << 2;  // 0x04

Resize Events

Kind: 5
Total size: 12 bytes
OffsetSizeTypeField
01u8kind = 5
11u8flags = 0
22u16size = 12
44u32cols (new column count)
84u32rows (new row count)

Tick Events

Kind: 6
Total size: 12 bytes
OffsetSizeTypeField
01u8kind = 6
11u8flags = 0
22u16size = 12
48i64deltaNs (nanoseconds since last tick)
Note: Offset 4 is 8 bytes (i64), not 4-byte aligned. Parser must handle this.

Parsing

Rezi parses ZREV deterministically:
function parseZrevBatch(buffer: Uint8Array): ZrevEvent[] {
  const reader = new BinaryReader(buffer);
  
  // Read header
  const magic = reader.readU32();
  if (magic !== ZREV_MAGIC) {
    throw new Error("Invalid ZREV magic");
  }
  
  const version = reader.readU32();
  if (version !== ZR_EVENT_BATCH_VERSION_V1) {
    throw new Error(`Unsupported ZREV version: ${version}`);
  }
  
  const headerSize = reader.readU32();
  const totalSize = reader.readU32();
  const recordCount = reader.readU32();
  const reserved0 = reader.readU32();
  
  // Read records
  const events: ZrevEvent[] = [];
  for (let i = 0; i < recordCount; i++) {
    const kind = reader.readU8();
    const flags = reader.readU8();
    const size = reader.readU16();
    
    // Parse kind-specific payload
    const event = parseEventRecord(kind, size, reader);
    events.push(event);
  }
  
  return events;
}
Safety guarantees:
  • No reads past buffer end
  • No unbounded allocations from untrusted sizes
  • Explicit, structured errors (no exceptions into user code)
Location: packages/core/src/events.ts

Event Caps

Event batches have configurable size limits:
const config = {
  maxEventBytes: 64 * 1024  // 64 KiB default
};
Enforcement:
  • Engine enforces cap when building ZREV batches
  • Events exceeding cap are dropped (with warning)
  • Prevents unbounded memory allocation

Validation Rules

The parser enforces these constraints:
  • magic must be 0x5645525A (ZREV)
  • version must be 1
  • total_size must be >= header_size (24)
  • record_count must be >= 0
  • Each record’s kind must be valid (1-6)
  • Each record’s size must be >= 4 (minimum record header)
  • Sum of all record sizes must not exceed buffer bounds

Build docs developers (and LLMs) love