BenJay10
BenJay10

Reputation: 25

Tcl - Reading characters from stdin without having to press enter in Tcl

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

Answers (1)

Donal Fellows
Donal Fellows

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

Related Questions