class Termisu::Event::Source::Input

Overview

Terminal input event source.

Wraps Reader and Input::Parser to produce Key and Mouse events via a dedicated polling fiber.

Usage

reader = Termisu::Reader.new(terminal.infd)
parser = Termisu::Input::Parser.new(reader)
input = Termisu::Event::Source::Input.new(reader, parser)

loop = Termisu::Event::Loop.new
loop.add_source(input)
loop.start

while event = loop.output.receive?
  case event
  when Termisu::Event::Key
    break if event.key.escape?
  when Termisu::Event::Mouse
    puts "Click at #{event.x},#{event.y}"
  end
end

Thread Safety

Uses Atomic(Bool) for the running state. Safe to call #start/#stop from different fibers.

Defined in:

termisu/event/source/input.cr

Constant Summary

IDLE_SLEEP = 4.milliseconds

Idle sleep when no input is available.

Keeps CPU usage low without introducing long blocking waits that can starve high-frequency timers.

4ms is a measured stopgap for the idle busy-poll: it cuts idle select(2) calls from ~978/s to ~244/s at the cost of up to 4ms of added input latency when idle. The proper fix (deferred) is evented IO on the input fd — cooperative IO::FileDescriptor wakeup on data, ~20 wakeups/s with lower latency than any fixed sleep.

Log = Termisu::Logs::Event
MAX_DRAIN_PER_CYCLE = 64

Maximum events drained per loop iteration.

Prevents a continuous input stream from monopolizing the scheduler while still allowing bursty input to be processed quickly.

Constructors

Instance Method Summary

Instance methods inherited from class Termisu::Event::Source

name : String name, running? : Bool running?, start(output : Channel(Event::Any)) : Nil start, stop : Nil stop

Constructor Detail

def self.new(reader : Termisu::Reader, parser : Termisu::Input::Parser) #

Creates a new input source.

  • reader - Reader instance for raw input
  • parser - Parser instance for escape sequence parsing

[View source]

Instance Method Detail

def name : String #

Returns the source name for identification.


[View source]
def prepare_raw_handoff : Bool #

Prepares the stopped source to hand its reader to a raw-input consumer.

Callers must stop the source first. A parsed event blocked on backpressure and bytes retained by an in-progress parser probe both remain event-owned; handing the reader to a raw consumer in either state would reorder or split the input stream.


[View source]
def running? : Bool #

Returns true if the input source is currently running.


[View source]
def start(output : Channel(Event::Any)) : Nil #

Starts polling for input events and sending them to the output channel.

Spawns a fiber that drains available input events without blocking and sends them to the channel.

Serializes lifecycle changes so a previous polling fiber is fully stopped before another can start.


[View source]
def stop : Nil #

Stops polling for input events.

Signals the polling fiber and waits for it to finish. When this method returns, the parser no longer touches the reader, so ownership can be handed to a raw-input caller without splitting an input sequence.


[View source]