1

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"
Brian Bruggeman
  • 5,008
  • 2
  • 36
  • 55
  • Is using the libc crate ok? Without it one needs to define a bunch of types to get `tcsetattr` to work. – HHK Nov 28 '21 at 21:24
  • Actually those are not "ANSI", because that refers to ECMA-48 which doesn't do those sequences. This particular one comes from xterm (see [documentation](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Miscellaneous)), and isn't a "DSR" sequence. – Thomas Dickey Nov 29 '21 at 00:40
  • @HHK are you able to point me to where those types might be? Is there an example in code somewhere (I guess I'm really looking for Rust, though I suppose I could go poke around at the existing LibC code). – Brian Bruggeman Nov 29 '21 at 20:03
  • Typos in question: 9 is maximize (19 is screensize). – Thomas Dickey Nov 29 '21 at 20:55
  • @ThomasDickey Can you be more specific? Are you suggesting that the captured response should be `\x1b[19;25;80t`? I ask because my terminal program returns `9` there, not `19`... I've been pecking at differing documentation all over and it seems to be inconsistent. – Brian Bruggeman Nov 29 '21 at 22:23
  • The way in which your question is written makes it less clear than the comment (which happens to agree with [this](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps;Ps;Ps-t:Ps-=-1-9.2069)). But if the Rust code is returning no response, I'd look to see what device it's actually reading. – Thomas Dickey Nov 29 '21 at 23:44
  • To be more clear: I'm using [iterm2](https://iterm2.com/) for my terminal. And the question's expected response above reflects the actual response I get from iterm2 when I run a `printf`. I believe HHK was correct; I need to run `tcsetattr` and/or put the terminal in "raw" mode before I can query the terminal's response. Rust doesn't have a native std library that can do this; we have to dip into [libc](https://github.com/rust-lang/libc). Rust has difficulty with multiple versions of any library that uses C underneath, so I wanted to avoid c-libraries. – Brian Bruggeman Nov 30 '21 at 01:16

0 Answers0