Termion IntoRawMode: read from stdin error

This may be more of a question about terminals or stdin/stdout. Not sure if this is the best place to ask this, but here goes:

I’m writing a command line application that I need a mini pager for quickly displaying a long list of data (much like more or less, with simple keyboard navigation) without cluttering up the terminal window once the app is done. I used termion::screen::AlternateScreen to accomplish this, along with a slightly modified version of redox-os/libpager.

It works great, except for when I need my application to read piped data from stdin. For the pager to capture keystrokes for page navigation, we need to call stdout.into_raw_mode()?. When there is data in stdin (from a pipe) this function returns an error:

Error: Os { code: 25, kind: Other, message: "Inappropriate ioctl for device" }

I can capture the piped data just fine, it’s just that I think stdin is blocking me from entering RawMode for capturing keystrokes. I’ve tried all combinations of flushing stdin/stdout, starting new instances of stdin/stdout as well as new AlternateScreens. I get this error on the redox pager implementation of less as well. Is there a better way to accomplish this, or am I just missing something?

My suspicion is that you’re (1) on a Unix-like operating system, and (2) attempting to set raw mode on a pipe, which won’t work (as it is not a tty). You could make the operation conditional based on the result of termion::is_tty().

Your suspicion is correct. I get this error on both Linux and MacOS. How can I capture the data from the pipe, and put it into a new tty for the pager?

It sounds like the issue is that you’re trying to use stdin for both data input and user input. It can’t be both. If stdin is coming from a pipe, (1) you don’t need to put it into raw mode, and (2) you won’t be able to read keystrokes over stdin.

You can decouple the notion of “tty input” from stdin by using termion::get_tty(). This opens /dev/tty behind the scenes and will always refer to the controlling terminal. You’ll want to use the result of that call as your reader/writer for user interaction instead of stdin/stdout. That’s probably the stream you want to put into raw mode – if you’re writing a pager, you can probably leave the data input (stdin) line-buffered.

1 Like