keypress
keypress copied to clipboard
Prevent echoing of chars with non-block keypress on mac/linux
I am testing the code below on Mac OS 10.13.6, which is not my usual/familiar machine - [edit] - issue also reproducible on ubuntu.
while(TRUE) {
kp <- keypress::keypress(block = TRUE)
if (kp == "X") break
}
When I run Rscript test.R from a Mac terminal with the above code and press some keys, I get no visible output in the terminal, until I press shift+X and it breaks and exits. If I do the same with block=FALSE
, each key I press gets echoed on the terminal, so I end up with something like abcXWes-Mac:~
in the terminal window. I would really like to have "invisible" behaviour for both cases, and have all the side effects of the keypresses coming from my R code.
I can't see any code in keypress-unix.c that would make this conditional on blocking, so I guess it is some property of the linux terminal when set to non-blocking, or perhaps it needs some explicit "consumption" of the input char...?
I think I know how to fix this. I will try and write a PR tomorrow...!
So basically, I think the characters somehow leak through the terminal when fcntl has set the terminal to non-blocking, and the ECHO flag is set on with tcsetattr. That happens (in the original code) I think transiently during keypress_read, but the window is enough for the chars to come out, before read() gets to them, perhaps in a subsequent keypress call.
I've tried to fix this by...
-
in keypress_read, rather than using
|=
to switch on echo and icanon when the function exits, I instead restore the flags to whatever they were at the start of the function call. -
I think for code that wants control of the keyboard, you probably want to be able to turn echo off as soon as you start, and then turn it back on before the script exits (otherwise, you exit into a terminal that doesn't echo your keypresses, which is quite fun). So three new functions to save the terminal state, restore it, and then set the echo flag to what you want.
Re (2): following PR comments, instead of exposing three separate save/restore/set functions above, it is better to expose a single with_no_echo(f) call, which saves the state, turns off echo, calls f, and then restores the state on either a clean or unclean exit.