Closed huyi51462 closed 7 months ago
Do you have some example code we can try out? I haven't seen this issue and I'd like to understand better why it is occurring.
from blessed import Terminal
import sys
Term = Terminal()
with Term.fullscreen():
print("start")
while True:
with Term.cbreak(), Term.hidden_cursor():
print("input_1 : ")
key = Term.inkey()
kc = key.code
if key.code == Term.KEY_ENTER:
print("Entry")
pass
else:
if key.code == Term.KEY_ESCAPE:
print("Esc")
break
continue
try:
print("input_2 : ")
key_1 = sys.stdin.readline().strip()
raise
except Exception as e:
print(f"error : {e}")
print("input_3 : ")
key_2 = Term.inkey()
continue
I debugged this piece of code on Windows 10 (virtual machine) and Windows 11 (physical machine) using VSCode. I entered Enter in input_1 to go into input_2, input some content, triggering an exception. The program ran to input_3, then pressed Enter again to return to input_1. At this point, pressing Esc resulted in an Enter.
This code is the framework for browsing and selecting in my project. It looks a bit odd because some logical parts have been removed, but its actual purpose is for segmenting browsing through a list. input_1 is used to receive page-turning keys, and after pressing Enter, it goes into input_2, which is the selection mode. If there's an error in the selection, it outputs an error message, runs to input_3, where you can input any key, and then loops back again.
I don't know exactly what's happening at the low level. When debugging and tracing into os.read() within Terminal.getch(), I sensed that it might be an issue with the cross-platform library os, as I've encountered some indescribable bugs with this library in other contexts. Therefore, I opted to replace it with msvcrt, and it worked. If you have insights into what's happening at the low level, I'd appreciate your guidance.
I think this is because the try/exception clause is outside of the cbreak context manager, but Term.inkey() is used in the exception block.
inkey() will return only the first character received, but because it is outside of the cbreak context manager it will need a return key to complete, and the return key is buffered until next call to inkey().
inkey() will return only the first character received, but because it is outside of the cbreak context manager it will need a return key to complete, and the return key is buffered until next call to inkey().
Um... I tried again,buff meaning Terminal._keyboard_buf? In the inkey call that triggered the error, it's empty, and the variable 'usc' is also empty. The error 'enter' comes from inkey() -> getch() -> os.read(keyboard_fd, 1). It seems like it's reading a byte from the keyboard device. Is the 'buff' you're referring to related to the keyboard device?
Also, I tested the above code directly in the Terminal(Powershell / Cmd), not the terminal invoked by vscode, and no errors occurred. This issue might be related to the terminal calls in vscode.
the "buffer" in this case is the terminal. By default, stdin is "line-buffered", so input is not "sent" to the application until return key is pressed.
By reading just one byte from stdin, but without cbreak activated, it leaves the remaining bytes in the file descriptor (stdin, 0). Here is a brief example:
>>> import os
>>> os.read(0, 1)
abcd
b'a'
>>> bcd
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'bcd' is not defined
In this case, I instruct to read one byte from stdin, I enter 'abcd\<return>'. The byte 'a' is received, and bytes 'bcd\<return>' remain in stdin, which the python REPL then reads (as an "invalid command").
Here is a rudimentary "readline" example function you could use to stay in cbreak() mode while also providing line input,
https://github.com/jquast/blessed/blob/c28b53fccab1c1966e06c31f58a44b9359838711/bin/editor.py#L36
the "buffer" in this case is the terminal. By default, stdin is "line-buffered", so input is not "sent" to the application until return key is pressed.
By reading just one byte from stdin, but without cbreak activated, it leaves the remaining bytes in the file descriptor (stdin, 0). Here is a brief example:
>>> import os >>> os.read(0, 1) abcd b'a' >>> bcd Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'bcd' is not defined
In this case, I instruct to read one byte from stdin, I enter 'abcd
'. The byte 'a' is received, and bytes 'bcd ' remain in stdin, which the python REPL then reads (as an "invalid command").
Thank you for the detailed explanation. I understand the meaning of the buffer, and I get what you said about 'outside of cbreak, inkey reads only one byte and caches the remaining content.' However, at the input_3 test point, I simply pressed enter once without any other input, and inkey captured this operation. Since key_2 also clearly received "\n," I believe the real issue may not lie here.
Here is a rudimentary "readline" example function you could use to stay in cbreak() mode while also providing line input,
https://github.com/jquast/blessed/blob/c28b53fccab1c1966e06c31f58a44b9359838711/bin/editor.py#L36
Sure,got it!
On the Windows platform, repeatedly entering the cbreak context environment can lead to inkey incorrectly interpreting keyboard input. In my project, for instance, inkey might misinterpret the Escape key as Enter. The solution is to differentiate the underlying getch function, using msvcrt.getch() specifically on Windows.