You've deliberately chosen a getch
implementation that avoids queueing and only grabs the latest character.
You can service input faster by moving the # Do Something #
part to a background thread, or use an event-loop-like implementation where you queue up all available characters before trying to process the first one off the queue, etc. But none of that will guarantee you get all characters.
If you want to allow characters to queue up to make sure you don't miss any, just… don't use an implementation that's designed not to let them queue up.
If you don't need getch
to timeout after 2 seconds, you can just do this by just not calling select
. That also means you don't need TCSADRAIN
. Also, instead of calling setraw
, try turning off just the flags you care about. Disabling ICANON
is enough to make it read character by character.
Here's a complete test:
import select
import sys
import termios
import time
import tty
def getch():
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
new_settings = old_settings[:]
new_settings[3] ~= ~termios.ICANON
try:
termios.tcsetattr(fd, termios.TCSANOW, new_settings)
ch=sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSANOW, old_settings)
return ch
while True:
ch = getch()
if ch == '\x03':
break
print "Queue: " + ch
time.sleep(1)
And here's the output:
aQueue: a
bcdefgQueue: b
Queue: c
Queue: d
Queue: e
Queue: f
Queue: g
^CTraceback (most recent call last):
File "getch.py", line 24, in <module>
ch = getch()
File "getch.py", line 16, in getch
ch=sys.stdin.read(1)
KeyboardInterrupt
If you do want a timeout, but don't want to throw away extra characters, that's easy; just use termios
to set it, instead of making an end-run around it:
new_settings[-1][termios.VTIME] = 20
new_settings[-1][termios.VMIN] = 0
Now the output looks like this:
aQueue: a
bcdQueue: b
Queue: c
Queue: d
Queue:
Queue:
^CTraceback (most recent call last):
File "getch.py", line 30, in <module>
time.sleep(1)
KeyboardInterrupt
There are many more options in termios
—if you wanted the no-echo functionality, ^C-swallowing, etc. of the implementation you were using, read the man page.
Also, if you want to see exactly what setraw
does, just run this:
import sys, termios, tty
fd = sys.stdin.fileno()
o = termios.tcgetattr(fd)
tty.setraw(fd)
n = termios.tcgetattr(fd)
tcsetattr(fd, termios.TCSANOW, o)
print('iflag : {:10x} {:10x}'.format(o[0], n[0]))
print('oflag : {:10x} {:10x}'.format(o[1], n[1]))
print('cflag : {:10x} {:10x}'.format(o[2], n[2]))
print('lflag : {:10x} {:10x}'.format(o[3], n[3]))
print('ispeed: {:10d} {:10d}'.format(o[4], n[4]))
print('ospeed: {:10d} {:10d}'.format(o[5], n[5]))
print('cc :')
for i, (oo, nn) in enumerate(zip(o[6], n[6])):
print(' {:4d}: {:>10} {:>10}'.format(i, oo, nn))
Then you can look at the termios
Python module, manpage, and C header (/usr/include/sys/termios.h
) to see what each of the different bit mean in each field.