1

I've been looking into a way to implement a "Press any key to continue" feature into my program, and figured I'd use $stdin.getch.

Seems to work in most cases, but I noticed that if I pressed an arrow key when prompted to press any key, the next gets command would have "parasites" characters at the beginning of the string it returned, presumably because arrow keys give several characters as input and only the first one is read by getch, with the rest being saved for the next gets. More precisely, when I press the Right Arrow key, getch returns "\e" and the next gets return has a "[C" prefixed to it.

I thought maybe I had to flush/empty the stdin buffer, but flush, iflush and ioflush don't seem to do anything. Trying to write '' into stdin before the gets doesn't work either. I've also tried sync and fsync on $stdin, but it doesn't work either. Any idea what the problem could be?

Qwib
  • 13
  • 4
  • Maybe you have to `getch` an additional character if you first is `\e` – max pleaner Dec 08 '22 at 23:15
  • Some keys are configured to generate more than one character. These ANSI escape sequences (like `ESC [C` for "cursor forward") are interpreted by your terminal. If you merely want to "clear" the input buffer, a small loop like `$stdin.getch while $stdin.ready?` right after the first `$stdin.getch` should work. If you want to get the actual key see https://stackoverflow.com/q/49541348/477037 – Stefan Dec 09 '22 at 17:34
  • @max pleaner Thanks! It worked. I had to getch two times in a row (to clear the "[" and the "C"), but otherwise it worked like a charm. – Qwib Dec 09 '22 at 18:56
  • @Stefan Yeah, that worked too. Just had to require io/wait first, but it seems to work just fine. Thank you too! – Qwib Dec 09 '22 at 18:57

1 Answers1

0

getch puts the IO in so-called raw mode and then reads a single character.

However, there are keys that generate more than one character when being pressed. The right arrow key for example is usually configured to generate the 3-byte sequence \e[C which is the ANSI escape sequence for "cursor forward".

Now, if you attempt to read that sequence via getch, you'll only get \e, i.e. the very first character. All subsequent characters (here: [ and C) will be returned by the next read operation.

Instead of reading a single character via getch, you could put stdin into raw mode manually and read all available characters (up to a reasonable limit) via readpartial, e.g.:

$stdin.raw { |io| io.readpartial(100) }

The above returns the whole "\e[C" sequence when pressing and behaves like getch for single-character keys.

Note that terminal emulators and operating systems behave differently. The above works just fine for me, but you might want to test the solution on various systems yourself.

Stefan
  • 109,145
  • 14
  • 143
  • 218