I'm looking for a pure rust solution which either uses Rust's stdlib or doesn't utilize a crate with libc.
One example of a Device Status Report (DSR) is a simple query to find the current size of a terminal in width and height. First a CSI
request: ESC
+ [
. Then a command of 19
and finally a termination character of t
. For example: \x1b[19t
. The terminal will then respond with another CSI
: ESC
+ [
, a status identifier, 9
and then the row count
followed by the column count
and a terminating character, t
. For an 80x25 terminal, the response would look like: \x1b[9;25;80t
.
In Rust, it is rather straight-forward to send a request to the terminal using a print!
macro call:
print!("\x1b[19t");
Alternatively, in a shell, one might run:
$ printf "\x1b[19t"
However, I've been unable to actually capture the response coming back from the terminal. This is not my initial approach, but it is supposedly the one that provides me with a non-blocking stdin read (based on experimentation, it is not non-blocking), which is something I'm expecting that I need.
use std::sync::mpsc::{channel, Receiver, TryRecvError};
use std::{
io::{self, Read},
thread, time,
};
fn main() {
let timeout = time::Duration::from_micros(50);
let rx = spawn_read();
print!("\x1b[19t");
let mut data = Vec::new();
for _ in 0..200 {
thread::sleep(timeout);
match rx.try_recv() {
Ok(value) => data.push(value),
Err(TryRecvError::Empty) => {}
Err(TryRecvError::Disconnected) => break,
}
}
let string = match std::str::from_utf8(&data) {
Ok(value) => value,
Err(_why) => "",
};
println!("Captured: {:?}", string);
}
fn spawn_read() -> Receiver<u8> {
let (tx, rx) = channel::<u8>();
thread::spawn(move || loop {
let mut buf = [0u8];
io::stdin().read_exact(&mut buf).unwrap();
if buf != [0u8] {
tx.send(buf[0]).ok();
}
});
rx
}
Running the above, I see:
Captured: ""
But with the code above, I expect (some flavor of CSI
9
;
<rows>
;
<cols>
t
):
Captured: "\x1b[9;25;80t"