Reactos
at master 574 lines 19 kB view raw
1// 2// getch.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// Defines _getch(), _getche(), and _ungetch(), which get and unget 7// characters directly from the console. 8// 9#include <conio.h> 10#include <corecrt_internal_lowio.h> 11#include <limits.h> 12#include <stdio.h> 13#include <stdlib.h> 14 15 16 17namespace 18{ 19 struct CharPair 20 { 21 unsigned char LeadChar; 22 unsigned char SecondChar; 23 }; 24 25 struct EnhKeyVals 26 { 27 unsigned short ScanCode; 28 CharPair RegChars; 29 CharPair ShiftChars; 30 CharPair CtrlChars; 31 CharPair AltChars; 32 }; 33 34 struct NormKeyVals 35 { 36 CharPair RegChars; 37 CharPair ShiftChars; 38 CharPair CtrlChars; 39 CharPair AltChars; 40 }; 41} 42 43 44 45// Table of enhanced key values: 46static EnhKeyVals const EnhancedKeys[] = 47{ 48 { 28, { 13, 0 }, { 13, 0 }, { 10, 0 }, { 0, 166 } }, 49 { 53, { 47, 0 }, { 63, 0 }, { 0, 149 }, { 0, 164 } }, 50 { 71, { 224, 71 }, { 224, 71 }, { 224, 119 }, { 0, 151 } }, 51 { 72, { 224, 72 }, { 224, 72 }, { 224, 141 }, { 0, 152 } }, 52 { 73, { 224, 73 }, { 224, 73 }, { 224, 134 }, { 0, 153 } }, 53 { 75, { 224, 75 }, { 224, 75 }, { 224, 115 }, { 0, 155 } }, 54 { 77, { 224, 77 }, { 224, 77 }, { 224, 116 }, { 0, 157 } }, 55 { 79, { 224, 79 }, { 224, 79 }, { 224, 117 }, { 0, 159 } }, 56 { 80, { 224, 80 }, { 224, 80 }, { 224, 145 }, { 0, 160 } }, 57 { 81, { 224, 81 }, { 224, 81 }, { 224, 118 }, { 0, 161 } }, 58 { 82, { 224, 82 }, { 224, 82 }, { 224, 146 }, { 0, 162 } }, 59 { 83, { 224, 83 }, { 224, 83 }, { 224, 147 }, { 0, 163 } } 60}; 61 62// The number of elements in EnhancedKeys: 63#define NUM_EKA_ELTS (sizeof(EnhancedKeys) / sizeof(EnhKeyVals)) 64 65 66 67// Table of key values for normal keys. Note that the table is padded so that 68// the key scan code serves as an index into the table. 69static NormKeyVals const NormalKeys[] = 70{ 71 /* padding */ 72 { /* 0 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 73 74 { /* 1 */ { 27, 0 }, { 27, 0 }, { 27, 0 }, { 0, 1 } }, 75 { /* 2 */ { 49, 0 }, { 33, 0 }, { 0, 0 }, { 0, 120 } }, 76 { /* 3 */ { 50, 0 }, { 64, 0 }, { 0, 3 }, { 0, 121 } }, 77 { /* 4 */ { 51, 0 }, { 35, 0 }, { 0, 0 }, { 0, 122 } }, 78 { /* 5 */ { 52, 0 }, { 36, 0 }, { 0, 0 }, { 0, 123 } }, 79 { /* 6 */ { 53, 0 }, { 37, 0 }, { 0, 0 }, { 0, 124 } }, 80 { /* 7 */ { 54, 0 }, { 94, 0 }, { 30, 0 }, { 0, 125 } }, 81 { /* 8 */ { 55, 0 }, { 38, 0 }, { 0, 0 }, { 0, 126 } }, 82 { /* 9 */ { 56, 0 }, { 42, 0 }, { 0, 0 }, { 0, 127 } }, 83 { /* 10 */ { 57, 0 }, { 40, 0 }, { 0, 0 }, { 0, 128 } }, 84 { /* 11 */ { 48, 0 }, { 41, 0 }, { 0, 0 }, { 0, 129 } }, 85 { /* 12 */ { 45, 0 }, { 95, 0 }, { 31, 0 }, { 0, 130 } }, 86 { /* 13 */ { 61, 0 }, { 43, 0 }, { 0, 0 }, { 0, 131 } }, 87 { /* 14 */ { 8, 0 }, { 8, 0 }, { 127, 0 }, { 0, 14 } }, 88 { /* 15 */ { 9, 0 }, { 0, 15 }, { 0, 148 }, { 0, 15 } }, 89 { /* 16 */ { 113, 0 }, { 81, 0 }, { 17, 0 }, { 0, 16 } }, 90 { /* 17 */ { 119, 0 }, { 87, 0 }, { 23, 0 }, { 0, 17 } }, 91 { /* 18 */ { 101, 0 }, { 69, 0 }, { 5, 0 }, { 0, 18 } }, 92 { /* 19 */ { 114, 0 }, { 82, 0 }, { 18, 0 }, { 0, 19 } }, 93 { /* 20 */ { 116, 0 }, { 84, 0 }, { 20, 0 }, { 0, 20 } }, 94 { /* 21 */ { 121, 0 }, { 89, 0 }, { 25, 0 }, { 0, 21 } }, 95 { /* 22 */ { 117, 0 }, { 85, 0 }, { 21, 0 }, { 0, 22 } }, 96 { /* 23 */ { 105, 0 }, { 73, 0 }, { 9, 0 }, { 0, 23 } }, 97 { /* 24 */ { 111, 0 }, { 79, 0 }, { 15, 0 }, { 0, 24 } }, 98 { /* 25 */ { 112, 0 }, { 80, 0 }, { 16, 0 }, { 0, 25 } }, 99 { /* 26 */ { 91, 0 }, { 123, 0 }, { 27, 0 }, { 0, 26 } }, 100 { /* 27 */ { 93, 0 }, { 125, 0 }, { 29, 0 }, { 0, 27 } }, 101 { /* 28 */ { 13, 0 }, { 13, 0 }, { 10, 0 }, { 0, 28 } }, 102 103 /* padding */ 104 { /* 29 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 105 106 { /* 30 */ { 97, 0 }, { 65, 0 }, { 1, 0 }, { 0, 30 } }, 107 { /* 31 */ { 115, 0 }, { 83, 0 }, { 19, 0 }, { 0, 31 } }, 108 { /* 32 */ { 100, 0 }, { 68, 0 }, { 4, 0 }, { 0, 32 } }, 109 { /* 33 */ { 102, 0 }, { 70, 0 }, { 6, 0 }, { 0, 33 } }, 110 { /* 34 */ { 103, 0 }, { 71, 0 }, { 7, 0 }, { 0, 34 } }, 111 { /* 35 */ { 104, 0 }, { 72, 0 }, { 8, 0 }, { 0, 35 } }, 112 { /* 36 */ { 106, 0 }, { 74, 0 }, { 10, 0 }, { 0, 36 } }, 113 { /* 37 */ { 107, 0 }, { 75, 0 }, { 11, 0 }, { 0, 37 } }, 114 { /* 38 */ { 108, 0 }, { 76, 0 }, { 12, 0 }, { 0, 38 } }, 115 { /* 39 */ { 59, 0 }, { 58, 0 }, { 0, 0 }, { 0, 39 } }, 116 { /* 40 */ { 39, 0 }, { 34, 0 }, { 0, 0 }, { 0, 40 } }, 117 { /* 41 */ { 96, 0 }, { 126, 0 }, { 0, 0 }, { 0, 41 } }, 118 119 /* padding */ 120 { /* 42 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 121 122 { /* 43 */ { 92, 0 }, { 124, 0 }, { 28, 0 }, { 0, 0 } }, 123 { /* 44 */ { 122, 0 }, { 90, 0 }, { 26, 0 }, { 0, 44 } }, 124 { /* 45 */ { 120, 0 }, { 88, 0 }, { 24, 0 }, { 0, 45 } }, 125 { /* 46 */ { 99, 0 }, { 67, 0 }, { 3, 0 }, { 0, 46 } }, 126 { /* 47 */ { 118, 0 }, { 86, 0 }, { 22, 0 }, { 0, 47 } }, 127 { /* 48 */ { 98, 0 }, { 66, 0 }, { 2, 0 }, { 0, 48 } }, 128 { /* 49 */ { 110, 0 }, { 78, 0 }, { 14, 0 }, { 0, 49 } }, 129 { /* 50 */ { 109, 0 }, { 77, 0 }, { 13, 0 }, { 0, 50 } }, 130 { /* 51 */ { 44, 0 }, { 60, 0 }, { 0, 0 }, { 0, 51 } }, 131 { /* 52 */ { 46, 0 }, { 62, 0 }, { 0, 0 }, { 0, 52 } }, 132 { /* 53 */ { 47, 0 }, { 63, 0 }, { 0, 0 }, { 0, 53 } }, 133 134 /* padding */ 135 { /* 54 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 136 137 { /* 55 */ { 42, 0 }, { 0, 0 }, { 114, 0 }, { 0, 0 } }, 138 139 /* padding */ 140 { /* 56 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 141 142 { /* 57 */ { 32, 0 }, { 32, 0 }, { 32, 0 }, { 32, 0 } }, 143 144 /* padding */ 145 { /* 58 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 146 147 { /* 59 */ { 0, 59 }, { 0, 84 }, { 0, 94 }, { 0, 104 } }, 148 { /* 60 */ { 0, 60 }, { 0, 85 }, { 0, 95 }, { 0, 105 } }, 149 { /* 61 */ { 0, 61 }, { 0, 86 }, { 0, 96 }, { 0, 106 } }, 150 { /* 62 */ { 0, 62 }, { 0, 87 }, { 0, 97 }, { 0, 107 } }, 151 { /* 63 */ { 0, 63 }, { 0, 88 }, { 0, 98 }, { 0, 108 } }, 152 { /* 64 */ { 0, 64 }, { 0, 89 }, { 0, 99 }, { 0, 109 } }, 153 { /* 65 */ { 0, 65 }, { 0, 90 }, { 0, 100 }, { 0, 110 } }, 154 { /* 66 */ { 0, 66 }, { 0, 91 }, { 0, 101 }, { 0, 111 } }, 155 { /* 67 */ { 0, 67 }, { 0, 92 }, { 0, 102 }, { 0, 112 } }, 156 { /* 68 */ { 0, 68 }, { 0, 93 }, { 0, 103 }, { 0, 113 } }, 157 158 /* padding */ 159 { /* 69 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 160 { /* 70 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 161 162 { /* 71 */ { 0, 71 }, { 55, 0 }, { 0, 119 }, { 0, 0 } }, 163 { /* 72 */ { 0, 72 }, { 56, 0 }, { 0, 141 }, { 0, 0 } }, 164 { /* 73 */ { 0, 73 }, { 57, 0 }, { 0, 132 }, { 0, 0 } }, 165 { /* 74 */ { 0, 0 }, { 45, 0 }, { 0, 0 }, { 0, 0 } }, 166 { /* 75 */ { 0, 75 }, { 52, 0 }, { 0, 115 }, { 0, 0 } }, 167 { /* 76 */ { 0, 0 }, { 53, 0 }, { 0, 0 }, { 0, 0 } }, 168 { /* 77 */ { 0, 77 }, { 54, 0 }, { 0, 116 }, { 0, 0 } }, 169 { /* 78 */ { 0, 0 }, { 43, 0 }, { 0, 0 }, { 0, 0 } }, 170 { /* 79 */ { 0, 79 }, { 49, 0 }, { 0, 117 }, { 0, 0 } }, 171 { /* 80 */ { 0, 80 }, { 50, 0 }, { 0, 145 }, { 0, 0 } }, 172 { /* 81 */ { 0, 81 }, { 51, 0 }, { 0, 118 }, { 0, 0 } }, 173 { /* 82 */ { 0, 82 }, { 48, 0 }, { 0, 146 }, { 0, 0 } }, 174 { /* 83 */ { 0, 83 }, { 46, 0 }, { 0, 147 }, { 0, 0 } }, 175 176 /* padding */ 177 { /* 84 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 178 { /* 85 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 179 { /* 86 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 180 181 { /* 87 */ { 224, 133 }, { 224, 135 }, { 224, 137 }, { 224, 139 } }, 182 { /* 88 */ { 224, 134 }, { 224, 136 }, { 224, 138 }, { 224, 140 } } 183}; 184 185// The primary purpose of the pushback buffer is so that if a 186// multi-byte character or extended key code is read, we can return 187// the first byte and store the rest of the data in the buffer for 188// subsequent calls. UTF-8 characters can be up to 4 bytes long, so 189// the pushback buffer must be able to store the 3 remaining bytes. 190size_t const getch_pushback_buffer_capacity = 3; 191 192static int getch_pushback_buffer[getch_pushback_buffer_capacity]; 193static int getch_pushback_buffer_index = 0; 194static int getch_pushback_buffer_current_size = 0; 195 196static bool is_getch_pushback_buffer_full() 197{ 198 return getch_pushback_buffer_current_size >= getch_pushback_buffer_capacity; 199} 200 201static void add_to_getch_pushback_buffer(int const c) 202{ 203 _ASSERTE(!is_getch_pushback_buffer_full()); 204 getch_pushback_buffer[getch_pushback_buffer_current_size++] = c; 205} 206 207static int peek_next_getch_pushback_buffer() 208{ 209 if (getch_pushback_buffer_current_size == 0) { 210 return EOF; 211 } 212 213 return getch_pushback_buffer[getch_pushback_buffer_index]; 214} 215 216static int get_next_getch_pushback_buffer() 217{ 218 if (getch_pushback_buffer_current_size == 0) { 219 return EOF; 220 } 221 222 int const ret_val = getch_pushback_buffer[getch_pushback_buffer_index++]; 223 224 if (getch_pushback_buffer_index == getch_pushback_buffer_current_size) { 225 getch_pushback_buffer_index = 0; 226 getch_pushback_buffer_current_size = 0; 227 } 228 229 return ret_val; 230} 231 232extern "C" intptr_t __dcrt_lowio_console_input_handle; 233 234extern "C" CharPair const* __cdecl _getextendedkeycode(KEY_EVENT_RECORD*); 235extern "C" int __cdecl _kbhit_nolock(); 236 237 238 239// These functions read a single character from the console. _getch() does not 240// echo the character; _getche() does echo the character. If the push-back 241// buffer is nonempty, the buffered character is returned immediately, without 242// being echoed. 243// 244// On success, the read character is returned; on failure, EOF is returned. 245extern "C" int __cdecl _getch() 246{ 247 __acrt_lock(__acrt_conio_lock); 248 int result = 0; 249 __try 250 { 251 result = _getch_nolock(); 252 } 253 __finally 254 { 255 __acrt_unlock(__acrt_conio_lock); 256 } 257 __endtry 258 return result; 259} 260 261extern "C" int __cdecl _getche() 262{ 263 __acrt_lock(__acrt_conio_lock); 264 int result = 0; 265 __try 266 { 267 result = _getche_nolock(); 268 } 269 __finally 270 { 271 __acrt_unlock(__acrt_conio_lock); 272 } 273 __endtry 274 return result; 275} 276 277extern "C" int __cdecl _getch_nolock() 278{ 279 // Check the pushback buffer for a character. If one is present, return it: 280 int const pushback = get_next_getch_pushback_buffer(); 281 if (pushback != EOF) 282 { 283 return pushback; 284 } 285 286 if (__dcrt_lowio_ensure_console_input_initialized() == FALSE) { 287 return EOF; 288 } 289 290 // Switch console to raw mode: 291 DWORD old_console_mode; 292 __dcrt_get_input_console_mode(&old_console_mode); 293 __dcrt_set_input_console_mode(0); 294 295 int result = 0; 296 297 __try 298 { 299 for ( ; ; ) 300 { 301 // Get a console input event: 302 INPUT_RECORD input_record; 303 DWORD num_read; 304 305 if (__dcrt_read_console_input(&input_record, 1, &num_read) == FALSE || num_read == 0) 306 { 307 result = EOF; 308 __leave; 309 } 310 311 // Look for, and decipher, key events. 312 if (input_record.EventType == KEY_EVENT && input_record.Event.KeyEvent.bKeyDown) 313 { 314 // Simple case: if UnicodeChar is non-zero, we can convert it to char and return it. 315 wchar_t const c = input_record.Event.KeyEvent.uChar.UnicodeChar; 316 if (c != 0) 317 { 318 wchar_t const c_buffer[2] = {c, L'\0'}; 319 char mb_chars[4]; 320 321 size_t const amount_written = __acrt_wcs_to_mbs_cp_array( 322 c_buffer, 323 mb_chars, 324 GetConsoleCP() 325 ); 326 327 // Mask with 0xFF to just get lowest byte 328 if (amount_written >= 1) { 329 result = mb_chars[0] & 0xFF; 330 } 331 332 if (amount_written >= 2) { 333 for (size_t i = 1; i < amount_written; ++i) { 334 add_to_getch_pushback_buffer(mb_chars[i] & 0xFF); 335 } 336 } 337 __leave; 338 } 339 340 // Hard case: either it is an extended code or an event which 341 // should not be recognized. Let _getextendedkeycode do the work: 342 CharPair const* const cp = _getextendedkeycode(&input_record.Event.KeyEvent); 343 if (cp != nullptr) 344 { 345 // Mask with 0xFF to just get lowest byte 346 add_to_getch_pushback_buffer(cp->SecondChar & 0xFF); 347 result = cp->LeadChar & 0xFF; 348 __leave; 349 } 350 } 351 } 352 } 353 __finally 354 { 355 // Restore the previous console mode: 356 __dcrt_set_input_console_mode(old_console_mode); 357 } 358 __endtry 359 return result; 360} 361 362 363 364extern "C" int __cdecl _getche_nolock() 365{ 366 // Check the pushback buffer for a character. If one is present, return 367 // it without echoing: 368 int const pushback = get_next_getch_pushback_buffer(); 369 if (pushback != EOF) 370 { 371 return pushback; 372 } 373 374 // Otherwise, read the next character from the console and echo it: 375 int const c = _getch_nolock(); 376 if (c == EOF) 377 return EOF; 378 379 if (_putch_nolock(c) == EOF) 380 return EOF; 381 382 return c; 383} 384 385 386 387// Returns nonzero if a keystroke is waiting to be read; otherwise returns zero. 388extern "C" int __cdecl _kbhit() 389{ 390 __acrt_lock(__acrt_conio_lock); 391 int result = 0; 392 __try 393 { 394 result = _kbhit_nolock(); 395 } 396 __finally 397 { 398 __acrt_unlock(__acrt_conio_lock); 399 } 400 __endtry 401 return result; 402} 403 404 405 406extern "C" int __cdecl _kbhit_nolock() 407{ 408 // If a character has been pushed back, return TRUE: 409 if (peek_next_getch_pushback_buffer() != EOF) { 410 return TRUE; 411 } 412 413 if (__dcrt_lowio_ensure_console_input_initialized() == FALSE) { 414 return FALSE; 415 } 416 417 // Peek at all pending console events: 418 DWORD num_pending; 419 if (__dcrt_get_number_of_console_input_events(&num_pending) == FALSE) { 420 return FALSE; 421 } 422 423 if (num_pending == 0) { 424 return FALSE; 425 } 426 427 __crt_scoped_stack_ptr<INPUT_RECORD> const input_buffer(_malloca_crt_t(INPUT_RECORD, num_pending)); 428 if (input_buffer.get() == nullptr) { 429 return FALSE; 430 } 431 432 DWORD num_peeked; 433 // AsciiChar is not read, so using the narrow Win32 API is permitted. 434 if (__dcrt_peek_console_input_a(input_buffer.get(), num_pending, &num_peeked) == FALSE) { 435 return FALSE; 436 } 437 438 if (num_peeked == 0 || num_peeked > num_pending) { 439 return FALSE; 440 } 441 442 // Scan all of the peeked events to determine if any is a key event 443 // that should be recognized: 444 for (INPUT_RECORD* p = input_buffer.get(); num_peeked > 0; --num_peeked, ++p) 445 { 446 if (p->EventType != KEY_EVENT) 447 continue; 448 449 if (!p->Event.KeyEvent.bKeyDown) 450 continue; 451 452 if (p->Event.KeyEvent.uChar.AsciiChar == 0 && 453 _getextendedkeycode(&p->Event.KeyEvent) == nullptr) 454 continue; 455 456 return TRUE; 457 } 458 459 return FALSE; 460} 461 462 463 464// Pushes back ("ungets") one character to be read next by _getwch() or 465// _getwche(). On success, returns the character that was pushed back; on 466// failure, returns EOF. 467extern "C" int __cdecl _ungetch(int const c) 468{ 469 __acrt_lock(__acrt_conio_lock); 470 int result = 0; 471 __try 472 { 473 result = _ungetch_nolock(c); 474 } 475 __finally 476 { 477 __acrt_unlock(__acrt_conio_lock); 478 } 479 __endtry 480 return result; 481} 482 483 484 485extern "C" int __cdecl _ungetch_nolock(int const c) 486{ 487 // Fail if the character is EOF or the pusback buffer is nonempty: 488 if (c == EOF || is_getch_pushback_buffer_full()) { 489 return EOF; 490 } 491 492 add_to_getch_pushback_buffer(c); 493 return c; 494} 495 496 497 498// Returns the extended code (if there is one) for a key event. This is the 499// core function for the _getch() and _getche() functions and their wide 500// character equivalents, and is essential to _kbhit(). This is the function 501// that determines whether or not a key event NOT accompanied by an ASCII 502// character has an extended code and returns that code. 503// 504// On success, a pointer to a CharPair value holding the lead and second 505// characters of the extended code is returned. On failure, nullptr is returned. 506extern "C" CharPair const* __cdecl _getextendedkeycode(KEY_EVENT_RECORD* const pKE) 507{ 508 DWORD const CKS = pKE->dwControlKeyState; 509 510 if (CKS & ENHANCED_KEY) 511 { 512 // Find the appropriate entry in EnhancedKeys[]: 513 for (int i = 0 ; i < NUM_EKA_ELTS; ++i) 514 { 515 if (EnhancedKeys[i].ScanCode != pKE->wVirtualScanCode) 516 { 517 continue; 518 } 519 520 // We found a match! Determine which pair to return: 521 if (CKS & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) 522 { 523 return &EnhancedKeys[i].AltChars; 524 } 525 else if (CKS & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) 526 { 527 return &EnhancedKeys[i].CtrlChars; 528 } 529 else if (CKS & SHIFT_PRESSED) 530 { 531 return &EnhancedKeys[i].ShiftChars; 532 } 533 else 534 { 535 return &EnhancedKeys[i].RegChars; 536 } 537 } 538 539 return nullptr; 540 } 541 else 542 { 543 // Regular key or keyboard event which shouldn't be recognized. 544 // Determine which by getting the proper field of the proper entry in 545 // NormalKeys[] and examining the extended code. 546 CharPair const* pCP; 547 548 if (CKS & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) 549 { 550 pCP = &NormalKeys[pKE->wVirtualScanCode].AltChars; 551 } 552 else if (CKS & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) 553 { 554 pCP = &NormalKeys[pKE->wVirtualScanCode].CtrlChars; 555 } 556 else if (CKS & SHIFT_PRESSED) 557 { 558 pCP = &NormalKeys[pKE->wVirtualScanCode].ShiftChars; 559 } 560 else 561 { 562 pCP = &NormalKeys[pKE->wVirtualScanCode].RegChars; 563 } 564 565 // Make sure it wasn't a keyboard event which should not be recognized 566 // (e.g. the shift key was pressed): 567 if ((pCP->LeadChar != 0 && pCP->LeadChar != 224) || pCP->SecondChar == 0) 568 { 569 return nullptr; 570 } 571 572 return pCP; 573 } 574}