terminal_graphics_interactive 0.1.0

Keyboard and mouse input for terminal_graphics: build interactive terminal games, art and animations. Almost no unsafe.
Documentation
  • Coverage
  • 100%
    51 out of 51 items documented3 out of 3 items with examples
  • Size
  • Source code size: 158.0 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 2.1 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 4s Average build duration of successful builds.
  • all releases: 4s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • calum/terminal_graphics_interactive
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • calum

terminal_graphics_interactive

Keyboard and mouse input for terminal_graphics, so you can build interactive terminal games, art, animations, screensavers, maths and physics demos, and drawing tools. One dependency (terminal_graphics, re-exported), almost no unsafe.

Status: early (0.1). Done: the input parser, raw terminal mode, Terminal (an interactive session with poll(timeout), the window size and draw), mouse-to-cell mapping, a paint example, and llms.txt for AI coding agents. Next: a game example and terminal resize events.

Try it

cargo run --example paint        # drag to paint, right-drag to erase, 1-8 or scroll for colour, c clears, q quits
cargo run --example screensaver   # bouncing dots with fading trails; click to add one, any key quits
cargo run --example events       # prints every key and mouse event

It works in Windows PowerShell and Windows Terminal, Linux and macOS terminals, and over SSH if you allocate a terminal (ssh -t host ...).

How it works

  1. Terminal::start() switches to the alternate screen (your scrollback comes back on exit), puts the terminal in raw mode, turns on mouse reporting, hides the cursor and finds the window size. Dropping it undoes all of that, even on panic. terminal.poll(timeout) returns the next event, or None when the time runs out (your cue to draw the next frame); terminal.wait() blocks. (RawMode, Parser, MOUSE_ON and MOUSE_OFF are public too if you want the pieces.)
  2. Events are Event::Key and Event::Mouse. A lone Escape byte becomes the Escape key after 25 ms, so arrow keys over a slow SSH link are not mistaken for it.
  3. terminal.draw(&display) shows a Display at the top left of the window, so mouse coordinates (0-based cells, (0, 0) at the top left) line up with display cells: mouse.cell_in(&display) gives the cell under the pointer. Make the display at most terminal.size() wide and one row shorter than it is high.

unsafe

Raw mode cannot be done in safe Rust, so it is the only place unsafe appears: three libc calls on Unix (src/raw_mode/unix.rs) and three kernel32 calls on Windows (src/raw_mode/windows.rs), each with a // SAFETY: comment. Everything else is safe Rust, and tests/unsafe_audit.rs fails the build if unsafe appears anywhere else.

Performance

cargo bench runs benches/parser.rs, which uses only std. Median of 15 samples on one machine (Windows 11, release build), parsing 10,000 events fed in chunks:

Stream Chunk Before Now
mouse drag (SGR) 64 B 44 ns/event 41 ns/event
mouse drag (SGR) 4 KB 134 ns/event 37 ns/event
mouse drag (SGR) 1 MB paste 1630 ns/event 85 ns/event
keyboard 64 B 102 ns/event 28 ns/event
keyboard 1 MB paste 415 ns/event 27 ns/event

"Before" had two problems that the benchmark exposed: parsed bytes were removed from the front of the buffer after every event (quadratic on large pastes), and key sequences allocated a Vec. A terminal produces nowhere near 10 million events a second, so parsing is far from being a bottleneck.

Platform checks so far

Platform Built and unit tested Run against a real terminal
Windows yes yes: 8 end-to-end tests in a ConPTY pseudo console (tests/e2e_windows.rs), run in CI and locally. Not yet tried by a person in a real PowerShell or Windows Terminal window
Linux, macOS yes yes: the same 8 end-to-end tests in a pty (tests/e2e_unix.rs), run in CI on Ubuntu and macOS

License

Licensed under either of MIT or Apache-2.0 at your option.