How to use IO.binstream to read/write to stdout/stdin

Hey I’m playing with kitty protocol and my first task is to write “\e[14t” to stdout and read the response. I’m struggling to work out how to make it work.

What is the correct way to use IO.binstream?

I suppose you read the docs. What have you tried so far?

IO.puts("\e[14t;")
input = IO.gets(:stdio)
IO.puts(input)
binstream = IO.binstream(:stdio, :line)

binstream |> IO.write("\e[14t;")
inputs = IO.read(binstream, :line)
IO.puts(inputs)

I’m struggling to understand how the docs relate to the libc/POSIX concepts tbh

I found Get cursor position in command line (Elixir) - Stack Overflow but it seems like they have the same problem.

I looked at the IO.ANSI stuff but it looks like they only write to the terminal, and don’t read say - cursor position.

So after a bunch of hacking i found something that works, kinda… (testing in ghostty btw Ghostty Docs)

    IO.binwrite("\e[14t;")
    output = IO.binread(:line)

    output_fixed = IO.chardata_to_string(output)
    IO.inspect(Regex.named_captures(~r/^\e\[4;(?<height_px>\d+);(?<width_px>\d+)t/, output_fixed))

it’s not perfect - you have to hit enter to trigger the :line in the binread, and it doesn’t work in IEX.

Any tips would be appreciated :slight_smile:

I thought i could do IO.binread(3) to see if the terminal gives back the ["\e", "[", "4"] sequence, before reading until i get the t character, but for some reason the IO.binread needs enter to be applied before it works. it’s like it’s line buffering somehow.

FWIW, raw mode is coming in OTP 28:

2 Likes