The code
import streams
var
x:int8
y:char
fs:FileStream
fs = newFileStream(stdin)
x = fs.readInt8()
fs.flush()
y = fs.readChar()
echo "x = ",x
echo "y = ",y
when work in windwos,The x, y, will wait for input, respectively. But in Linux,if input two chars at first, then it will output x and y directly. Procedure flush seems doesn't work. why?The underlying problem is that on Unix, stdin is line-buffered by default, and that happens at the terminal driver level. So, the terminal has to be told to process input character by character ("raw mode"). This is done through system calls, and terminal.getch normally accomplishes that.
Unfortunately, terminal.getch has issues. On Linux, because it drops the terminal out of raw mode intermittently, it may actually not behave as intended if time passes between reads (because the terminal gets dropped out of raw mode in between); on OS X, it still requires you hitting Ctrl-J to get a newline.
A more portable (but somewhat hackish) workaround is the following, which uses the external stty command to keep the terminal in raw mode for the duration of a block:
import streams, osproc
template withRawTerminal(body: typed) =
block:
proc testError(error: int) =
if error != 0:
raise newException(OSError, "stty error")
const stty_cmd = "stty </dev/tty "
let (stty_reset, error) = execCmdEx(stty_cmd & "-g")
testError(error)
defer:
testError execCmd(stty_cmd & stty_reset)
testError execCmd(stty_cmd & "cbreak")
body
var
x:int8
y:char
fs:FileStream
withRawTerminal:
fs = newFileStream(stdin)
x = fs.readInt8()
fs.flush()
y = fs.readChar()
echo "x = ", x
echo "y = ", y
The ncurses nimble package also has the necessary functionality, but it takes over the screen, which may not be what you want.
Most things terminal character-at-a-time in Linux or Unix in general use CBREAK mode for this type of thing, not RAW. RAW also disables echo and so terminal.getch() seems to have been introduced to be type-a-password-friendly. Personally, when I mistype a passwd I like to be able to hit Ctrl-U to start over and RAW probably prevents that. Just disabling echo is better for passwords and just doing CBREAK is better for char-at-a-time. RAW mushes the two settings together. I could also imagine shells or tty libraries detecting RAW and/or non-echo and restoring settings as an "aid" to "unstick" users..some "stty sane" equivalent. [ That may be causing the trouble Jehan sees intermittently. strace should be able to show you what is going on more definitively with readline/bash/zsh/whatever across such weirdness if it's at all reproducible. ]
So, really some kind of terminal.nim clean-up is probably warranted someday. rltty.c:prepare_terminal_settings() in libreadline is probably a better guide for how terminal.nim should go about things than the present terminal.getch() approach.
Thanks for your reply!
@Varriount, I try to use ordinary file to test flush, if use flush procedure then the fileStream's content will disappear in windows, but in Linux the content still exist.
Thank you very much! I think I have a lot of knowledge about this problem to learn.