Проверьте дополнительные символы в буфере терминала Linux

Я пытаюсь реализовать 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) 

Здесь есть два вопроса:

  1. Почему 4 символа в очереди с обычными нажатиями клавиш отбрасываются? Это из-за перехода на «сырой» режим терминала? Как можно сохранить символы для последующих getch() не покидая терминал в «сыром» режиме?

  2. Почему буфер ioctl для одного специального нажатия клавиши пуст? Откуда берутся эти символы для последующих getch() ? Как их проверить?

  • Linux: Pipe в скрипт Python (ncurses), stdin и termios
  • One Solution collect form web for “Проверьте дополнительные символы в буфере терминала Linux”

    Я столкнулся с этой проблемой. Некоторый поиск дал рабочий пример, который читал не более 4 байт (вместо 1), чтобы разрешить специальные escape-последовательности и использовать os.read (вместо file.read ). Основываясь на этих различиях, я смог написать небольшой класс клавиатуры, который распознавал события клавиш курсора:

     #!/usr/bin/env python import os import select import sys import termios class Keyboard: ESCAPE = 27 LEFT = 1000 RIGHT = 1001 DOWN = 1002 UP = 1003 keylist = { '\x1b' : ESCAPE, '\x1b[A' : UP, '\x1b[B' : DOWN, '\x1b[C' : RIGHT, '\x1b[D' : LEFT, } def __init__(self): self.fd = sys.stdin.fileno() self.old = termios.tcgetattr(self.fd) self.new = termios.tcgetattr(self.fd) self.new[3] = self.new[3] & ~termios.ICANON & ~termios.ECHO self.new[6][termios.VMIN] = 1 self.new[6][termios.VTIME] = 0 termios.tcsetattr(self.fd, termios.TCSANOW, self.new) def __enter__(self): return self def __exit__(self, type, value, traceback): termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old) def getFile(self): return self.fd def read(self): keys = os.read(self.fd, 4) if keys in Keyboard.keylist: return Keyboard.keylist[keys] else: return None if __name__ == "__main__": with Keyboard() as keyboard: key = keyboard.read() while key != Keyboard.ESCAPE: print '%d' % key key = keyboard.read() 

    С file.read(4) – блоки чтения. С os.read(fd, 4) чтение не блокируется. Я не знаю, почему есть разница и приветствую просветление.

    Python - лучший язык программирования в мире.