1

I have a script to read and handle keyboard strokes within python. This works perfectly fine for me for standard keys which send one byte to stdin. I cannot find a reasonable way to read keystrokes that produce a multi-byte ansi escape code. What do I need to do to be able to read all available data from stdin?

System: OSX, Python 3.4

Here is my minimal example code:

import sys
import termios
import select

# Save the terminal settings
fd = sys.stdin.fileno()
new_term = termios.tcgetattr(fd)
old_term = termios.tcgetattr(fd)

# New terminal setting unbuffered
new_term[3] = (new_term[3] & ~termios.ICANON & ~termios.ECHO)
termios.tcsetattr(fd, termios.TCSAFLUSH, new_term)

while sys.stdin in select.select([sys.stdin], [], [], 10.0)[0]:
    char = sys.stdin.buffer.read(1)
    print('User input: {}'.format(char))

    if char == b'q':
        break

termios.tcsetattr(fd, termios.TCSAFLUSH, old_term)


Expected/Desired Behavior

When I start the script and press the right arrow button I would expect the output to be:

b'\x1b'
b'['
b'C'

What I actually get is:

b'\x1b'

If I then press any other key, everything else gets read. For example if I now press 'x' I get:

b'['
b'C'
b'x'

How can I get all three bytes with the initial key press?

amicitas
  • 13,053
  • 5
  • 38
  • 50
  • 1
    `sys.stdin` is the wrong abstraction for this. It's just a file that contains bytes; any information about what keystrokes *produced* those bytes is lost. You need to interact directly with the keyboard, which I don't think is possible using the standard library alone. – chepner Jul 20 '16 at 20:16
  • I guess maybe a more specific question here is why is `select.select` not properly reporting that `sys.stdin` is available for reading? – amicitas Jul 20 '16 at 20:19

1 Answers1

1

When you encounter an \x1b, wait for the rest of the escape sequence. Then leave a timeout, just in case the user pressed esc alone. Vim does that, as it is the only way to do it.

Min4Builder
  • 148
  • 1
  • 8