← All libraries

CandyInput

🍬 CandyInput

Terminal escape sequence decoder

pioneering (no upstream) input foundation-package

The missing input layer for PHP TUI. EscapeDecoder consumes raw TTY bytes and emits typed Event objects — plain keys, Kitty keyboard protocol, SGR 1006 mouse, focus events, and bracketed paste. Reentrant and fuzz-friendly. Unblocks sugar-readline migration to real-TTY input (step-21).

Install

composer require sugarcraft/candy-input:@dev

Quickstart

use SugarCraft\Input\EscapeDecoder;
use SugarCraft\Input\Driver\StreamInputDriver;
use SugarCraft\Input\Event\KeyEvent;
use SugarCraft\Input\Event\MouseEvent;

$decoder = new EscapeDecoder();
$driver  = new StreamInputDriver(STDIN);

while (true) {
    $event = $driver->read();
    if ($event === null) continue; // non-blocking empty or EOF

    match (true) {
        $event instanceof KeyEvent  => handleKey($event),
        $event instanceof MouseEvent => handleMouse($event),
        default                      => handleOther($event),
    };
}

EscapeDecoder

decode(string $bytes): list<Event>Decode a byte buffer — emits 0+ Events; buffers partial sequences.
remainder(): stringGet unconsumed bytes that couldn't yet form a complete sequence.
reset(): voidClear the partial-sequence buffer.

InputDriver

read(): ?EventReturn the next Event, or null on EOF / non-blocking empty.

Event types

KeyEventkey, modifiers, raw — plain keys, F1-F12, arrows, Home/End/PgUp/PgDn, Insert, Delete, Backspace, Tab, Enter, Escape, Ctrl+letter
MouseEventx, y, button, action, modifiers — SGR 1006: press, release, drag, scroll
FocusEventgained — terminal window focus gained/lost (DECSET 1004)
PasteEventcontent — bracketed paste (CSI 200~ … CSI 201~), capped at 1 MiB
ResizeEventcols, rows — terminal resize

KeyModifier

Bitmask flags: Shift, Ctrl, Alt, Meta, Super, Hyper, CapsLock, NumLock. Combine with bitwise OR.

Use it for

Source & demos

Try the quickstart →

API

ClassMethodDescription
EscapeDecoderdecode(string $bytes): list<Event>Decode byte buffer, buffer partials
EscapeDecoderremainder(): stringGet unconsumed bytes
EscapeDecoderreset(): voidClear partial buffer
StreamInputDriverread(): ?EventNext event from stream, or null
KeyEventkey: stringSymbolic key name
KeyEventmodifiers: KeyModifierModifier bitmask
KeyEventraw: stringRaw bytes that produced this event
MouseEventx, y: intColumn and row (1-based)
MouseEventbutton: intButton index (0=left, 1=middle, 2=right)
MouseEventaction: stringpress | release | drag | scroll
FocusEventgained: boolTrue if focus gained, false if lost
PasteEventcontent: stringPasted content (max 1 MiB)
ResizeEventcols, rows: intNew terminal dimensions
KeyModifiernone(): selfNo modifiers
KeyModifiershift(): selfShift modifier
KeyModifierctrl(): selfCtrl modifier
KeyModifieralt(): selfAlt modifier
KeyModifierincludes(int $flag): boolCheck if flag is set
KeyModifiervalue(): intRaw bitmask value