@@ -124,11 +124,84 @@ GetModifierKey(DWORD dwControlKeyState)
124
124
return modKey ;
125
125
}
126
126
127
+ // ReadConsoleForTermEmul() but for ENABLE_VIRTUAL_TERMINAL_INPUT.
128
+ static int
129
+ ReadConsoleForTermEmulModern (HANDLE hInput , char * destin , int destinlen )
130
+ {
131
+ // If the previous input ended on a lead (high) surrogate,
132
+ // we stash it here to combine it with the next input.
133
+ static wchar_t s_previous_lead ;
134
+
135
+ INPUT_RECORD records [TERM_IO_BUF_SIZE_UTF16 ];
136
+ DWORD records_cap = ARRAYSIZE (records );
137
+ DWORD records_len = 0 ;
138
+ wchar_t text [TERM_IO_BUF_SIZE_UTF16 ];
139
+ int text_len = 0 ;
140
+
141
+ // If we'll restore the previous lead surrogate, we can only read
142
+ // ARRAYSIZE(records)-1 records before the storage overflows.
143
+ if (s_previous_lead ) {
144
+ records_cap -- ;
145
+ }
146
+
147
+ // As this application heavily relies on APCs, it's important that we call
148
+ // DataAvailable(), because it calls WaitForSingleObjectEx with bAlertable=TRUE.
149
+ if (!DataAvailable (hInput ) ||
150
+ !ReadConsoleInputW (hInput , records , records_cap , & records_len ) ||
151
+ records_len == 0 )
152
+ return 0 ;
153
+
154
+ // Restore the previous lead surrogate if we have one.
155
+ if (s_previous_lead ) {
156
+ text [text_len ++ ] = s_previous_lead ;
157
+ s_previous_lead = 0 ;
158
+ }
159
+
160
+ // Accumulate the UTF-16 text.
161
+ for (DWORD i = 0 ; i < records_len ; i ++ ) {
162
+ switch (records [i ].EventType ) {
163
+ case WINDOW_BUFFER_SIZE_EVENT :
164
+ queue_terminal_window_change_event ();
165
+ break ;
166
+ case KEY_EVENT : {
167
+ const KEY_EVENT_RECORD * k = & records [i ].Event .KeyEvent ;
168
+ if (
169
+ // The old Windows console added support for Unicode by encoding the characters in the
170
+ // current code page as usual, while stuffing a UCS2 value into a trailing VK_MENU event.
171
+ // Modern terminals on Windows stopped doing this and the Windows console may as well at some point.
172
+ (k -> bKeyDown || k -> wVirtualKeyCode == VK_MENU ) &&
173
+ // Current versions of ConPTY suffer from a bug where pressing modifier keys enqueues
174
+ // a KEY_EVENT with UnicodeChar=0 despite ENABLE_VIRTUAL_TERMINAL_INPUT being enabled.
175
+ // They can be identified by the fact that their UnicodeChar value is zero,
176
+ // but they still have a non-zero wVirtualScanCode.
177
+ (k -> uChar .UnicodeChar != L'\0' || k -> wVirtualScanCode == 0 ))
178
+ text [text_len ++ ] = k -> uChar .UnicodeChar ;
179
+ break ;
180
+ }
181
+ default :
182
+ break ;
183
+ }
184
+ }
185
+
186
+ // Pop any lone lead surrogate from the input for later.
187
+ const wchar_t last_char = text [text_len - 1 ];
188
+ if (IS_HIGH_SURROGATE (last_char )) {
189
+ s_previous_lead = last_char ;
190
+ text_len -- ;
191
+ }
192
+
193
+ // ...and finally convert everything to UTF-8.
194
+ // It'll always fit, because we sized TERM_IO_BUF_SIZE to be large enough.
195
+ return WideCharToMultiByte (CP_UTF8 , 0 , text , text_len , destin , destinlen , NULL , NULL );
196
+ }
197
+
127
198
int
128
199
ReadConsoleForTermEmul (HANDLE hInput , char * destin , int destinlen )
129
200
{
130
- HANDLE hHandle [] = { hInput , NULL };
131
- DWORD nHandle = 1 ;
201
+ if (isConsoleVTSeqAvailable ) {
202
+ return ReadConsoleForTermEmulModern (hInput , destin , destinlen );
203
+ }
204
+
132
205
DWORD dwInput = 0 ;
133
206
DWORD rc = 0 ;
134
207
unsigned char octets [20 ];
@@ -187,23 +260,7 @@ ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen)
187
260
break ;
188
261
}
189
262
190
- if (isConsoleVTSeqAvailable ) {
191
- if (inputRecord .Event .KeyEvent .uChar .UnicodeChar != L'\0' || inputRecord .Event .KeyEvent .wVirtualScanCode == 0 ) {
192
- n = WideCharToMultiByte (
193
- CP_UTF8 ,
194
- 0 ,
195
- & (inputRecord .Event .KeyEvent .uChar .UnicodeChar ),
196
- 1 ,
197
- (LPSTR )octets ,
198
- 20 ,
199
- NULL ,
200
- NULL );
201
-
202
- WriteToBuffer ((char * )octets , n );
203
- }
204
- } else {
205
- GetVTSeqFromKeyStroke (inputRecord );
206
- }
263
+ GetVTSeqFromKeyStroke (inputRecord );
207
264
}
208
265
break ;
209
266
}
0 commit comments