检查Linux终端缓冲区中是否有其他字符 [英] Check for extra characters in Linux terminal buffer

查看:145
本文介绍了检查Linux终端缓冲区中是否有其他字符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试在Python中实现getch()函数,该函数还应为特殊键(例如F1-F12和箭头键)返回字符列表.这些特殊键按顺序生成几个字符.因此,getch()在阻塞模式下读取一个字符,然后应检查输入缓冲区中是否还有多余的字符来提取它们.

I try to implement getch() function in Python, which should also return list of chars for special keys like F1-F12 and arrow keys. These special keys generate several chars in a sequence. Therefore getch() reads one char in blocking mode and then should check if there are extra chars in input buffer to fetch them too.

我正在将ioctl呼叫与 termios.FIONREAD 一起使用以获取电话号码输入缓冲区中的字节数.它捕获堆积在缓冲区中的非特殊按键,但是错过了特殊按键中的多余符号.似乎有两个不同的缓冲区,如果有人可以解释一下,那就太好了.

I am using ioctl call together with termios.FIONREAD to get the number of bytes in the input buffer. It catches non-special key presses stacked in buffer, but misses extra symbols from special keys. It seems like there are two different buffers and it would be nice if somebody could explain this.

这是交互式示例:

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).如果您在第二秒到期之前按了一个键,则输出将是:

Note sleep(1) in the middle. If you hit one key before this second expires, the output will be:

buf queue: 0, sym: l, ord(108)

对于在一秒钟内输入的5个普通键(例如'asdfg'),输出为:

For 5 ordinary keys (for example 'asdfg') entered in one second, the output is:

buf queue: 4, sym: a, ord(97)

但对于单个箭头键,输出:

but for a single arrow key, the output:

buf queue: 0, sym: , ord(27)
buf queue: 0, sym: [, ord(91)
buf queue: 0, sym: D, ord(68)

这里有两个问题:

  1. 为什么在使用普通按键的队列中会丢弃4个符号?是因为切换到原始"终端模式吗?

  1. Why 4 symbols in queue with ordinary key presses are discarded? Is it because of switch to "raw" terminal mode? How is it possible to preserve chars for subsequent getch() runs without leaving terminal in "raw" mode?

为什么单个特殊按键的ioctl缓冲区为空?那些字符随后的getch()运行来自何处​​?如何检查它们?

Why the ioctl buffer for a single special key press is empty? Where are those characters are coming from for subsequent getch() runs? How to check for them?

推荐答案

我遇到了同样的问题.一些搜索产生了一个工作示例,该示例读取最多4个字节(而不是您的1个字节)以允许特殊的转义序列,并使用了os.read(而不是您的file.read).基于这些差异,我能够编写一个小型的Keyboard类来识别光标键事件:

I ran into this same issue. Some searching yielded a working example that read at most 4 bytes (instead of your 1) to allow for special escape sequences and used os.read (instead of your file.read). Based on those differences, I was able to write a little Keyboard class that recognized cursor key events:

#!/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),不会阻塞读数.我不知道为什么会有所不同,欢迎您给予启发.

With file.read(4), the reading blocks. With os.read(fd, 4), the reading does not block. I don't know why there's a difference and would welcome enlightenment.

这篇关于检查Linux终端缓冲区中是否有其他字符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆