Reading responses to ANSI escape codes

When writing an ANSI escape code to the console like this:

julia> print(stdout, "\033[14t");

One gets an answer back that is also written to the console. In this case we get

^[[4;1012;1419t

which is the width and height of my console.
Now I was wondering how I can use this to query for my console size, i.e how can I ensure that the answer is not directly written to the console but to some variable and how would I do such thing in a thread save way, so that other processes that might try to read from stdin do not interfere with my answer.

Maybe you can use displaysize?

2 Likes

Unfortunately that only gives me the number of rows/columns but I need the number of pixels. But I looked into the code for displaysize and it uses ioctl with TIOCGWINSZ internally (which is the second approach I am exploring right now). That call actually returns the number of pixels, but that information is then just thrown away.

Oh, then I misunderstood your question. Why do you want the pixel size of the terminal? I.e. what will you use the information for? Just out of curiosity, I don’t know of a way to retrieve that information.

It s for a package of mine that allows one to display images in a terminal when using the Kitty terminal emulator. So I thought It might be a good idea to have the ability to scale some output to the width of the terminal.

Also, there might be other information that I could get out using escape sequences, for example the name of the terminal emulator.

1 Like

Bumping this… In 2024, are there any new recommendations for getting the response from ANSI escape codes?

1 Like

To read, the console needs to be in raw mode otherwise it buffers the response from the query. I was able to read the response using the following:

function query_terminal(s :: String, esc :: Char) :: String
    run(`stty raw`)
    print(s)
    out :: Vector{Char} = []
    while true
        push!(out, read(stdin, 1)[begin])
        (out[end] == esc) && break
    end
    run(`stty cooked`)
    String(out)
end

Probably not the most effective way to do this, but it works.

2 Likes

I use a similar approach to read my local system clipboard on remote machines:

using Base64: base64decode
using REPL: Terminals

function pbpaste()
    # Put term in raw mode:
    term = Terminals.TTYTerminal("xterm", stdin, stdout, stderr)
    Terminals.raw!(term, true)
    Base.start_reading(stdin)

    # Request the system clipboard with OSC52:
    print(stdout, "\e]52;c;?\a")

    # Decode the response:
    data = readuntil(stdin, "\e\\")
    startswith(data, "\e]52;c;") || return ""
    return String(base64decode(chopprefix(data, "\e]52;c;")))
end

The raw mode part is borrowed from TerminalExtensions.jl. This is really fast (< 1 ms). It uses undocumented functions, though.

The terminal exits the raw mode when the function returns. That’s handy if something goes wrong. I can hit ^C and the terminal goes back to a functional state.

There should be probably a timeout to make it more robust but as you said, it works.

1 Like