1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS kernel 4 * PURPOSE: Keyboard functions 5 * FILE: win32ss/user/ntuser/keyboard.c 6 * PROGRAMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) 7 * Rafal Harabien (rafalh@reactos.org) 8 */ 9 10 #include <win32k.h> 11 DBG_DEFAULT_CHANNEL(UserKbd); 12 13 BYTE gafAsyncKeyState[256 * 2 / 8]; // 2 bits per key 14 static BYTE gafAsyncKeyStateRecentDown[256 / 8]; // 1 bit per key 15 static PKEYBOARD_INDICATOR_TRANSLATION gpKeyboardIndicatorTrans = NULL; 16 static KEYBOARD_INDICATOR_PARAMETERS gIndicators = {0, 0}; 17 KEYBOARD_ATTRIBUTES gKeyboardInfo; 18 int gLanguageToggleKeyState = 0; 19 DWORD gdwLanguageToggleKey = 0; 20 21 /* FUNCTIONS *****************************************************************/ 22 23 /* 24 * InitKeyboardImpl 25 * 26 * Initialization -- Right now, just zero the key state 27 */ 28 INIT_FUNCTION 29 NTSTATUS 30 NTAPI 31 InitKeyboardImpl(VOID) 32 { 33 RtlZeroMemory(&gafAsyncKeyState, sizeof(gafAsyncKeyState)); 34 RtlZeroMemory(&gafAsyncKeyStateRecentDown, sizeof(gafAsyncKeyStateRecentDown)); 35 // Clear and set default information. 36 RtlZeroMemory(&gKeyboardInfo, sizeof(gKeyboardInfo)); 37 gKeyboardInfo.KeyboardIdentifier.Type = 4; /* AT-101 */ 38 gKeyboardInfo.NumberOfFunctionKeys = 12; /* We're doing an 101 for now, so return 12 F-keys */ 39 return STATUS_SUCCESS; 40 } 41 42 /* 43 * IntKeyboardGetIndicatorTrans 44 * 45 * Asks the keyboard driver to send a small table that shows which 46 * lights should connect with which scancodes 47 */ 48 //static 49 NTSTATUS APIENTRY 50 IntKeyboardGetIndicatorTrans(HANDLE hKeyboardDevice, 51 PKEYBOARD_INDICATOR_TRANSLATION *ppIndicatorTrans) 52 { 53 NTSTATUS Status; 54 DWORD dwSize = 0; 55 IO_STATUS_BLOCK Block; 56 PKEYBOARD_INDICATOR_TRANSLATION pRet; 57 58 dwSize = sizeof(KEYBOARD_INDICATOR_TRANSLATION); 59 60 pRet = ExAllocatePoolWithTag(PagedPool, 61 dwSize, 62 USERTAG_KBDTABLE); 63 64 while (pRet) 65 { 66 Status = ZwDeviceIoControlFile(hKeyboardDevice, 67 NULL, 68 NULL, 69 NULL, 70 &Block, 71 IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION, 72 NULL, 0, 73 pRet, dwSize); 74 75 if (Status != STATUS_BUFFER_TOO_SMALL) 76 break; 77 78 ExFreePoolWithTag(pRet, USERTAG_KBDTABLE); 79 80 dwSize += sizeof(KEYBOARD_INDICATOR_TRANSLATION); 81 82 pRet = ExAllocatePoolWithTag(PagedPool, 83 dwSize, 84 USERTAG_KBDTABLE); 85 } 86 87 if (!pRet) 88 return STATUS_INSUFFICIENT_RESOURCES; 89 90 if (!NT_SUCCESS(Status)) 91 { 92 ExFreePoolWithTag(pRet, USERTAG_KBDTABLE); 93 return Status; 94 } 95 96 *ppIndicatorTrans = pRet; 97 return Status; 98 } 99 100 /* 101 * IntKeyboardUpdateLeds 102 * 103 * Sends the keyboard commands to turn on/off the lights 104 */ 105 static 106 NTSTATUS APIENTRY 107 IntKeyboardUpdateLeds(HANDLE hKeyboardDevice, 108 WORD wVk, 109 WORD wScanCode) 110 { 111 NTSTATUS Status; 112 UINT i; 113 USHORT LedFlag = 0; 114 IO_STATUS_BLOCK Block; 115 116 if (!gpKeyboardIndicatorTrans) 117 return STATUS_NOT_SUPPORTED; 118 119 switch (wVk) 120 { 121 case VK_CAPITAL: LedFlag = KEYBOARD_CAPS_LOCK_ON; break; 122 case VK_NUMLOCK: LedFlag = KEYBOARD_NUM_LOCK_ON; break; 123 case VK_SCROLL: LedFlag = KEYBOARD_SCROLL_LOCK_ON; break; 124 default: 125 for (i = 0; i < gpKeyboardIndicatorTrans->NumberOfIndicatorKeys; i++) 126 { 127 if (gpKeyboardIndicatorTrans->IndicatorList[i].MakeCode == wScanCode) 128 { 129 LedFlag = gpKeyboardIndicatorTrans->IndicatorList[i].IndicatorFlags; 130 break; 131 } 132 } 133 } 134 135 if (LedFlag) 136 { 137 gIndicators.LedFlags ^= LedFlag; 138 139 /* Update the lights on the hardware */ 140 Status = ZwDeviceIoControlFile(hKeyboardDevice, 141 NULL, 142 NULL, 143 NULL, 144 &Block, 145 IOCTL_KEYBOARD_SET_INDICATORS, 146 &gIndicators, sizeof(gIndicators), 147 NULL, 0); 148 149 return Status; 150 } 151 152 return STATUS_SUCCESS; 153 } 154 155 /* 156 * UserInitKeyboard 157 * 158 * Initializes keyboard indicators translation and their state 159 */ 160 VOID NTAPI 161 UserInitKeyboard(HANDLE hKeyboardDevice) 162 { 163 NTSTATUS Status; 164 IO_STATUS_BLOCK Block; 165 166 IntKeyboardGetIndicatorTrans(hKeyboardDevice, &gpKeyboardIndicatorTrans); 167 168 Status = ZwDeviceIoControlFile(hKeyboardDevice, 169 NULL, 170 NULL, 171 NULL, 172 &Block, 173 IOCTL_KEYBOARD_QUERY_INDICATORS, 174 NULL, 0, 175 &gIndicators, 176 sizeof(gIndicators)); 177 178 if (!NT_SUCCESS(Status)) 179 { 180 WARN("NtDeviceIoControlFile() failed, ignored\n"); 181 gIndicators.LedFlags = 0; 182 gIndicators.UnitId = 0; 183 } 184 185 SET_KEY_LOCKED(gafAsyncKeyState, VK_CAPITAL, 186 gIndicators.LedFlags & KEYBOARD_CAPS_LOCK_ON); 187 SET_KEY_LOCKED(gafAsyncKeyState, VK_NUMLOCK, 188 gIndicators.LedFlags & KEYBOARD_NUM_LOCK_ON); 189 SET_KEY_LOCKED(gafAsyncKeyState, VK_SCROLL, 190 gIndicators.LedFlags & KEYBOARD_SCROLL_LOCK_ON); 191 192 // FIXME: Need device driver to work! HID support more than one!!!! 193 Status = ZwDeviceIoControlFile(hKeyboardDevice, 194 NULL, 195 NULL, 196 NULL, 197 &Block, 198 IOCTL_KEYBOARD_QUERY_ATTRIBUTES, 199 NULL, 0, 200 &gKeyboardInfo, sizeof(gKeyboardInfo)); 201 202 if (!NT_SUCCESS(Status)) 203 { 204 ERR("NtDeviceIoControlFile() failed, ignored\n"); 205 } 206 TRACE("Keyboard type %u, subtype %u and number of func keys %u\n", 207 gKeyboardInfo.KeyboardIdentifier.Type, 208 gKeyboardInfo.KeyboardIdentifier.Subtype, 209 gKeyboardInfo.NumberOfFunctionKeys); 210 } 211 212 /* 213 * IntSimplifyVk 214 * 215 * Changes virtual keys which distinguish between left and right hand, to keys which don't distinguish 216 */ 217 static 218 WORD 219 IntSimplifyVk(WORD wVk) 220 { 221 switch (wVk) 222 { 223 case VK_LSHIFT: 224 case VK_RSHIFT: 225 return VK_SHIFT; 226 227 case VK_LCONTROL: 228 case VK_RCONTROL: 229 return VK_CONTROL; 230 231 case VK_LMENU: 232 case VK_RMENU: 233 return VK_MENU; 234 235 default: 236 return wVk; 237 } 238 } 239 240 /* 241 * IntFixVk 242 * 243 * Changes virtual keys which don't not distinguish between left and right hand to proper keys 244 */ 245 static 246 WORD 247 IntFixVk(WORD wVk, BOOL bExt) 248 { 249 switch (wVk) 250 { 251 case VK_SHIFT: 252 return bExt ? VK_RSHIFT : VK_LSHIFT; 253 254 case VK_CONTROL: 255 return bExt ? VK_RCONTROL : VK_LCONTROL; 256 257 case VK_MENU: 258 return bExt ? VK_RMENU : VK_LMENU; 259 260 default: 261 return wVk; 262 } 263 } 264 265 /* 266 * IntTranslateNumpadKey 267 * 268 * Translates numpad keys when numlock is enabled 269 */ 270 static 271 WORD 272 IntTranslateNumpadKey(WORD wVk) 273 { 274 switch (wVk) 275 { 276 case VK_INSERT: return VK_NUMPAD0; 277 case VK_END: return VK_NUMPAD1; 278 case VK_DOWN: return VK_NUMPAD2; 279 case VK_NEXT: return VK_NUMPAD3; 280 case VK_LEFT: return VK_NUMPAD4; 281 case VK_CLEAR: return VK_NUMPAD5; 282 case VK_RIGHT: return VK_NUMPAD6; 283 case VK_HOME: return VK_NUMPAD7; 284 case VK_UP: return VK_NUMPAD8; 285 case VK_PRIOR: return VK_NUMPAD9; 286 case VK_DELETE: return VK_DECIMAL; 287 default: return wVk; 288 } 289 } 290 291 /* 292 * IntGetModBits 293 * 294 * Gets layout specific modification bits, for example KBDSHIFT, KBDCTRL, KBDALT 295 */ 296 static 297 DWORD 298 IntGetModBits(PKBDTABLES pKbdTbl, PBYTE pKeyState) 299 { 300 DWORD i, dwModBits = 0; 301 302 /* DumpKeyState( KeyState ); */ 303 304 for (i = 0; pKbdTbl->pCharModifiers->pVkToBit[i].Vk; i++) 305 if (IS_KEY_DOWN(pKeyState, pKbdTbl->pCharModifiers->pVkToBit[i].Vk)) 306 dwModBits |= pKbdTbl->pCharModifiers->pVkToBit[i].ModBits; 307 308 TRACE("Current Mod Bits: %lx\n", dwModBits); 309 310 return dwModBits; 311 } 312 313 /* 314 * IntTranslateChar 315 * 316 * Translates virtual key to character 317 */ 318 static 319 BOOL 320 IntTranslateChar(WORD wVirtKey, 321 PBYTE pKeyState, 322 PBOOL pbDead, 323 PBOOL pbLigature, 324 PWCHAR pwcTranslatedChar, 325 PKBDTABLES pKbdTbl) 326 { 327 PVK_TO_WCHAR_TABLE pVkToVchTbl; 328 PVK_TO_WCHARS10 pVkToVch; 329 DWORD i, dwModBits, dwVkModBits, dwModNumber = 0; 330 WCHAR wch; 331 BOOL bAltGr; 332 WORD wCaplokAttr; 333 334 dwModBits = pKeyState ? IntGetModBits(pKbdTbl, pKeyState) : 0; 335 bAltGr = pKeyState && (pKbdTbl->fLocaleFlags & KLLF_ALTGR) && IS_KEY_DOWN(pKeyState, VK_RMENU); 336 wCaplokAttr = bAltGr ? CAPLOKALTGR : CAPLOK; 337 338 TRACE("TryToTranslate: %04x %x\n", wVirtKey, dwModBits); 339 340 /* If ALT without CTRL has ben used, remove ALT flag */ 341 if ((dwModBits & (KBDALT|KBDCTRL)) == KBDALT) 342 dwModBits &= ~KBDALT; 343 344 if (dwModBits > pKbdTbl->pCharModifiers->wMaxModBits) 345 { 346 TRACE("dwModBits %x > wMaxModBits %x\n", dwModBits, pKbdTbl->pCharModifiers->wMaxModBits); 347 return FALSE; 348 } 349 350 for (i = 0; pKbdTbl->pVkToWcharTable[i].pVkToWchars; i++) 351 { 352 pVkToVchTbl = &pKbdTbl->pVkToWcharTable[i]; 353 pVkToVch = (PVK_TO_WCHARS10)(pVkToVchTbl->pVkToWchars); 354 while (pVkToVch->VirtualKey) 355 { 356 if (wVirtKey == (pVkToVch->VirtualKey & 0xFF)) 357 { 358 dwVkModBits = dwModBits; 359 360 /* If CapsLock is enabled for this key and locked, add SHIFT bit */ 361 if ((pVkToVch->Attributes & wCaplokAttr) && 362 pKeyState && 363 IS_KEY_LOCKED(pKeyState, VK_CAPITAL)) 364 { 365 /* Note: we use special value here instead of getting VK_SHIFT mod bit - it's verified */ 366 dwVkModBits ^= KBDSHIFT; 367 } 368 369 if (dwVkModBits > pKbdTbl->pCharModifiers->wMaxModBits) 370 break; 371 372 /* Get modification number */ 373 dwModNumber = pKbdTbl->pCharModifiers->ModNumber[dwVkModBits]; 374 if (dwModNumber >= pVkToVchTbl->nModifications) 375 { 376 TRACE("dwModNumber %u >= nModifications %u\n", dwModNumber, pVkToVchTbl->nModifications); 377 break; 378 } 379 380 /* Read character */ 381 wch = pVkToVch->wch[dwModNumber]; 382 if (wch == WCH_NONE) 383 break; 384 385 *pbDead = (wch == WCH_DEAD); 386 *pbLigature = (wch == WCH_LGTR); 387 *pwcTranslatedChar = wch; 388 389 TRACE("%lu %04x: dwModNumber %08x Char %04x\n", 390 i, wVirtKey, dwModNumber, wch); 391 392 if (*pbDead) 393 { 394 /* After WCH_DEAD, real character is located */ 395 pVkToVch = (PVK_TO_WCHARS10)(((BYTE *)pVkToVch) + pVkToVchTbl->cbSize); 396 if (pVkToVch->VirtualKey != 0xFF) 397 { 398 WARN("Found dead key with no trailer in the table.\n"); 399 WARN("VK: %04x, ADDR: %p\n", wVirtKey, pVkToVch); 400 break; 401 } 402 *pwcTranslatedChar = pVkToVch->wch[dwModNumber]; 403 } 404 return TRUE; 405 } 406 pVkToVch = (PVK_TO_WCHARS10)(((BYTE *)pVkToVch) + pVkToVchTbl->cbSize); 407 } 408 } 409 410 /* If nothing has been found in layout, check if this is ASCII control character. 411 Note: we could add it to layout table, but windows does not have it there */ 412 if (wVirtKey >= 'A' && wVirtKey <= 'Z' && 413 pKeyState && IS_KEY_DOWN(pKeyState, VK_CONTROL) && 414 !IS_KEY_DOWN(pKeyState, VK_MENU)) 415 { 416 *pwcTranslatedChar = (wVirtKey - 'A') + 1; /* ASCII control character */ 417 *pbDead = FALSE; 418 *pbLigature = FALSE; 419 return TRUE; 420 } 421 422 return FALSE; 423 } 424 425 /* 426 * IntToUnicodeEx 427 * 428 * Translates virtual key to characters 429 */ 430 static 431 int APIENTRY 432 IntToUnicodeEx(UINT wVirtKey, 433 UINT wScanCode, 434 PBYTE pKeyState, 435 LPWSTR pwszBuff, 436 int cchBuff, 437 UINT wFlags, 438 PKBDTABLES pKbdTbl) 439 { 440 WCHAR wchTranslatedChar; 441 BOOL bDead, bLigature; 442 static WCHAR wchDead = 0; 443 int iRet = 0; 444 445 ASSERT(pKbdTbl); 446 447 if (!IntTranslateChar(wVirtKey, 448 pKeyState, 449 &bDead, 450 &bLigature, 451 &wchTranslatedChar, 452 pKbdTbl)) 453 { 454 return 0; 455 } 456 457 if (bLigature) 458 { 459 WARN("Not handling ligature (yet)\n" ); 460 return 0; 461 } 462 463 /* If we got dead char in previous call check dead keys in keyboard layout */ 464 if (wchDead) 465 { 466 UINT i; 467 WCHAR wchFirst, wchSecond; 468 TRACE("Previous dead char: %lc (%x)\n", wchDead, wchDead); 469 470 if (pKbdTbl->pDeadKey) 471 { 472 for (i = 0; pKbdTbl->pDeadKey[i].dwBoth; i++) 473 { 474 wchFirst = pKbdTbl->pDeadKey[i].dwBoth >> 16; 475 wchSecond = pKbdTbl->pDeadKey[i].dwBoth & 0xFFFF; 476 if (wchFirst == wchDead && wchSecond == wchTranslatedChar) 477 { 478 wchTranslatedChar = pKbdTbl->pDeadKey[i].wchComposed; 479 wchDead = 0; 480 bDead = FALSE; 481 break; 482 } 483 } 484 } 485 else 486 { 487 #if defined(__GNUC__) 488 if (wchDead == 0x8000) 489 { 490 ERR("GCC is inventing bits, ignoring fake dead key\n"); 491 wchDead = 0; 492 } 493 #endif 494 } 495 496 TRACE("Final char: %lc (%x)\n", wchTranslatedChar, wchTranslatedChar); 497 } 498 499 /* Dead char has not been not found */ 500 if (wchDead) 501 { 502 /* Treat both characters normally */ 503 if (cchBuff > iRet) 504 pwszBuff[iRet++] = wchDead; 505 bDead = FALSE; 506 } 507 508 /* Add character to the buffer */ 509 if (cchBuff > iRet) 510 pwszBuff[iRet++] = wchTranslatedChar; 511 512 /* Save dead character */ 513 wchDead = bDead ? wchTranslatedChar : 0; 514 515 return bDead ? -iRet : iRet; 516 } 517 518 /* 519 * IntVkToVsc 520 * 521 * Translates virtual key to scan code 522 */ 523 static 524 WORD FASTCALL 525 IntVkToVsc(WORD wVk, PKBDTABLES pKbdTbl) 526 { 527 unsigned i; 528 529 ASSERT(pKbdTbl); 530 531 /* Check standard keys first */ 532 for (i = 0; i < pKbdTbl->bMaxVSCtoVK; i++) 533 { 534 if ((pKbdTbl->pusVSCtoVK[i] & 0xFF) == wVk) 535 return i; 536 } 537 538 /* Check extended keys now */ 539 for (i = 0; pKbdTbl->pVSCtoVK_E0[i].Vsc; i++) 540 { 541 if ((pKbdTbl->pVSCtoVK_E0[i].Vk & 0xFF) == wVk) 542 return 0xE000 | pKbdTbl->pVSCtoVK_E0[i].Vsc; 543 } 544 545 for (i = 0; pKbdTbl->pVSCtoVK_E1[i].Vsc; i++) 546 { 547 if ((pKbdTbl->pVSCtoVK_E1[i].Vk & 0xFF) == wVk) 548 return 0xE100 | pKbdTbl->pVSCtoVK_E1[i].Vsc; 549 } 550 551 /* Virtual key has not been found */ 552 return 0; 553 } 554 555 /* 556 * IntVscToVk 557 * 558 * Translates prefixed scancode to virtual key 559 */ 560 static 561 WORD FASTCALL 562 IntVscToVk(WORD wScanCode, PKBDTABLES pKbdTbl) 563 { 564 unsigned i; 565 WORD wVk = 0; 566 567 ASSERT(pKbdTbl); 568 569 if ((wScanCode & 0xFF00) == 0xE000) 570 { 571 for (i = 0; pKbdTbl->pVSCtoVK_E0[i].Vsc; i++) 572 { 573 if (pKbdTbl->pVSCtoVK_E0[i].Vsc == (wScanCode & 0xFF)) 574 { 575 wVk = pKbdTbl->pVSCtoVK_E0[i].Vk; 576 } 577 } 578 } 579 else if ((wScanCode & 0xFF00) == 0xE100) 580 { 581 for (i = 0; pKbdTbl->pVSCtoVK_E1[i].Vsc; i++) 582 { 583 if (pKbdTbl->pVSCtoVK_E1[i].Vsc == (wScanCode & 0xFF)) 584 { 585 wVk = pKbdTbl->pVSCtoVK_E1[i].Vk; 586 } 587 } 588 } 589 else if (wScanCode < pKbdTbl->bMaxVSCtoVK) 590 { 591 wVk = pKbdTbl->pusVSCtoVK[wScanCode]; 592 } 593 594 /* 0xFF nad 0x00 are invalid VKs */ 595 return wVk != 0xFF ? wVk : 0; 596 } 597 598 /* 599 * IntVkToChar 600 * 601 * Translates virtual key to character, ignoring shift state 602 */ 603 static 604 WCHAR FASTCALL 605 IntVkToChar(WORD wVk, PKBDTABLES pKbdTbl) 606 { 607 WCHAR wch; 608 BOOL bDead, bLigature; 609 610 ASSERT(pKbdTbl); 611 612 if (IntTranslateChar(wVk, 613 NULL, 614 &bDead, 615 &bLigature, 616 &wch, 617 pKbdTbl)) 618 { 619 return wch; 620 } 621 622 return 0; 623 } 624 625 /* 626 * NtUserGetAsyncKeyState 627 * 628 * Gets key state from global bitmap 629 */ 630 SHORT 631 APIENTRY 632 NtUserGetAsyncKeyState(INT Key) 633 { 634 WORD wRet = 0; 635 636 TRACE("Enter NtUserGetAsyncKeyState\n"); 637 638 if (Key >= 0x100) 639 { 640 EngSetLastError(ERROR_INVALID_PARAMETER); 641 ERR("Invalid parameter Key\n"); 642 return 0; 643 } 644 645 UserEnterExclusive(); 646 647 if (IS_KEY_DOWN(gafAsyncKeyState, Key)) 648 wRet |= 0x8000; // If down, windows returns 0x8000. 649 if (gafAsyncKeyStateRecentDown[Key / 8] & (1 << (Key % 8))) 650 wRet |= 0x1; 651 gafAsyncKeyStateRecentDown[Key / 8] &= ~(1 << (Key % 8)); 652 653 UserLeave(); 654 655 TRACE("Leave NtUserGetAsyncKeyState, ret=%u\n", wRet); 656 return wRet; 657 } 658 659 /* 660 * UpdateAsyncKeyState 661 * 662 * Updates gafAsyncKeyState array 663 */ 664 static 665 VOID NTAPI 666 UpdateAsyncKeyState(WORD wVk, BOOL bIsDown) 667 { 668 if (bIsDown) 669 { 670 /* If it's first key down event, xor lock bit */ 671 if (!IS_KEY_DOWN(gafAsyncKeyState, wVk)) 672 SET_KEY_LOCKED(gafAsyncKeyState, wVk, !IS_KEY_LOCKED(gafAsyncKeyState, wVk)); 673 674 SET_KEY_DOWN(gafAsyncKeyState, wVk, TRUE); 675 gafAsyncKeyStateRecentDown[wVk / 8] |= (1 << (wVk % 8)); 676 } 677 else 678 SET_KEY_DOWN(gafAsyncKeyState, wVk, FALSE); 679 } 680 681 /* 682 * co_CallLowLevelKeyboardHook 683 * 684 * Calls WH_KEYBOARD_LL hook 685 */ 686 static LRESULT 687 co_CallLowLevelKeyboardHook(WORD wVk, WORD wScanCode, DWORD dwFlags, BOOL bInjected, DWORD dwTime, DWORD dwExtraInfo) 688 { 689 KBDLLHOOKSTRUCT KbdHookData; 690 UINT uMsg; 691 692 KbdHookData.vkCode = wVk; 693 KbdHookData.scanCode = wScanCode; 694 KbdHookData.flags = 0; 695 if (dwFlags & KEYEVENTF_EXTENDEDKEY) 696 KbdHookData.flags |= LLKHF_EXTENDED; 697 if (IS_KEY_DOWN(gafAsyncKeyState, VK_MENU)) 698 KbdHookData.flags |= LLKHF_ALTDOWN; 699 if (dwFlags & KEYEVENTF_KEYUP) 700 KbdHookData.flags |= LLKHF_UP; 701 if (bInjected) 702 KbdHookData.flags |= LLKHF_INJECTED; 703 KbdHookData.time = dwTime; 704 KbdHookData.dwExtraInfo = dwExtraInfo; 705 706 /* Note: it doesnt support WM_SYSKEYUP */ 707 if (dwFlags & KEYEVENTF_KEYUP) 708 uMsg = WM_KEYUP; 709 else if (IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) && !IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL)) 710 uMsg = WM_SYSKEYDOWN; 711 else 712 uMsg = WM_KEYDOWN; 713 714 return co_HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, uMsg, (LPARAM)&KbdHookData); 715 } 716 717 /* 718 * SnapWindow 719 * 720 * Saves snapshot of specified window or whole screen in the clipboard 721 */ 722 static VOID 723 SnapWindow(HWND hWnd) 724 { 725 HBITMAP hbm = NULL, hbmOld; 726 HDC hdc = NULL, hdcMem; 727 SETCLIPBDATA scd; 728 INT cx, cy; 729 PWND pWnd = NULL; 730 731 TRACE("SnapWindow(%p)\n", hWnd); 732 733 /* If no windows is given, make snapshot of desktop window */ 734 if (!hWnd) 735 hWnd = IntGetDesktopWindow(); 736 737 pWnd = UserGetWindowObject(hWnd); 738 if (!pWnd) 739 { 740 ERR("Invalid window\n"); 741 goto cleanup; 742 } 743 744 hdc = UserGetDCEx(pWnd, NULL, DCX_USESTYLE | DCX_WINDOW); 745 if (!hdc) 746 { 747 ERR("UserGetDCEx failed!\n"); 748 goto cleanup; 749 } 750 751 cx = pWnd->rcWindow.right - pWnd->rcWindow.left; 752 cy = pWnd->rcWindow.bottom - pWnd->rcWindow.top; 753 754 hbm = NtGdiCreateCompatibleBitmap(hdc, cx, cy); 755 if (!hbm) 756 { 757 ERR("NtGdiCreateCompatibleBitmap failed!\n"); 758 goto cleanup; 759 } 760 761 hdcMem = NtGdiCreateCompatibleDC(hdc); 762 if (!hdcMem) 763 { 764 ERR("NtGdiCreateCompatibleDC failed!\n"); 765 goto cleanup; 766 } 767 768 hbmOld = NtGdiSelectBitmap(hdcMem, hbm); 769 NtGdiBitBlt(hdcMem, 0, 0, cx, cy, hdc, 0, 0, SRCCOPY, 0, 0); 770 NtGdiSelectBitmap(hdcMem, hbmOld); 771 IntGdiDeleteDC(hdcMem, FALSE); 772 773 /* Save snapshot in clipboard */ 774 if (UserOpenClipboard(NULL)) 775 { 776 UserEmptyClipboard(); 777 scd.fIncSerialNumber = TRUE; 778 scd.fGlobalHandle = FALSE; 779 if (UserSetClipboardData(CF_BITMAP, hbm, &scd)) 780 { 781 /* Bitmap is managed by system now */ 782 hbm = NULL; 783 } 784 UserCloseClipboard(); 785 } 786 787 cleanup: 788 if (hbm) 789 GreDeleteObject(hbm); 790 if (hdc) 791 UserReleaseDC(pWnd, hdc, FALSE); 792 } 793 794 /* 795 * UserSendKeyboardInput 796 * 797 * Process keyboard input from input devices and SendInput API 798 */ 799 BOOL NTAPI 800 ProcessKeyEvent(WORD wVk, WORD wScanCode, DWORD dwFlags, BOOL bInjected, DWORD dwTime, DWORD dwExtraInfo) 801 { 802 WORD wSimpleVk = 0, wFixedVk, wVk2; 803 PUSER_MESSAGE_QUEUE pFocusQueue; 804 PTHREADINFO pti; 805 BOOL bExt = (dwFlags & KEYEVENTF_EXTENDEDKEY) ? TRUE : FALSE; 806 BOOL bIsDown = (dwFlags & KEYEVENTF_KEYUP) ? FALSE : TRUE; 807 BOOL bPacket = (dwFlags & KEYEVENTF_UNICODE) ? TRUE : FALSE; 808 BOOL bWasSimpleDown = FALSE, bPostMsg = TRUE, bIsSimpleDown; 809 MSG Msg; 810 static BOOL bMenuDownRecently = FALSE; 811 812 /* Get virtual key without shifts (VK_(L|R)* -> VK_*) */ 813 wSimpleVk = IntSimplifyVk(wVk); 814 bWasSimpleDown = IS_KEY_DOWN(gafAsyncKeyState, wSimpleVk); 815 816 /* Update key without shifts */ 817 wVk2 = IntFixVk(wSimpleVk, !bExt); 818 bIsSimpleDown = bIsDown || IS_KEY_DOWN(gafAsyncKeyState, wVk2); 819 UpdateAsyncKeyState(wSimpleVk, bIsSimpleDown); 820 821 if (bIsDown) 822 { 823 /* Update keyboard LEDs */ 824 IntKeyboardUpdateLeds(ghKeyboardDevice, 825 wSimpleVk, 826 wScanCode); 827 } 828 829 /* Call WH_KEYBOARD_LL hook */ 830 if (co_CallLowLevelKeyboardHook(wVk, wScanCode, dwFlags, bInjected, dwTime, dwExtraInfo)) 831 { 832 ERR("Kbd msg dropped by WH_KEYBOARD_LL hook\n"); 833 bPostMsg = FALSE; 834 } 835 836 /* Check if this is a hotkey */ 837 if (co_UserProcessHotKeys(wSimpleVk, bIsDown)) //// Check if this is correct, refer to hotkey sequence message tests. 838 { 839 TRACE("HotKey Processed\n"); 840 bPostMsg = FALSE; 841 } 842 843 wFixedVk = IntFixVk(wSimpleVk, bExt); /* LSHIFT + EXT = RSHIFT */ 844 if (wSimpleVk == VK_SHIFT) /* shift can't be extended */ 845 bExt = FALSE; 846 847 /* If we have a focus queue, post a keyboard message */ 848 pFocusQueue = IntGetFocusMessageQueue(); 849 TRACE("ProcessKeyEvent Q 0x%p Active pWnd 0x%p Focus pWnd 0x%p\n", 850 pFocusQueue, 851 (pFocusQueue ? pFocusQueue->spwndActive : 0), 852 (pFocusQueue ? pFocusQueue->spwndFocus : 0)); 853 854 /* If it is F10 or ALT is down and CTRL is up, it's a system key */ 855 if ( wVk == VK_F10 || 856 (wSimpleVk == VK_MENU && bMenuDownRecently) || 857 (IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) && 858 !IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL)) || 859 // See MSDN WM_SYSKEYDOWN/UP fixes last wine Win test_keyboard_input. 860 (pFocusQueue && !pFocusQueue->spwndFocus) ) 861 { 862 bMenuDownRecently = FALSE; // reset 863 if (bIsDown) 864 { 865 Msg.message = WM_SYSKEYDOWN; 866 if (wSimpleVk == VK_MENU) 867 { 868 // Note: If only LALT is pressed WM_SYSKEYUP is generated instead of WM_KEYUP 869 bMenuDownRecently = TRUE; 870 } 871 } 872 else 873 Msg.message = WM_SYSKEYUP; 874 } 875 else 876 { 877 if (bIsDown) 878 Msg.message = WM_KEYDOWN; 879 else 880 Msg.message = WM_KEYUP; 881 } 882 883 /* Update async state of not simplified vk here. 884 See user32_apitest:GetKeyState */ 885 UpdateAsyncKeyState(wFixedVk, bIsDown); 886 887 /* Alt-Tab/Esc Check. Use FocusQueue or RIT Queue */ 888 if (bIsSimpleDown && !bWasSimpleDown && 889 IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) && 890 !IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL) && 891 (wVk == VK_ESCAPE || wVk == VK_TAB)) 892 { 893 TRACE("Alt-Tab/Esc Pressed wParam %x\n",wVk); 894 } 895 896 if (bIsDown && wVk == VK_SNAPSHOT) 897 { 898 if (pFocusQueue && 899 IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) && 900 !IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL)) 901 { 902 // Snap from Active Window, Focus can be null. 903 SnapWindow(pFocusQueue->spwndActive ? UserHMGetHandle(pFocusQueue->spwndActive) : 0); 904 } 905 else 906 SnapWindow(NULL); // Snap Desktop. 907 } 908 else if (pFocusQueue && bPostMsg) 909 { 910 PWND Wnd = pFocusQueue->spwndFocus; // SysInit..... 911 912 pti = pFocusQueue->ptiKeyboard; 913 914 if (!Wnd && pFocusQueue->spwndActive) // SysInit..... 915 { 916 // Going with Active. WM_SYSKEYXXX last wine Win test_keyboard_input. 917 Wnd = pFocusQueue->spwndActive; 918 } 919 if (Wnd) pti = Wnd->head.pti; 920 921 /* Init message */ 922 Msg.hwnd = Wnd ? UserHMGetHandle(Wnd) : NULL; 923 Msg.wParam = wFixedVk & 0xFF; /* Note: It's simplified by msg queue */ 924 Msg.lParam = MAKELPARAM(1, wScanCode); 925 Msg.time = dwTime; 926 Msg.pt = gpsi->ptCursor; 927 928 if ( Msg.message == WM_KEYDOWN || Msg.message == WM_SYSKEYDOWN ) 929 { 930 if ( (Msg.wParam == VK_SHIFT || 931 Msg.wParam == VK_CONTROL || 932 Msg.wParam == VK_MENU ) && 933 !IS_KEY_DOWN(gafAsyncKeyState, Msg.wParam)) 934 { 935 ERR("Set last input\n"); 936 //ptiLastInput = pti; 937 } 938 } 939 940 /* If it is VK_PACKET, high word of wParam is used for wchar */ 941 if (!bPacket) 942 { 943 if (bExt) 944 Msg.lParam |= KF_EXTENDED << 16; 945 if (IS_KEY_DOWN(gafAsyncKeyState, VK_MENU)) 946 Msg.lParam |= KF_ALTDOWN << 16; 947 if (bWasSimpleDown) 948 Msg.lParam |= KF_REPEAT << 16; 949 if (!bIsDown) 950 Msg.lParam |= KF_UP << 16; 951 /* FIXME: Set KF_DLGMODE and KF_MENUMODE when needed */ 952 if (pFocusQueue->QF_flags & QF_DIALOGACTIVE) 953 Msg.lParam |= KF_DLGMODE << 16; 954 if (pFocusQueue->MenuOwner) // pti->pMenuState->fMenuStarted 955 Msg.lParam |= KF_MENUMODE << 16; 956 } 957 958 // Post mouse move before posting key buttons, to keep it syned. 959 if (pFocusQueue->QF_flags & QF_MOUSEMOVED) 960 { 961 IntCoalesceMouseMove(pti); 962 } 963 964 /* Post a keyboard message */ 965 TRACE("Posting keyboard msg %u wParam 0x%x lParam 0x%x\n", Msg.message, Msg.wParam, Msg.lParam); 966 if (!Wnd) {ERR("Window is NULL\n");} 967 MsqPostMessage(pti, &Msg, TRUE, QS_KEY, 0, dwExtraInfo); 968 } 969 return TRUE; 970 } 971 972 BOOL NTAPI 973 UserSendKeyboardInput(KEYBDINPUT *pKbdInput, BOOL bInjected) 974 { 975 WORD wScanCode, wVk; 976 PKL pKl = NULL; 977 PKBDTABLES pKbdTbl; 978 PUSER_MESSAGE_QUEUE pFocusQueue; 979 LARGE_INTEGER LargeTickCount; 980 DWORD dwTime; 981 BOOL bExt = (pKbdInput->dwFlags & KEYEVENTF_EXTENDEDKEY) ? TRUE : FALSE; 982 983 gppiInputProvider = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->ppi; 984 985 /* Find the target thread whose locale is in effect */ 986 pFocusQueue = IntGetFocusMessageQueue(); 987 988 if (pFocusQueue && pFocusQueue->ptiKeyboard) 989 { 990 pKl = pFocusQueue->ptiKeyboard->KeyboardLayout; 991 } 992 993 if (!pKl) 994 pKl = W32kGetDefaultKeyLayout(); 995 if (!pKl) 996 { 997 ERR("No keyboard layout!\n"); 998 return FALSE; 999 } 1000 1001 pKbdTbl = pKl->spkf->pKbdTbl; 1002 1003 /* Note: wScan field is always used */ 1004 wScanCode = pKbdInput->wScan; 1005 1006 if (pKbdInput->dwFlags & KEYEVENTF_UNICODE) 1007 { 1008 /* Generate WM_KEYDOWN msg with wParam == VK_PACKET and 1009 high order word of lParam == pKbdInput->wScan */ 1010 wVk = VK_PACKET; 1011 } 1012 else 1013 { 1014 wScanCode &= 0x7F; 1015 if (pKbdInput->dwFlags & KEYEVENTF_SCANCODE) 1016 { 1017 /* Don't ignore invalid scan codes */ 1018 wVk = IntVscToVk(wScanCode | (bExt ? 0xE000 : 0), pKbdTbl); 1019 if (!wVk) /* use 0xFF if vsc is invalid */ 1020 wVk = 0xFF; 1021 } 1022 else 1023 { 1024 wVk = pKbdInput->wVk & 0xFF; 1025 } 1026 } 1027 1028 /* If time is given, use it */ 1029 if (pKbdInput->time) 1030 dwTime = pKbdInput->time; 1031 else 1032 { 1033 KeQueryTickCount(&LargeTickCount); 1034 dwTime = MsqCalculateMessageTime(&LargeTickCount); 1035 } 1036 1037 if (wVk == VK_RMENU && (pKbdTbl->fLocaleFlags & KLLF_ALTGR)) 1038 { 1039 /* For AltGr keyboards RALT generates CTRL events */ 1040 ProcessKeyEvent(VK_LCONTROL, 0, pKbdInput->dwFlags & KEYEVENTF_KEYUP, bInjected, dwTime, 0); 1041 } 1042 1043 /* Finally process this key */ 1044 return ProcessKeyEvent(wVk, wScanCode, pKbdInput->dwFlags, bInjected, dwTime, pKbdInput->dwExtraInfo); 1045 } 1046 1047 /* 1048 * UserProcessKeyboardInput 1049 * 1050 * Process raw keyboard input data 1051 */ 1052 VOID NTAPI 1053 UserProcessKeyboardInput( 1054 PKEYBOARD_INPUT_DATA pKbdInputData) 1055 { 1056 WORD wScanCode, wVk; 1057 PKL pKl = NULL; 1058 PKBDTABLES pKbdTbl; 1059 PUSER_MESSAGE_QUEUE pFocusQueue; 1060 1061 /* Calculate scan code with prefix */ 1062 wScanCode = pKbdInputData->MakeCode & 0x7F; 1063 if (pKbdInputData->Flags & KEY_E0) 1064 wScanCode |= 0xE000; 1065 if (pKbdInputData->Flags & KEY_E1) 1066 wScanCode |= 0xE100; 1067 1068 /* Find the target thread whose locale is in effect */ 1069 pFocusQueue = IntGetFocusMessageQueue(); 1070 1071 if (pFocusQueue && pFocusQueue->ptiKeyboard) 1072 { 1073 pKl = pFocusQueue->ptiKeyboard->KeyboardLayout; 1074 } 1075 1076 if (!pKl) 1077 pKl = W32kGetDefaultKeyLayout(); 1078 if (!pKl) 1079 return; 1080 1081 pKbdTbl = pKl->spkf->pKbdTbl; 1082 1083 /* Convert scan code to virtual key. 1084 Note: We could call UserSendKeyboardInput using scan code, 1085 but it wouldn't interpret E1 key(s) properly */ 1086 wVk = IntVscToVk(wScanCode, pKbdTbl); 1087 TRACE("UserProcessKeyboardInput: %x (break: %u) -> %x\n", 1088 wScanCode, (pKbdInputData->Flags & KEY_BREAK) ? 1u : 0, wVk); 1089 1090 if (wVk) 1091 { 1092 KEYBDINPUT KbdInput; 1093 1094 /* Support numlock */ 1095 if ((wVk & KBDNUMPAD) && IS_KEY_LOCKED(gafAsyncKeyState, VK_NUMLOCK)) 1096 { 1097 wVk = IntTranslateNumpadKey(wVk & 0xFF); 1098 } 1099 1100 /* Send keyboard input */ 1101 KbdInput.wVk = wVk & 0xFF; 1102 KbdInput.wScan = wScanCode & 0x7F; 1103 KbdInput.dwFlags = 0; 1104 if (pKbdInputData->Flags & KEY_BREAK) 1105 KbdInput.dwFlags |= KEYEVENTF_KEYUP; 1106 1107 if (wVk & KBDEXT) 1108 KbdInput.dwFlags |= KEYEVENTF_EXTENDEDKEY; 1109 // 1110 // Based on wine input:test_Input_blackbox this is okay. It seems the 1111 // bit did not get set and more research is needed. Now the right 1112 // shift works. 1113 // 1114 if (wVk == VK_RSHIFT) 1115 KbdInput.dwFlags |= KEYEVENTF_EXTENDEDKEY; 1116 1117 KbdInput.time = 0; 1118 KbdInput.dwExtraInfo = pKbdInputData->ExtraInformation; 1119 UserSendKeyboardInput(&KbdInput, FALSE); 1120 1121 /* E1 keys don't have break code */ 1122 if (pKbdInputData->Flags & KEY_E1) 1123 { 1124 /* Send key up event */ 1125 KbdInput.dwFlags |= KEYEVENTF_KEYUP; 1126 UserSendKeyboardInput(&KbdInput, FALSE); 1127 } 1128 } 1129 } 1130 1131 /* 1132 * IntTranslateKbdMessage 1133 * 1134 * Addes WM_(SYS)CHAR messages to message queue if message 1135 * describes key which produce character. 1136 */ 1137 BOOL FASTCALL 1138 IntTranslateKbdMessage(LPMSG lpMsg, 1139 UINT flags) 1140 { 1141 PTHREADINFO pti; 1142 INT cch = 0, i; 1143 WCHAR wch[3] = { 0 }; 1144 MSG NewMsg = { 0 }; 1145 PKBDTABLES pKbdTbl; 1146 LARGE_INTEGER LargeTickCount; 1147 BOOL bResult = FALSE; 1148 1149 switch(lpMsg->message) 1150 { 1151 case WM_KEYDOWN: 1152 case WM_KEYUP: 1153 case WM_SYSKEYDOWN: 1154 case WM_SYSKEYUP: 1155 break; 1156 default: 1157 return FALSE; 1158 } 1159 1160 pti = PsGetCurrentThreadWin32Thread(); 1161 1162 if (!pti->KeyboardLayout) 1163 { 1164 pti->KeyboardLayout = W32kGetDefaultKeyLayout(); 1165 pti->pClientInfo->hKL = pti->KeyboardLayout ? pti->KeyboardLayout->hkl : NULL; 1166 pKbdTbl = pti->KeyboardLayout ? pti->KeyboardLayout->spkf->pKbdTbl : NULL; 1167 } 1168 else 1169 pKbdTbl = pti->KeyboardLayout->spkf->pKbdTbl; 1170 if (!pKbdTbl) 1171 return FALSE; 1172 1173 if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN) 1174 return FALSE; 1175 1176 /* Init pt, hwnd and time msg fields */ 1177 NewMsg.pt = gpsi->ptCursor; 1178 NewMsg.hwnd = lpMsg->hwnd; 1179 KeQueryTickCount(&LargeTickCount); 1180 NewMsg.time = MsqCalculateMessageTime(&LargeTickCount); 1181 1182 TRACE("Enter IntTranslateKbdMessage msg %s, vk %x\n", 1183 lpMsg->message == WM_SYSKEYDOWN ? "WM_SYSKEYDOWN" : "WM_KEYDOWN", lpMsg->wParam); 1184 1185 if (lpMsg->wParam == VK_PACKET) 1186 { 1187 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR; 1188 NewMsg.wParam = HIWORD(lpMsg->lParam); 1189 NewMsg.lParam = LOWORD(lpMsg->lParam); 1190 MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0, 0); 1191 return TRUE; 1192 } 1193 1194 cch = IntToUnicodeEx(lpMsg->wParam, 1195 HIWORD(lpMsg->lParam) & 0xFF, 1196 pti->MessageQueue->afKeyState, 1197 wch, 1198 sizeof(wch) / sizeof(wch[0]), 1199 0, 1200 pKbdTbl); 1201 1202 if (cch) 1203 { 1204 if (cch > 0) /* Normal characters */ 1205 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR; 1206 else /* Dead character */ 1207 { 1208 cch = -cch; 1209 NewMsg.message = 1210 (lpMsg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR; 1211 } 1212 NewMsg.lParam = lpMsg->lParam; 1213 1214 /* Send all characters */ 1215 for (i = 0; i < cch; ++i) 1216 { 1217 TRACE("Msg: %x '%lc' (%04x) %08x\n", NewMsg.message, wch[i], wch[i], NewMsg.lParam); 1218 NewMsg.wParam = wch[i]; 1219 MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0, 0); 1220 } 1221 bResult = TRUE; 1222 } 1223 1224 TRACE("Leave IntTranslateKbdMessage ret %d, cch %d, msg %x, wch %x\n", 1225 bResult, cch, NewMsg.message, NewMsg.wParam); 1226 return bResult; 1227 } 1228 1229 /* 1230 * Map a virtual key code, or virtual scan code, to a scan code, key code, 1231 * or unshifted unicode character. 1232 * 1233 * Code: See Below 1234 * Type: 1235 * 0 -- Code is a virtual key code that is converted into a virtual scan code 1236 * that does not distinguish between left and right shift keys. 1237 * 1 -- Code is a virtual scan code that is converted into a virtual key code 1238 * that does not distinguish between left and right shift keys. 1239 * 2 -- Code is a virtual key code that is converted into an unshifted unicode 1240 * character. 1241 * 3 -- Code is a virtual scan code that is converted into a virtual key code 1242 * that distinguishes left and right shift keys. 1243 * KeyLayout: Keyboard layout handle 1244 * 1245 * @implemented 1246 */ 1247 static UINT 1248 IntMapVirtualKeyEx(UINT uCode, UINT Type, PKBDTABLES pKbdTbl) 1249 { 1250 UINT uRet = 0; 1251 1252 switch (Type) 1253 { 1254 case MAPVK_VK_TO_VSC: 1255 uCode = IntFixVk(uCode, FALSE); 1256 uRet = IntVkToVsc(uCode, pKbdTbl); 1257 if (uRet > 0xFF) // Fail for scancodes with prefix (e0, e1) 1258 uRet = 0; 1259 break; 1260 1261 case MAPVK_VSC_TO_VK: 1262 uRet = IntVscToVk(uCode, pKbdTbl) & 0xFF; 1263 uRet = IntSimplifyVk(uRet); 1264 break; 1265 1266 case MAPVK_VK_TO_CHAR: 1267 uRet = (UINT)IntVkToChar(uCode, pKbdTbl); 1268 break; 1269 1270 case MAPVK_VSC_TO_VK_EX: 1271 uRet = IntVscToVk(uCode, pKbdTbl) & 0xFF; 1272 break; 1273 1274 case MAPVK_VK_TO_VSC_EX: 1275 uRet = IntVkToVsc(uCode, pKbdTbl); 1276 break; 1277 1278 default: 1279 EngSetLastError(ERROR_INVALID_PARAMETER); 1280 ERR("Wrong type value: %u\n", Type); 1281 } 1282 1283 return uRet; 1284 } 1285 1286 /* 1287 * NtUserMapVirtualKeyEx 1288 * 1289 * Map a virtual key code, or virtual scan code, to a scan code, key code, 1290 * or unshifted unicode character. See IntMapVirtualKeyEx. 1291 */ 1292 UINT 1293 APIENTRY 1294 NtUserMapVirtualKeyEx(UINT uCode, UINT uType, DWORD keyboardId, HKL dwhkl) 1295 { 1296 PKBDTABLES pKbdTbl = NULL; 1297 UINT ret = 0; 1298 1299 TRACE("Enter NtUserMapVirtualKeyEx\n"); 1300 UserEnterShared(); 1301 1302 if (!dwhkl) 1303 { 1304 PTHREADINFO pti; 1305 1306 pti = PsGetCurrentThreadWin32Thread(); 1307 if (pti && pti->KeyboardLayout) 1308 pKbdTbl = pti->KeyboardLayout->spkf->pKbdTbl; 1309 } 1310 else 1311 { 1312 PKL pKl; 1313 1314 pKl = UserHklToKbl(dwhkl); 1315 if (pKl) 1316 pKbdTbl = pKl->spkf->pKbdTbl; 1317 } 1318 1319 if (pKbdTbl) 1320 ret = IntMapVirtualKeyEx(uCode, uType, pKbdTbl); 1321 1322 UserLeave(); 1323 TRACE("Leave NtUserMapVirtualKeyEx, ret=%u\n", ret); 1324 return ret; 1325 } 1326 1327 /* 1328 * NtUserToUnicodeEx 1329 * 1330 * Translates virtual key to characters 1331 */ 1332 int 1333 APIENTRY 1334 NtUserToUnicodeEx( 1335 UINT wVirtKey, 1336 UINT wScanCode, 1337 PBYTE pKeyStateUnsafe, 1338 LPWSTR pwszBuffUnsafe, 1339 INT cchBuff, 1340 UINT wFlags, 1341 HKL dwhkl) 1342 { 1343 PTHREADINFO pti; 1344 BYTE afKeyState[256 * 2 / 8] = {0}; 1345 PWCHAR pwszBuff = NULL; 1346 INT i, iRet = 0; 1347 PKL pKl = NULL; 1348 1349 TRACE("Enter NtUserSetKeyboardState\n"); 1350 1351 /* Return 0 if SC_KEY_UP bit is set */ 1352 if (wScanCode & SC_KEY_UP || wVirtKey >= 0x100) 1353 { 1354 ERR("Invalid parameter\n"); 1355 return 0; 1356 } 1357 1358 _SEH2_TRY 1359 { 1360 /* Probe and copy key state to smaller bitmap */ 1361 ProbeForRead(pKeyStateUnsafe, 256 * sizeof(BYTE), 1); 1362 for (i = 0; i < 256; ++i) 1363 { 1364 if (pKeyStateUnsafe[i] & KS_DOWN_BIT) 1365 SET_KEY_DOWN(afKeyState, i, TRUE); 1366 if (pKeyStateUnsafe[i] & KS_LOCK_BIT) 1367 SET_KEY_LOCKED(afKeyState, i, TRUE); 1368 } 1369 } 1370 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1371 { 1372 ERR("Cannot copy key state\n"); 1373 SetLastNtError(_SEH2_GetExceptionCode()); 1374 _SEH2_YIELD(return 0); 1375 } 1376 _SEH2_END; 1377 1378 pwszBuff = ExAllocatePoolWithTag(NonPagedPool, sizeof(WCHAR) * cchBuff, TAG_STRING); 1379 if (!pwszBuff) 1380 { 1381 ERR("ExAllocatePoolWithTag(%u) failed\n", sizeof(WCHAR) * cchBuff); 1382 return 0; 1383 } 1384 RtlZeroMemory(pwszBuff, sizeof(WCHAR) * cchBuff); 1385 1386 UserEnterExclusive(); // Note: We modify wchDead static variable 1387 1388 if (dwhkl) 1389 pKl = UserHklToKbl(dwhkl); 1390 1391 if (!pKl) 1392 { 1393 pti = PsGetCurrentThreadWin32Thread(); 1394 pKl = pti->KeyboardLayout; 1395 } 1396 1397 iRet = IntToUnicodeEx(wVirtKey, 1398 wScanCode, 1399 afKeyState, 1400 pwszBuff, 1401 cchBuff, 1402 wFlags, 1403 pKl ? pKl->spkf->pKbdTbl : NULL); 1404 1405 MmCopyToCaller(pwszBuffUnsafe, pwszBuff, cchBuff * sizeof(WCHAR)); 1406 ExFreePoolWithTag(pwszBuff, TAG_STRING); 1407 1408 UserLeave(); 1409 TRACE("Leave NtUserSetKeyboardState, ret=%i\n", iRet); 1410 return iRet; 1411 } 1412 1413 /* 1414 * NtUserGetKeyNameText 1415 * 1416 * Gets key name from keyboard layout 1417 */ 1418 DWORD 1419 APIENTRY 1420 NtUserGetKeyNameText(LONG lParam, LPWSTR lpString, int cchSize) 1421 { 1422 PTHREADINFO pti; 1423 DWORD i, dwRet = 0; 1424 SIZE_T cchKeyName; 1425 WORD wScanCode = (lParam >> 16) & 0xFF; 1426 BOOL bExtKey = (HIWORD(lParam) & KF_EXTENDED) ? TRUE : FALSE; 1427 PKBDTABLES pKbdTbl; 1428 VSC_LPWSTR *pKeyNames = NULL; 1429 CONST WCHAR *pKeyName = NULL; 1430 WCHAR KeyNameBuf[2]; 1431 1432 TRACE("Enter NtUserGetKeyNameText\n"); 1433 1434 UserEnterShared(); 1435 1436 /* Get current keyboard layout */ 1437 pti = PsGetCurrentThreadWin32Thread(); 1438 pKbdTbl = pti ? pti->KeyboardLayout->spkf->pKbdTbl : 0; 1439 1440 if (!pKbdTbl || cchSize < 1) 1441 { 1442 ERR("Invalid parameter\n"); 1443 goto cleanup; 1444 } 1445 1446 /* "Do not care" flag */ 1447 if(lParam & LP_DO_NOT_CARE_BIT) 1448 { 1449 /* Note: We could do vsc -> vk -> vsc conversion, instead of using 1450 hardcoded scan codes, but it's not what Windows does */ 1451 if (wScanCode == SCANCODE_RSHIFT && !bExtKey) 1452 wScanCode = SCANCODE_LSHIFT; 1453 else if (wScanCode == SCANCODE_CTRL || wScanCode == SCANCODE_ALT) 1454 bExtKey = FALSE; 1455 } 1456 1457 if (bExtKey) 1458 pKeyNames = pKbdTbl->pKeyNamesExt; 1459 else 1460 pKeyNames = pKbdTbl->pKeyNames; 1461 1462 for (i = 0; pKeyNames[i].pwsz; i++) 1463 { 1464 if (pKeyNames[i].vsc == wScanCode) 1465 { 1466 pKeyName = pKeyNames[i].pwsz; 1467 break; 1468 } 1469 } 1470 1471 if (!pKeyName) 1472 { 1473 WORD wVk = IntVscToVk(wScanCode, pKbdTbl); 1474 1475 if (wVk) 1476 { 1477 KeyNameBuf[0] = IntVkToChar(wVk, pKbdTbl); 1478 KeyNameBuf[1] = 0; 1479 if (KeyNameBuf[0]) 1480 pKeyName = KeyNameBuf; 1481 } 1482 } 1483 1484 if (pKeyName) 1485 { 1486 cchKeyName = wcslen(pKeyName); 1487 if (cchKeyName > (cchSize - 1UL)) 1488 cchKeyName = cchSize - 1UL; // Don't count '\0' 1489 1490 _SEH2_TRY 1491 { 1492 ProbeForWrite(lpString, (cchKeyName + 1) * sizeof(WCHAR), 1); 1493 RtlCopyMemory(lpString, pKeyName, cchKeyName * sizeof(WCHAR)); 1494 lpString[cchKeyName] = UNICODE_NULL; 1495 dwRet = cchKeyName; 1496 } 1497 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1498 { 1499 SetLastNtError(_SEH2_GetExceptionCode()); 1500 } 1501 _SEH2_END; 1502 } 1503 else 1504 { 1505 EngSetLastError(ERROR_INVALID_PARAMETER); 1506 } 1507 1508 cleanup: 1509 UserLeave(); 1510 TRACE("Leave NtUserGetKeyNameText, ret=%lu\n", dwRet); 1511 return dwRet; 1512 } 1513 1514 /* 1515 * UserGetKeyboardType 1516 * 1517 * Returns some keyboard specific information 1518 */ 1519 DWORD FASTCALL 1520 UserGetKeyboardType( 1521 DWORD dwTypeFlag) 1522 { 1523 switch (dwTypeFlag) 1524 { 1525 case 0: /* Keyboard type */ 1526 return (DWORD)gKeyboardInfo.KeyboardIdentifier.Type; 1527 case 1: /* Keyboard Subtype */ 1528 return (DWORD)gKeyboardInfo.KeyboardIdentifier.Subtype; 1529 case 2: /* Number of F-keys */ 1530 return (DWORD)gKeyboardInfo.NumberOfFunctionKeys; 1531 default: 1532 ERR("Unknown type!\n"); 1533 return 0; /* Note: we don't have to set last error here */ 1534 } 1535 } 1536 1537 /* 1538 * NtUserVkKeyScanEx 1539 * 1540 * Based on IntTranslateChar, instead of processing VirtualKey match, 1541 * look for wChar match. 1542 */ 1543 DWORD 1544 APIENTRY 1545 NtUserVkKeyScanEx( 1546 WCHAR wch, 1547 HKL dwhkl, 1548 BOOL bUsehKL) 1549 { 1550 PKBDTABLES pKbdTbl; 1551 PVK_TO_WCHAR_TABLE pVkToWchTbl; 1552 PVK_TO_WCHARS10 pVkToWch; 1553 PKL pKl = NULL; 1554 DWORD i, dwModBits = 0, dwModNumber = 0, Ret = (DWORD)-1; 1555 1556 TRACE("NtUserVkKeyScanEx() wch %u, KbdLayout 0x%p\n", wch, dwhkl); 1557 UserEnterShared(); 1558 1559 if (bUsehKL) 1560 { 1561 // Use given keyboard layout 1562 if (dwhkl) 1563 pKl = UserHklToKbl(dwhkl); 1564 } 1565 else 1566 { 1567 // Use thread keyboard layout 1568 pKl = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->KeyboardLayout; 1569 } 1570 1571 if (!pKl) 1572 goto Exit; 1573 1574 pKbdTbl = pKl->spkf->pKbdTbl; 1575 1576 // Interate through all VkToWchar tables while pVkToWchars is not NULL 1577 for (i = 0; pKbdTbl->pVkToWcharTable[i].pVkToWchars; i++) 1578 { 1579 pVkToWchTbl = &pKbdTbl->pVkToWcharTable[i]; 1580 pVkToWch = (PVK_TO_WCHARS10)(pVkToWchTbl->pVkToWchars); 1581 1582 // Interate through all virtual keys 1583 while (pVkToWch->VirtualKey) 1584 { 1585 for (dwModNumber = 0; dwModNumber < pVkToWchTbl->nModifications; dwModNumber++) 1586 { 1587 if (pVkToWch->wch[dwModNumber] == wch) 1588 { 1589 dwModBits = pKbdTbl->pCharModifiers->ModNumber[dwModNumber]; 1590 TRACE("i %lu wC %04x: dwModBits %08x dwModNumber %08x MaxModBits %08x\n", 1591 i, wch, dwModBits, dwModNumber, pKbdTbl->pCharModifiers->wMaxModBits); 1592 Ret = (dwModBits << 8) | (pVkToWch->VirtualKey & 0xFF); 1593 goto Exit; 1594 } 1595 } 1596 pVkToWch = (PVK_TO_WCHARS10)(((BYTE *)pVkToWch) + pVkToWchTbl->cbSize); 1597 } 1598 } 1599 Exit: 1600 UserLeave(); 1601 return Ret; 1602 } 1603 1604 /* EOF */ 1605