从sys.stdin读取转义序列,转义后的字节将延迟到下一次按键时使用select。

huangapple go评论59阅读模式
英文:

reading escaped sequences from sys.stdin, bytes after escape are delayed until the next keystroke using select

问题

我正在尝试在Linux中处理按键输入,以便处理箭头键以及普通的字母数字键等。这种潜在简单的方法使用select和stdin可以接收所有按键,但在按下(例如)向上箭头后,我不会立即收到转义后的额外字符,直到我按下另一个键。

这些额外的字符存在,如果我读取它们,但如果它们不存在(例如只按下转义键),那么尝试读取将挂起输入,并且当我获得下一个字符读取时,我不知道它是否是转义序列的一部分(除非查看时间戳)。

我尝试过其他包,但它们都存在问题 - 需要root权限,或者只在有屏幕的情况下工作,例如。

我想要在PC上直接运行此代码,或者通过SSH连接到仅运行服务器的树莓派上。

这是一个简单的测试程序:

#!/usr/bin/env python

import sys
import tty
import termios
import select, time

old_settings = termios.tcgetattr(sys.stdin)
ts = time.time()
try:
    tty.setraw(sys.stdin.fileno())
    while True:
        rlist, _, _ = select.select([sys.stdin], [], [], 2)
        if rlist:
            # Read a single character
            char = sys.stdin.read(1)
            if ord(char) == 27:
                print('at %6.2f ESCAPE!' % (time.time()-ts), '\x0d')
                # at this point any other characers can be read, but you can't check to see if read will block!!!
            else:
                print('at %6.2f gotta' % (time.time()-ts), char if ord(char) > 32 else '{%d}' % ord(char), '\x0d')
                if char == 'x':
                    break
finally:
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
    print('byeeeee')

如果我输入'abcde',你可以看到在按下e之前,转义后的向上箭头的合格字符不会出现。如果我在没有select的情况下运行相同的代码,那么时间戳是正确的,但代码会阻塞等待所有键盘输入,因此无法响应其他事件。

取消注释rlist行,一切都按预期工作:
按下向上箭头键会给我[A,时间戳几乎相同:

$ python3 inputer3.py
at   1.38 gotta a 
at   2.44 gotta b 
at   3.72 gotta c 
at   4.88 ESCAPE! 
at   9.32 gotta d 
at  10.51 ESCAPE! 
at  13.42 gotta [ 
at  13.42 gotta A 
at  13.42 gotta e 

将rlist行注释掉后,一切都按预期工作:
按下向上箭头键会给我[A,时间戳几乎相同:

$ python3 inputer3.py
at   1.33 gotta a 
at   2.10 gotta b 
at   2.93 gotta c 
at   4.66 ESCAPE! 
at   5.58 gotta d 
at   7.88 ESCAPE! 
at   7.88 gotta [ 
at   7.88 gotta A 
at  10.44 gotta e 

希望这有助于你理解代码的问题。

英文:

I'm trying to process keystrokes in linux so I can handle arrow keys as well as normal alphnumeric etc keys.
This potentially simple approach using select and stdin delivers all the keys, but after pressing (for example) uparrow, I don't get the extra chars after escape until I press another key.

The extra characters are there if I read them, but if they aren't there (as in just pressing escape), then trying to read will hang the input and when I get the next char read I don 't know if it was part of an escape sequence or not (unless I look at timestamps)

I have tried other packages but they all have problems - requiring root, or only working if there is a screen present for example.

I want to run this code both directly on a PC, or over ssh to a raspberry pi with a server only build.

Here is a trivial test program:

#!/usr/bin/env python

import sys
import tty
import termios
import select, time

old_settings = termios.tcgetattr(sys.stdin)
ts = time.time()
try:
    tty.setraw(sys.stdin.fileno())
    while True:
        rlist, _, _ = select.select([sys.stdin], [], [], 2)
        if rlist:
            # Read a single character
            char = sys.stdin.read(1)
            if ord(char) == 27:
                print('at %6.2f ESCAPE!' % (time.time()-ts), '\x0d')
                # at this point any other characers can be read, but you can't check to see if read will block!!!
            else:
                print('at %6.2f gotta' % (time.time()-ts), char if ord(char) >32 else '{%d}' % ord(char), '\x0d')
                if char == 'x':
                    break
finally:
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
    print('byeeeee')

If I type 'abc<esc>d<uparrow>e' you can see the qualifying chars after esc for uparrow don't appear until I type e. If I run the same code without select, then the timings are correct, but the code blocks waiting for all keyboard input so I cannot respond to other events.

$ python3 inputer3.py
at   1.38 gotta a 
at   2.44 gotta b 
at   3.72 gotta c 
at   4.88 ESCAPE! 
at   9.32 gotta d 
at  10.51 ESCAPE! 
at  13.42 gotta [ 
at  13.42 gotta A 
at  13.42 gotta e 

Commenting out the rlist lines it all works as expected:
pressing uparrow gives me <esc>[A all with almost identical timestampsL

$ python3 inputer3.py
at   1.33 gotta a 
at   2.10 gotta b 
at   2.93 gotta c 
at   4.66 ESCAPE! 
at   5.58 gotta d 
at   7.88 ESCAPE! 
at   7.88 gotta [ 
at   7.88 gotta A 
at  10.44 gotta e 

答案1

得分: 1

Instead of sys.stdin.read, use os.read :

#!/usr/bin/env python

import os, sys
import tty
import termios
import select, time

old_settings = termios.tcgetattr(sys.stdin)
ts = time.time()
try:
    tty.setraw(sys.stdin.fileno())
    while True:
        rlist, _, _ = select.select([sys.stdin], [], [], 2)
        if rlist:
            # Read a single character
            # char = sys.stdin.read(1)
            char = os.read(sys.stdin.fileno(), 1).decode("utf-8")
            if ord(char) == 27:
                print('at %6.2f ESCAPE!' % (time.time()-ts), '\x0d')
                # at this point any other characters can be read, but you can't check to see if read will block!!!
            else:
                print('at %6.2f gotta' % (time.time()-ts), char if ord(char) > 32 else '{%d}' % ord(char), '\x0d')
                if char == 'x':
                    break
finally:
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
    print('byeeeee')

(Note: The code portion has been translated as requested.)

英文:

Instead of sys.stdin.read, use os.read :

#!/usr/bin/env python

import os, sys
import tty
import termios
import select, time

old_settings = termios.tcgetattr(sys.stdin)
ts = time.time()
try:
    tty.setraw(sys.stdin.fileno())
    while True:
        rlist, _, _ = select.select([sys.stdin], [], [], 2)
        if rlist:
            # Read a single character
            # char = sys.stdin.read(1)
            char = os.read(sys.stdin.fileno(), 1).decode(&quot;utf-8&quot;)
            if ord(char) == 27:
                print(&#39;at %6.2f ESCAPE!&#39; % (time.time()-ts), &#39;\x0d&#39;)
                # at this point any other characers can be read, but you can&#39;t check to see if read will block!!!
            else:
                print(&#39;at %6.2f gotta&#39; % (time.time()-ts), char if ord(char) &gt;32 else &#39;{%d}&#39; % ord(char), &#39;\x0d&#39;)
                if char == &#39;x&#39;:
                    break
finally:
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
    print(&#39;byeeeee&#39;)

huangapple
  • 本文由 发表于 2023年5月24日 20:30:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76323573.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定