11import sys
22import termios
33from copy import copy
4+ from io import StringIO
45from select import select
56
67from ._config import config
@@ -12,53 +13,71 @@ class ReadChar:
1213
1314 def __init__ (self , cfg : config = None ) -> None :
1415 self .config = cfg if cfg is not None else config
16+ self ._buffer = StringIO ()
1517
1618 def __enter__ (self ) -> "ReadChar" :
1719 self .fd = sys .stdin .fileno ()
1820 term = termios .tcgetattr (self .fd )
1921 self .old_settings = copy (term )
20- term [3 ] &= ~ (termios .ICANON | termios .ECHO | termios .IGNBRK | termios .BRKINT )
22+
23+ term [3 ] &= ~ (
24+ termios .ICANON # don't require ENTER
25+ | termios .ECHO # don't echo
26+ | termios .IGNBRK
27+ | termios .BRKINT
28+ )
29+ term [6 ][termios .VMIN ] = 0 # imideatly process every input
30+ term [6 ][termios .VTIME ] = 0
2131 termios .tcsetattr (self .fd , termios .TCSAFLUSH , term )
2232 return self
2333
2434 def __exit__ (self , type , value , traceback ) -> None :
25- termios .tcsetattr (self .fd , termios .TCSADRAIN , self .old_settings )
35+ termios .tcsetattr (self .fd , termios .TCSAFLUSH , self .old_settings )
36+
37+ def __update (self ) -> None :
38+ """check stdin and update the interal buffer if it holds data"""
39+ if sys .stdin in select ([sys .stdin ], [], [], 0 )[0 ]:
40+ pos = self ._buffer .tell ()
41+ data = sys .stdin .read ()
42+ self ._buffer .write (data )
43+ self ._buffer .seek (pos )
2644
2745 @property
2846 def key_waiting (self ) -> bool :
2947 """True if a key has been pressed and is waiting to be read. False if not."""
30- return sys .stdin in select ([sys .stdin ], [], [], 0 )[0 ]
48+ self .__update ()
49+ pos = self ._buffer .tell ()
50+ next_byte = self ._buffer .read (1 )
51+ self ._buffer .seek (pos )
52+ return bool (next_byte )
3153
3254 def char (self ) -> str :
3355 """Reads a singel char from the input stream and returns it as a string of
3456 length one. Does not require the user to press ENTER."""
35- return sys .stdin .read (1 )
57+ self .__update ()
58+ return self ._buffer .read (1 )
3659
3760 def key (self ) -> str :
3861 """Reads a keypress from the input stream and returns it as a string. Keypressed
3962 consisting of multiple characterrs will be read completly and be returned as a
4063 string matching the definitions in `key.py`.
4164 Does not require the user to press ENTER."""
42- c1 = self .char ()
65+ self .__update ()
4366
67+ c1 = self .char ()
4468 if c1 in self .config .INTERRUPT_KEYS :
4569 raise KeyboardInterrupt
46-
4770 if c1 != "\x1B " :
4871 return c1
49-
5072 c2 = self .char ()
5173 if c2 not in "\x4F \x5B " :
5274 return c1 + c2
53-
5475 c3 = self .char ()
5576 if c3 not in "\x31 \x32 \x33 \x35 \x36 " :
5677 return c1 + c2 + c3
57-
5878 c4 = self .char ()
5979 if c4 not in "\x30 \x31 \x33 \x34 \x35 \x37 \x38 \x39 " :
6080 return c1 + c2 + c3 + c4
61-
6281 c5 = self .char ()
6382 return c1 + c2 + c3 + c4 + c5
6483
0 commit comments