Я пытаюсь реализовать функцию getch()
в Python, которая также должна возвращать список символов для специальных клавиш, таких как F1-F12 и клавиш со стрелками.Эти специальные ключи генерируют несколько символов в последовательности.Поэтому getch()
читает один символ в режиме блокировки, а затем должен проверить, есть ли дополнительные входные символы во входном буфере для их извлечения.
Я использую вызов ioctl
вместе с termios.FIONREAD чтобы получить количество байтов во входном буфере.Он отлавливает нажатия специальных клавиш, помещенных в буфер, но пропускает дополнительные символы из специальных клавиш.Кажется, что есть два разных буфера, и было бы неплохо, если бы кто-нибудь смог это объяснить.
Вот интерактивный пример:
from time import sleep
def getch():
import sys, tty, termios
fd = sys.stdin.fileno()
# save old terminal settings, because we are changing them
old_settings = termios.tcgetattr(fd)
try:
# set terminal to "raw" mode, in which driver returns
# one char at a time instead of one line at a time
#
# tty.setraw() is just a helper for tcsetattr() call, see
# http://hg.python.org/cpython/file/c6880edaf6f3/Lib/tty.py
tty.setraw(fd)
ch = sys.stdin.read(1)
# --- check if there are more characters in buffer
from fcntl import ioctl
from array import array
sleep(1)
buf = array('i', [0])
ioctl(fd, termios.FIONREAD, buf)
print "buf queue: %s," % buf[0],
# ---
finally:
# restore terminal settings. Do this when all output is
# finished - TCSADRAIN flag
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
char = ''
while char != 'q':
char = getch()
print 'sym: %s, ord(%s)' % (char, ord(char))
Примечание sleep(1)
в середине.Если вы нажмете одну клавишу до истечения этой секунды, вы получите:
buf queue: 0, sym: l, ord(108)
Для 5 обычных клавиш (например, asdfg), введенных за одну секунду, вы получите:
buf queue: 4, sym: a, ord(97)
но для одной клавиши со стрелкой вывод:
buf queue: 0, sym: , ord(27)
buf queue: 0, sym: [, ord(91)
buf queue: 0, sym: D, ord(68)
Здесь есть два вопроса:
Почему 4 символа в очереди с обычными нажатиями клавишотбрасываются?Это из-за переключения в «сырой» режим терминала?Как можно сохранить символы для последующих getch()
запусков, не оставляя терминал в «сыром» режиме?
Почему буфер ioctl
для нажатия одной специальной клавиши пуст?Откуда берутся эти персонажи для последующих getch()
прогонов?Как их проверить?