Class: Potty::Input::Decoder
- Inherits:
-
Object
- Object
- Potty::Input::Decoder
- Defined in:
- lib/potty/input/decoder.rb
Overview
Turns a raw terminal byte stream into Keys codes. In curses mode the library gets this for free via keypad(true); inline “listen” mode reads raw bytes, so we decode here — and crucially we emit the same integer codes curses would, so every widget’s handle_key works unchanged in either mode.
Printable/control bytes pass straight through as codes. Escape sequences (ESC [ A, ESC O P, ESC [ 3 ~, …) map to Keys::UP / DELETE / etc. A lone ESC is ambiguous — it may begin a sequence — so it’s held until either more bytes complete a sequence, or enough time passes (escape_timeout) that we resolve it to a bare ESC. Same heuristic curses runs internally via ESCDELAY; here it’s ours.
Usage (driven by the inline loop each tick):
keys = decoder.feed(bytes_available, now) # bytes may be ""
keys.each { |code| view.handle_key(code) }
Constant Summary collapse
- SEQUENCES =
Escape sequence (the bytes after ESC) -> Keys code.
{ '[A' => Keys::UP, 'OA' => Keys::UP, '[B' => Keys::DOWN, 'OB' => Keys::DOWN, '[C' => Keys::RIGHT, 'OC' => Keys::RIGHT, '[D' => Keys::LEFT, 'OD' => Keys::LEFT, '[H' => Keys::HOME, 'OH' => Keys::HOME, '[1~' => Keys::HOME, '[F' => Keys::END_, 'OF' => Keys::END_, '[4~' => Keys::END_, '[3~' => Keys::DELETE, '[Z' => Keys::SHIFT_TAB }.freeze
- MAX_SEQ =
Longest escape body we might still be completing (e.g. “[3~”).
SEQUENCES.keys.map(&:length).max
Instance Method Summary collapse
-
#feed(bytes, now) ⇒ Object
Append newly-read bytes (may be empty) and return the key codes that can be resolved now.
-
#initialize(escape_timeout: 0.25) ⇒ Decoder
constructor
A new instance of Decoder.
Constructor Details
#initialize(escape_timeout: 0.25) ⇒ Decoder
Returns a new instance of Decoder.
39 40 41 42 43 |
# File 'lib/potty/input/decoder.rb', line 39 def initialize(escape_timeout: 0.25) @escape_timeout = escape_timeout @buffer = +'' @esc_at = nil end |
Instance Method Details
#feed(bytes, now) ⇒ Object
Append newly-read bytes (may be empty) and return the key codes that can be resolved now. ‘now` is a monotonic-ish time used only for the bare-ESC timeout; pass Time.now from the loop (and in tests).
48 49 50 51 52 53 54 55 |
# File 'lib/potty/input/decoder.rb', line 48 def feed(bytes, now) @buffer << bytes.to_s codes = [] while (code = take(now)) codes << code end codes end |