Search code examples
pythonpython-3.xstdintermios

Read multi-character keyboard strokes


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?


Solution

  • 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.