@@ -81,7 +81,7 @@ def __init__(self, stdin: TextIO | None = None) -> None:
8181 self ._use_virtual_terminal_input = _is_win_vt100_input_enabled ()
8282
8383 if self ._use_virtual_terminal_input :
84- self .console_input_reader = Vt100InputReader ()
84+ self .console_input_reader = Vt100ConsoleInputReader ()
8585 else :
8686 self .console_input_reader = ConsoleInputReader ()
8787
@@ -135,88 +135,6 @@ def handle(self) -> HANDLE:
135135 return self .console_input_reader .handle
136136
137137
138- class Vt100InputReader :
139- def __init__ (self ) -> None :
140- self ._fdcon = None
141-
142- self ._buffer : list [KeyPress ] = [] # Buffer to collect the Key objects.
143- self ._vt100_parser = Vt100Parser (
144- lambda key_press : self ._buffer .append (key_press )
145- )
146-
147- # When stdin is a tty, use that handle, otherwise, create a handle from
148- # CONIN$.
149- self .handle : HANDLE
150- if sys .stdin .isatty ():
151- self .handle = HANDLE (windll .kernel32 .GetStdHandle (STD_INPUT_HANDLE ))
152- else :
153- self ._fdcon = os .open ("CONIN$" , os .O_RDWR | os .O_BINARY )
154- self .handle = HANDLE (msvcrt .get_osfhandle (self ._fdcon ))
155-
156- def close (self ) -> None :
157- "Close fdcon."
158- if self ._fdcon is not None :
159- os .close (self ._fdcon )
160-
161- def read (self ) -> Iterable [KeyPress ]:
162- """
163- Return a list of `KeyPress` instances. It won't return anything when
164- there was nothing to read. (This function doesn't block.)
165-
166- http://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx
167- """
168- max_count = 2048 # Max events to read at the same time.
169-
170- read = DWORD (0 )
171- arrtype = INPUT_RECORD * max_count
172- input_records = arrtype ()
173-
174- # Check whether there is some input to read. `ReadConsoleInputW` would
175- # block otherwise.
176- # (Actually, the event loop is responsible to make sure that this
177- # function is only called when there is something to read, but for some
178- # reason this happened in the asyncio_win32 loop, and it's better to be
179- # safe anyway.)
180- if not wait_for_handles ([self .handle ], timeout = 0 ):
181- return
182-
183- # Get next batch of input event.
184- windll .kernel32 .ReadConsoleInputW (
185- self .handle , pointer (input_records ), max_count , pointer (read )
186- )
187-
188- # First, get all the keys from the input buffer, in order to determine
189- # whether we should consider this a paste event or not.
190- for key_data in self ._get_keys (read , input_records ):
191- self ._vt100_parser .feed (key_data )
192-
193- # Return result.
194- result = self ._buffer
195- self ._buffer = []
196- return result
197-
198- def _get_keys (
199- self , read : DWORD , input_records : Array [INPUT_RECORD ]
200- ) -> Iterator [str ]:
201- """
202- Generator that yields `KeyPress` objects from the input records.
203- """
204- for i in range (read .value ):
205- ir = input_records [i ]
206-
207- # Get the right EventType from the EVENT_RECORD.
208- # (For some reason the Windows console application 'cmder'
209- # [http://gooseberrycreative.com/cmder/] can return '0' for
210- # ir.EventType. -- Just ignore that.)
211- if ir .EventType in EventTypes :
212- ev = getattr (ir .Event , EventTypes [ir .EventType ])
213-
214- # Process if this is a key event. (We also have mouse, menu and
215- # focus events.)
216- if isinstance (ev , KEY_EVENT_RECORD ) and ev .KeyDown :
217- yield ev .uChar .UnicodeChar
218-
219-
220138class ConsoleInputReader :
221139 """
222140 :param recognize_paste: When True, try to discover paste actions and turn
@@ -648,6 +566,102 @@ def _handle_mouse(self, ev: MOUSE_EVENT_RECORD) -> list[KeyPress]:
648566 return [KeyPress (Keys .WindowsMouseEvent , data )]
649567
650568
569+ class Vt100ConsoleInputReader :
570+ """
571+ Similar to `ConsoleInputReader`, but for usage when
572+ `ENABLE_VIRTUAL_TERMINAL_INPUT` is enabled. This assumes that Windows sends
573+ us the right vt100 escape sequences and we parse those with our vt100
574+ parser.
575+
576+ (Using this instead of `ConsoleInputReader` results in the "data" attribute
577+ from the `KeyPress` instances to be more correct in edge cases, because
578+ this responds to for instance the terminal being in application cursor keys
579+ mode.)
580+ """
581+
582+ def __init__ (self ) -> None :
583+ self ._fdcon = None
584+
585+ self ._buffer : list [KeyPress ] = [] # Buffer to collect the Key objects.
586+ self ._vt100_parser = Vt100Parser (
587+ lambda key_press : self ._buffer .append (key_press )
588+ )
589+
590+ # When stdin is a tty, use that handle, otherwise, create a handle from
591+ # CONIN$.
592+ self .handle : HANDLE
593+ if sys .stdin .isatty ():
594+ self .handle = HANDLE (windll .kernel32 .GetStdHandle (STD_INPUT_HANDLE ))
595+ else :
596+ self ._fdcon = os .open ("CONIN$" , os .O_RDWR | os .O_BINARY )
597+ self .handle = HANDLE (msvcrt .get_osfhandle (self ._fdcon ))
598+
599+ def close (self ) -> None :
600+ "Close fdcon."
601+ if self ._fdcon is not None :
602+ os .close (self ._fdcon )
603+
604+ def read (self ) -> Iterable [KeyPress ]:
605+ """
606+ Return a list of `KeyPress` instances. It won't return anything when
607+ there was nothing to read. (This function doesn't block.)
608+
609+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx
610+ """
611+ max_count = 2048 # Max events to read at the same time.
612+
613+ read = DWORD (0 )
614+ arrtype = INPUT_RECORD * max_count
615+ input_records = arrtype ()
616+
617+ # Check whether there is some input to read. `ReadConsoleInputW` would
618+ # block otherwise.
619+ # (Actually, the event loop is responsible to make sure that this
620+ # function is only called when there is something to read, but for some
621+ # reason this happened in the asyncio_win32 loop, and it's better to be
622+ # safe anyway.)
623+ if not wait_for_handles ([self .handle ], timeout = 0 ):
624+ return
625+
626+ # Get next batch of input event.
627+ windll .kernel32 .ReadConsoleInputW (
628+ self .handle , pointer (input_records ), max_count , pointer (read )
629+ )
630+
631+ # First, get all the keys from the input buffer, in order to determine
632+ # whether we should consider this a paste event or not.
633+ for key_data in self ._get_keys (read , input_records ):
634+ self ._vt100_parser .feed (key_data )
635+
636+ # Return result.
637+ result = self ._buffer
638+ self ._buffer = []
639+ return result
640+
641+ def _get_keys (
642+ self , read : DWORD , input_records : Array [INPUT_RECORD ]
643+ ) -> Iterator [str ]:
644+ """
645+ Generator that yields `KeyPress` objects from the input records.
646+ """
647+ for i in range (read .value ):
648+ ir = input_records [i ]
649+
650+ # Get the right EventType from the EVENT_RECORD.
651+ # (For some reason the Windows console application 'cmder'
652+ # [http://gooseberrycreative.com/cmder/] can return '0' for
653+ # ir.EventType. -- Just ignore that.)
654+ if ir .EventType in EventTypes :
655+ ev = getattr (ir .Event , EventTypes [ir .EventType ])
656+
657+ # Process if this is a key event. (We also have mouse, menu and
658+ # focus events.)
659+ if isinstance (ev , KEY_EVENT_RECORD ) and ev .KeyDown :
660+ u_char = ev .uChar .UnicodeChar
661+ if u_char != "\x00 " :
662+ yield u_char
663+
664+
651665class _Win32Handles :
652666 """
653667 Utility to keep track of which handles are connectod to which callbacks.
0 commit comments