Reputation: 25
I would like to read from stdin
on a per character basis without the stdin
being flushed. I could not find how to do that after tweaking for hours. Tcl always seems to wait for the channel to be flushed even in fconfigure stdin -blocking 0 -buffering none
. Is that true? How would I otherwise approach this?
More explanation:
Imagine a Tcl program that makes its own prompt with some threads running code in the background. I would like this prompt to react to single keystrokes, for example: when you press 'p' (without pressing enter
) the prompt reads that character and pauses the threads, when you press 'q' the prompt kills the threads and stops the program. The cleanest and closest solution is shortly demonstrated in the following code snippet.
proc readPrompt { } {
set in [ read stdin 1 ]
if { $in eq "q" } {
puts "Quitting..."
set ::x 1
} {
puts "Given unknown command $in"
}
}
fconfigure stdin -blocking 0 -buffering none
fileevent stdin readable { readPrompt }
vwait x
The result from running this is:
a
Given unknown command a
Given unknown command
After pressing the 'a', nothing happens. My guess is that the stdin
is not flushed or something. Pressing enter
or CTRL-d
triggers the fileevent and the prompt then reads both the characters 'a' and 'enter
'.
Ideally, I want the enter
-press not to be needed. How could I accomplish this?
EDIT: I found this question and solution about a related use in Python: Determine the terminal cursor position with an ANSI sequence in Python 3 This is approximately the behaviour I'm looking for, but in Tcl.
Upvotes: 1
Views: 714
Reputation: 137567
If you have 8.7 (currently in alpha) then this is “trivial”:
fconfigure stdin -inputmode raw
That delivers all characters to you, without echoing them. (There's also modes normal
and password
, both of which preprocess the data before delivery and only one of which echoes.) You'll have to look after giving visual feedback to the user yourself, and be aware that all includes all characters usually only used for line editing purposes.
Otherwise, on Unixes (Linux, macOS) you do:
exec stty raw -echo <@stdin >@stdout
to switch the mode to the same config, and:
exec stty -raw echo <@stdin >@stdout
to switch back. (Not all Unixes need the input and output redirects, but some definitely do.)
Windows consoles have something similar in 8.7, but not in previous versions; a workaround might be possible using the TWAPI console support but that's a very low level API (and I don't know the details).
Upvotes: 1