[ Concepts ]
Events and Input Decoding
The idea in plain words
Section titled “The idea in plain words”A terminal delivers keyboard presses, mouse clicks, and pasted text over a single byte stream (STDIN_FILENO), while window resizes arrive asynchronously as POSIX SIGWINCH signals. koto::EventLoop turns those raw bytes and signals into strongly typed Event values without busy-waiting or splitting multi-byte sequences.
How koto implements it
Section titled “How koto implements it”- Dual-FD
poll()with POSIX self-pipe:EventLoopcreates a non-blocking pipem_pipe_fds[2]and registersEventLoop::on_sigwinchforSIGWINCH(src/event.cpp:1043-1066).on_sigwinchwrites'R'to the pipe, andEventLoop::wakeup()writes'W'.EventLoop::poll(timeout_ms)blocks in::poll(pfds, 2, eff_timeout)watching bothSTDIN_FILENOandm_pipe_fds[0](src/event.cpp:896-936). - Kernel burst draining via
ioctl(FIONREAD): WhenSTDIN_FILENOis readable,EventLoop::poll()queriesioctl(STDIN_FILENO, FIONREAD, &available)and reads all available bytes intom_pending_inputbefore parsing (src/event.cpp:984-1013). InApp::run(), afterm_loop.poll(wait_ms)returns the first event, awhile (m_running && m_loop.has_pending_event())loop drains all complete events fromm_pending_inputwithm_loop.poll(0)before running a singledraw_frame()pass (src/app.cpp:282-313). - 35 ms escape deadline vs. 500 ms bracketed paste deadline:
- When
m_pending_inputstarts with an incomplete short sequence (such as a lone,[,O,], or an incomplete multi-byte UTF-8 lead byte),EventLoopsetsm_escape_deadline = now + 35ms. If more bytes arrive, the deadline resets; if 35 ms elapse,flush_incomplete_escape()emitsKey::Escape(orAlt+'[',Alt+Shift+'O',Alt+']', or drops an orphaned non-ESC byte) (src/event.cpp:702-754). - When
m_pending_inputstarts with[200~(bracketed paste start without[201~),EventLoopsetsm_paste_deadline = now + 500ms(resetting on each incoming chunk, up to a4 MBsafety cap) so slow pastes over SSH ortmuxare never torn apart by the 35 ms escape timer (src/event.cpp:642-655, 671-700).
- When
EventLoop loop;loop.feed("[1;");// Incomplete CSI sequence: has_pending() is true, has_pending_event() is falseloop.feed("5C");// Complete sequence: Ctrl+Right is readyEvent ev = loop.poll(0);static_cast<void>(ev);Consequences for your code
Section titled “Consequences for your code”- Zero CPU usage when idle: If you do not register
app.on_tick(...),App::run()passeswait_ms = -1tom_loop.poll(-1)whenever no bytes are pending (src/app.cpp:237-269), sleeping inside the kernel until input or a signal arrives. - Pasted text arrives atomically: With bracketed paste enabled (automatic in
Terminal::enter_raw_mode()), multi-line clipboard pastes arrive as a singlePasteEventwithandnormalized to.