The idea behind termset, is to provide a barebones interface for interacting with the terminal. It is not in any way supposed to be a complete TUI library, but is intended to be used as an alternative backend to something like ncurses.
I have no intention of making this compatible with anything other than Linux, however, I believe this should actually work just fine on Windows right now, though I do not care.
Since I've learned more about UB, I have a had a quick skim of this code and declared it
"definitely unsound". Don't use it for anything. It's mostly minor things, I don't think I was
completely stupid 10 months ago, but I do things like transmuting #[repr(Rust)]
types.
I'm checking these off but they're hardly done really...
- Easily clear the terminal, storing the previous contents
- Acquire information about the terminal reactively
- terminal size
- mouse cursor location (as terminal coordinates)
- Easily switch to a raw input mode, allowing the user to
- write a buffer to a specific location on the terminal
- control the cursor (and keep track of its location)
- Easily restore the terminal to its previous state (
Termset::restore(self)
)
This is (pretty much) an exhaustive list. I intend to make a more complex TUI library on top of this, but I know a lot of people want to do that kind of thing on their own, so I'm hoping this can act as an easy-to-learn backend for people who want to make their own TUI (for fun).
This library contains a library rtc
that may be useful when debugging. It exposes this library under
termset::rtc
fn prepare_terminal() -> (tsc::Termset, tsc::TsStdout) {
// turn off all those pesky defaults!
let mut termset = tsc::Termset::new().unwrap();
termset.disable_lflag(tsc::ECHO | tsc::ICANON | tsc::ISIG);
termset.update(None);
// locally buffered handle to stdout
let mut stdout = tsc::TsStdout::new();
stdout
.exec([
&StdoutEscSeq::SaveCursorPosition,
&StdoutEscSeq::SaveScreen,
&StdoutEscSeq::EraseEntireScreen,
])
.flush();
(termset, stdout)
}
fn restore_terminal(termset: tsc::Termset, mut ts_stdout: tsc::TsStdout) {
ts_stdout
.exec([
&StdoutEscSeq::RestorCursorPosition,
&StdoutEscSeq::RestoreScreen,
])
.flush();
termset.restore();
}
fn main() {
let (termset, mut stdout) = prepare_terminal();
// reads chars and esc seqs from stdin
let mut tr = tsc::TokenReader::new();
loop {
let token = tr.next();
rtc_println!("token = {:?}", token); // using rtc to print on another terminal
if token == tsc::Token::Char("\u{3}") {
// break if we get Ctrl-C SIGINT
break;
}
match token {
tsc::Token::Char(c) => match c {
// basic backspace
"\u{7f}" => {
stdout
.exec([&StdoutEscSeq::MoveLeft(1)])
.write_str(" ")
.exec([&StdoutEscSeq::MoveLeft(1)]);
}
// or just write the codepoint
_ => {
stdout.write_str(c);
}
},
// just forward the esc seq to stdout
tsc::Token::Esc(seq) => {
stdout.exec([&seq.as_stdout_esc_seq().unwrap()]);
}
}
// syscall
stdout.flush();
}
restore_terminal(termset, stdout);
}