@@ -81,7 +81,7 @@ def __init__(self, stdin: TextIO | None = None) -> None:
81
81
self ._use_virtual_terminal_input = _is_win_vt100_input_enabled ()
82
82
83
83
if self ._use_virtual_terminal_input :
84
- self .console_input_reader = Vt100InputReader ()
84
+ self .console_input_reader = Vt100ConsoleInputReader ()
85
85
else :
86
86
self .console_input_reader = ConsoleInputReader ()
87
87
@@ -135,88 +135,6 @@ def handle(self) -> HANDLE:
135
135
return self .console_input_reader .handle
136
136
137
137
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
-
220
138
class ConsoleInputReader :
221
139
"""
222
140
: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]:
648
566
return [KeyPress (Keys .WindowsMouseEvent , data )]
649
567
650
568
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
+
651
665
class _Win32Handles :
652
666
"""
653
667
Utility to keep track of which handles are connectod to which callbacks.
0 commit comments