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 CODE_SEG("INIT") 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 DWORD dwTime; 980 BOOL bExt = (pKbdInput->dwFlags & KEYEVENTF_EXTENDEDKEY) ? TRUE : FALSE; 981 982 gppiInputProvider = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->ppi; 983 984 /* Find the target thread whose locale is in effect */ 985 pFocusQueue = IntGetFocusMessageQueue(); 986 987 if (pFocusQueue && pFocusQueue->ptiKeyboard) 988 { 989 pKl = pFocusQueue->ptiKeyboard->KeyboardLayout; 990 } 991 992 if (!pKl) 993 pKl = W32kGetDefaultKeyLayout(); 994 if (!pKl) 995 { 996 ERR("No keyboard layout!\n"); 997 return FALSE; 998 } 999 1000 pKbdTbl = pKl->spkf->pKbdTbl; 1001 1002 /* Note: wScan field is always used */ 1003 wScanCode = pKbdInput->wScan; 1004 1005 if (pKbdInput->dwFlags & KEYEVENTF_UNICODE) 1006 { 1007 /* Generate WM_KEYDOWN msg with wParam == VK_PACKET and 1008 high order word of lParam == pKbdInput->wScan */ 1009 wVk = VK_PACKET; 1010 } 1011 else 1012 { 1013 wScanCode &= 0x7F; 1014 if (pKbdInput->dwFlags & KEYEVENTF_SCANCODE) 1015 { 1016 /* Don't ignore invalid scan codes */ 1017 wVk = IntVscToVk(wScanCode | (bExt ? 0xE000 : 0), pKbdTbl); 1018 if (!wVk) /* use 0xFF if vsc is invalid */ 1019 wVk = 0xFF; 1020 } 1021 else 1022 { 1023 wVk = pKbdInput->wVk & 0xFF; 1024 } 1025 } 1026 1027 /* If time is given, use it */ 1028 if (pKbdInput->time) 1029 dwTime = pKbdInput->time; 1030 else 1031 { 1032 dwTime = EngGetTickCount32(); 1033 } 1034 1035 if (wVk == VK_RMENU && (pKbdTbl->fLocaleFlags & KLLF_ALTGR)) 1036 { 1037 /* For AltGr keyboards RALT generates CTRL events */ 1038 ProcessKeyEvent(VK_LCONTROL, 0, pKbdInput->dwFlags & KEYEVENTF_KEYUP, bInjected, dwTime, 0); 1039 } 1040 1041 /* Finally process this key */ 1042 return ProcessKeyEvent(wVk, wScanCode, pKbdInput->dwFlags, bInjected, dwTime, pKbdInput->dwExtraInfo); 1043 } 1044 1045 /* 1046 * UserProcessKeyboardInput 1047 * 1048 * Process raw keyboard input data 1049 */ 1050 VOID NTAPI 1051 UserProcessKeyboardInput( 1052 PKEYBOARD_INPUT_DATA pKbdInputData) 1053 { 1054 WORD wScanCode, wVk; 1055 PKL pKl = NULL; 1056 PKBDTABLES pKbdTbl; 1057 PUSER_MESSAGE_QUEUE pFocusQueue; 1058 1059 /* Calculate scan code with prefix */ 1060 wScanCode = pKbdInputData->MakeCode & 0x7F; 1061 if (pKbdInputData->Flags & KEY_E0) 1062 wScanCode |= 0xE000; 1063 if (pKbdInputData->Flags & KEY_E1) 1064 wScanCode |= 0xE100; 1065 1066 /* Find the target thread whose locale is in effect */ 1067 pFocusQueue = IntGetFocusMessageQueue(); 1068 1069 if (pFocusQueue && pFocusQueue->ptiKeyboard) 1070 { 1071 pKl = pFocusQueue->ptiKeyboard->KeyboardLayout; 1072 } 1073 1074 if (!pKl) 1075 pKl = W32kGetDefaultKeyLayout(); 1076 if (!pKl) 1077 return; 1078 1079 pKbdTbl = pKl->spkf->pKbdTbl; 1080 1081 /* Convert scan code to virtual key. 1082 Note: We could call UserSendKeyboardInput using scan code, 1083 but it wouldn't interpret E1 key(s) properly */ 1084 wVk = IntVscToVk(wScanCode, pKbdTbl); 1085 TRACE("UserProcessKeyboardInput: %x (break: %u) -> %x\n", 1086 wScanCode, (pKbdInputData->Flags & KEY_BREAK) ? 1u : 0, wVk); 1087 1088 if (wVk) 1089 { 1090 KEYBDINPUT KbdInput; 1091 1092 /* Support numlock */ 1093 if ((wVk & KBDNUMPAD) && IS_KEY_LOCKED(gafAsyncKeyState, VK_NUMLOCK)) 1094 { 1095 wVk = IntTranslateNumpadKey(wVk & 0xFF); 1096 } 1097 1098 /* Send keyboard input */ 1099 KbdInput.wVk = wVk & 0xFF; 1100 KbdInput.wScan = wScanCode & 0x7F; 1101 KbdInput.dwFlags = 0; 1102 if (pKbdInputData->Flags & KEY_BREAK) 1103 KbdInput.dwFlags |= KEYEVENTF_KEYUP; 1104 1105 if (wVk & KBDEXT) 1106 KbdInput.dwFlags |= KEYEVENTF_EXTENDEDKEY; 1107 // 1108 // Based on wine input:test_Input_blackbox this is okay. It seems the 1109 // bit did not get set and more research is needed. Now the right 1110 // shift works. 1111 // 1112 if (wVk == VK_RSHIFT) 1113 KbdInput.dwFlags |= KEYEVENTF_EXTENDEDKEY; 1114 1115 KbdInput.time = 0; 1116 KbdInput.dwExtraInfo = pKbdInputData->ExtraInformation; 1117 UserSendKeyboardInput(&KbdInput, FALSE); 1118 1119 /* E1 keys don't have break code */ 1120 if (pKbdInputData->Flags & KEY_E1) 1121 { 1122 /* Send key up event */ 1123 KbdInput.dwFlags |= KEYEVENTF_KEYUP; 1124 UserSendKeyboardInput(&KbdInput, FALSE); 1125 } 1126 } 1127 } 1128 1129 /* 1130 * IntTranslateKbdMessage 1131 * 1132 * Addes WM_(SYS)CHAR messages to message queue if message 1133 * describes key which produce character. 1134 */ 1135 BOOL FASTCALL 1136 IntTranslateKbdMessage(LPMSG lpMsg, 1137 UINT flags) 1138 { 1139 PTHREADINFO pti; 1140 INT cch = 0, i; 1141 WCHAR wch[3] = { 0 }; 1142 MSG NewMsg = { 0 }; 1143 PKBDTABLES pKbdTbl; 1144 BOOL bResult = FALSE; 1145 1146 switch(lpMsg->message) 1147 { 1148 case WM_KEYDOWN: 1149 case WM_KEYUP: 1150 case WM_SYSKEYDOWN: 1151 case WM_SYSKEYUP: 1152 break; 1153 default: 1154 return FALSE; 1155 } 1156 1157 pti = PsGetCurrentThreadWin32Thread(); 1158 1159 if (!pti->KeyboardLayout) 1160 { 1161 pti->KeyboardLayout = W32kGetDefaultKeyLayout(); 1162 pti->pClientInfo->hKL = pti->KeyboardLayout ? pti->KeyboardLayout->hkl : NULL; 1163 pKbdTbl = pti->KeyboardLayout ? pti->KeyboardLayout->spkf->pKbdTbl : NULL; 1164 } 1165 else 1166 pKbdTbl = pti->KeyboardLayout->spkf->pKbdTbl; 1167 if (!pKbdTbl) 1168 return FALSE; 1169 1170 if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN) 1171 return FALSE; 1172 1173 /* Init pt, hwnd and time msg fields */ 1174 NewMsg.pt = gpsi->ptCursor; 1175 NewMsg.hwnd = lpMsg->hwnd; 1176 NewMsg.time = EngGetTickCount32(); 1177 1178 TRACE("Enter IntTranslateKbdMessage msg %s, vk %x\n", 1179 lpMsg->message == WM_SYSKEYDOWN ? "WM_SYSKEYDOWN" : "WM_KEYDOWN", lpMsg->wParam); 1180 1181 if (lpMsg->wParam == VK_PACKET) 1182 { 1183 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR; 1184 NewMsg.wParam = HIWORD(lpMsg->lParam); 1185 NewMsg.lParam = LOWORD(lpMsg->lParam); 1186 MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0, 0); 1187 return TRUE; 1188 } 1189 1190 cch = IntToUnicodeEx(lpMsg->wParam, 1191 HIWORD(lpMsg->lParam) & 0xFF, 1192 pti->MessageQueue->afKeyState, 1193 wch, 1194 sizeof(wch) / sizeof(wch[0]), 1195 0, 1196 pKbdTbl); 1197 1198 if (cch) 1199 { 1200 if (cch > 0) /* Normal characters */ 1201 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR; 1202 else /* Dead character */ 1203 { 1204 cch = -cch; 1205 NewMsg.message = 1206 (lpMsg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR; 1207 } 1208 NewMsg.lParam = lpMsg->lParam; 1209 1210 /* Send all characters */ 1211 for (i = 0; i < cch; ++i) 1212 { 1213 TRACE("Msg: %x '%lc' (%04x) %08x\n", NewMsg.message, wch[i], wch[i], NewMsg.lParam); 1214 NewMsg.wParam = wch[i]; 1215 MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0, 0); 1216 } 1217 bResult = TRUE; 1218 } 1219 1220 TRACE("Leave IntTranslateKbdMessage ret %d, cch %d, msg %x, wch %x\n", 1221 bResult, cch, NewMsg.message, NewMsg.wParam); 1222 return bResult; 1223 } 1224 1225 /* 1226 * Map a virtual key code, or virtual scan code, to a scan code, key code, 1227 * or unshifted unicode character. 1228 * 1229 * Code: See Below 1230 * Type: 1231 * 0 -- Code is a virtual key code that is converted into a virtual scan code 1232 * that does not distinguish between left and right shift keys. 1233 * 1 -- Code is a virtual scan code that is converted into a virtual key code 1234 * that does not distinguish between left and right shift keys. 1235 * 2 -- Code is a virtual key code that is converted into an unshifted unicode 1236 * character. 1237 * 3 -- Code is a virtual scan code that is converted into a virtual key code 1238 * that distinguishes left and right shift keys. 1239 * KeyLayout: Keyboard layout handle 1240 * 1241 * @implemented 1242 */ 1243 static UINT 1244 IntMapVirtualKeyEx(UINT uCode, UINT Type, PKBDTABLES pKbdTbl) 1245 { 1246 UINT uRet = 0; 1247 1248 switch (Type) 1249 { 1250 case MAPVK_VK_TO_VSC: 1251 uCode = IntFixVk(uCode, FALSE); 1252 uRet = IntVkToVsc(uCode, pKbdTbl); 1253 if (uRet > 0xFF) // Fail for scancodes with prefix (e0, e1) 1254 uRet = 0; 1255 break; 1256 1257 case MAPVK_VSC_TO_VK: 1258 uRet = IntVscToVk(uCode, pKbdTbl) & 0xFF; 1259 uRet = IntSimplifyVk(uRet); 1260 break; 1261 1262 case MAPVK_VK_TO_CHAR: 1263 uRet = (UINT)IntVkToChar(uCode, pKbdTbl); 1264 break; 1265 1266 case MAPVK_VSC_TO_VK_EX: 1267 uRet = IntVscToVk(uCode, pKbdTbl) & 0xFF; 1268 break; 1269 1270 case MAPVK_VK_TO_VSC_EX: 1271 uRet = IntVkToVsc(uCode, pKbdTbl); 1272 break; 1273 1274 default: 1275 EngSetLastError(ERROR_INVALID_PARAMETER); 1276 ERR("Wrong type value: %u\n", Type); 1277 } 1278 1279 return uRet; 1280 } 1281 1282 /* 1283 * NtUserMapVirtualKeyEx 1284 * 1285 * Map a virtual key code, or virtual scan code, to a scan code, key code, 1286 * or unshifted unicode character. See IntMapVirtualKeyEx. 1287 */ 1288 UINT 1289 APIENTRY 1290 NtUserMapVirtualKeyEx(UINT uCode, UINT uType, DWORD keyboardId, HKL dwhkl) 1291 { 1292 PKBDTABLES pKbdTbl = NULL; 1293 UINT ret = 0; 1294 1295 TRACE("Enter NtUserMapVirtualKeyEx\n"); 1296 UserEnterShared(); 1297 1298 if (!dwhkl) 1299 { 1300 PTHREADINFO pti; 1301 1302 pti = PsGetCurrentThreadWin32Thread(); 1303 if (pti && pti->KeyboardLayout) 1304 pKbdTbl = pti->KeyboardLayout->spkf->pKbdTbl; 1305 } 1306 else 1307 { 1308 PKL pKl; 1309 1310 pKl = UserHklToKbl(dwhkl); 1311 if (pKl) 1312 pKbdTbl = pKl->spkf->pKbdTbl; 1313 } 1314 1315 if (pKbdTbl) 1316 ret = IntMapVirtualKeyEx(uCode, uType, pKbdTbl); 1317 1318 UserLeave(); 1319 TRACE("Leave NtUserMapVirtualKeyEx, ret=%u\n", ret); 1320 return ret; 1321 } 1322 1323 /* 1324 * NtUserToUnicodeEx 1325 * 1326 * Translates virtual key to characters 1327 */ 1328 int 1329 APIENTRY 1330 NtUserToUnicodeEx( 1331 UINT wVirtKey, 1332 UINT wScanCode, 1333 PBYTE pKeyStateUnsafe, 1334 LPWSTR pwszBuffUnsafe, 1335 INT cchBuff, 1336 UINT wFlags, 1337 HKL dwhkl) 1338 { 1339 PTHREADINFO pti; 1340 BYTE afKeyState[256 * 2 / 8] = {0}; 1341 PWCHAR pwszBuff = NULL; 1342 INT i, iRet = 0; 1343 PKL pKl = NULL; 1344 NTSTATUS Status = STATUS_SUCCESS; 1345 1346 TRACE("Enter NtUserSetKeyboardState\n"); 1347 1348 /* Return 0 if SC_KEY_UP bit is set */ 1349 if (wScanCode & SC_KEY_UP || wVirtKey >= 0x100) 1350 { 1351 ERR("Invalid parameter\n"); 1352 return 0; 1353 } 1354 1355 _SEH2_TRY 1356 { 1357 /* Probe and copy key state to smaller bitmap */ 1358 ProbeForRead(pKeyStateUnsafe, 256 * sizeof(BYTE), 1); 1359 for (i = 0; i < 256; ++i) 1360 { 1361 if (pKeyStateUnsafe[i] & KS_DOWN_BIT) 1362 SET_KEY_DOWN(afKeyState, i, TRUE); 1363 if (pKeyStateUnsafe[i] & KS_LOCK_BIT) 1364 SET_KEY_LOCKED(afKeyState, i, TRUE); 1365 } 1366 } 1367 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1368 { 1369 ERR("Cannot copy key state\n"); 1370 SetLastNtError(_SEH2_GetExceptionCode()); 1371 _SEH2_YIELD(return 0); 1372 } 1373 _SEH2_END; 1374 1375 pwszBuff = ExAllocatePoolWithTag(NonPagedPool, sizeof(WCHAR) * cchBuff, TAG_STRING); 1376 if (!pwszBuff) 1377 { 1378 ERR("ExAllocatePoolWithTag(%u) failed\n", sizeof(WCHAR) * cchBuff); 1379 return 0; 1380 } 1381 RtlZeroMemory(pwszBuff, sizeof(WCHAR) * cchBuff); 1382 1383 UserEnterExclusive(); // Note: We modify wchDead static variable 1384 1385 if (dwhkl) 1386 pKl = UserHklToKbl(dwhkl); 1387 1388 if (!pKl) 1389 { 1390 pti = PsGetCurrentThreadWin32Thread(); 1391 pKl = pti->KeyboardLayout; 1392 } 1393 1394 if (pKl) 1395 { 1396 iRet = IntToUnicodeEx(wVirtKey, 1397 wScanCode, 1398 afKeyState, 1399 pwszBuff, 1400 cchBuff, 1401 wFlags, 1402 pKl->spkf->pKbdTbl); 1403 1404 if (iRet) 1405 { 1406 Status = MmCopyToCaller(pwszBuffUnsafe, pwszBuff, cchBuff * sizeof(WCHAR)); 1407 } 1408 } 1409 else 1410 { 1411 ERR("No keyboard layout ?!\n"); 1412 Status = STATUS_INVALID_HANDLE; 1413 } 1414 1415 ExFreePoolWithTag(pwszBuff, TAG_STRING); 1416 1417 if (!NT_SUCCESS(Status)) 1418 { 1419 iRet = 0; 1420 SetLastNtError(Status); 1421 } 1422 1423 UserLeave(); 1424 TRACE("Leave NtUserSetKeyboardState, ret=%i\n", iRet); 1425 return iRet; 1426 } 1427 1428 /* 1429 * NtUserGetKeyNameText 1430 * 1431 * Gets key name from keyboard layout 1432 */ 1433 DWORD 1434 APIENTRY 1435 NtUserGetKeyNameText(LONG lParam, LPWSTR lpString, int cchSize) 1436 { 1437 PTHREADINFO pti; 1438 DWORD i, dwRet = 0; 1439 SIZE_T cchKeyName; 1440 WORD wScanCode = (lParam >> 16) & 0xFF; 1441 BOOL bExtKey = (HIWORD(lParam) & KF_EXTENDED) ? TRUE : FALSE; 1442 PKBDTABLES pKbdTbl; 1443 VSC_LPWSTR *pKeyNames = NULL; 1444 CONST WCHAR *pKeyName = NULL; 1445 WCHAR KeyNameBuf[2]; 1446 1447 TRACE("Enter NtUserGetKeyNameText\n"); 1448 1449 UserEnterShared(); 1450 1451 /* Get current keyboard layout */ 1452 pti = PsGetCurrentThreadWin32Thread(); 1453 pKbdTbl = pti ? pti->KeyboardLayout->spkf->pKbdTbl : 0; 1454 1455 if (!pKbdTbl || cchSize < 1) 1456 { 1457 ERR("Invalid parameter\n"); 1458 goto cleanup; 1459 } 1460 1461 /* "Do not care" flag */ 1462 if(lParam & LP_DO_NOT_CARE_BIT) 1463 { 1464 /* Note: We could do vsc -> vk -> vsc conversion, instead of using 1465 hardcoded scan codes, but it's not what Windows does */ 1466 if (wScanCode == SCANCODE_RSHIFT && !bExtKey) 1467 wScanCode = SCANCODE_LSHIFT; 1468 else if (wScanCode == SCANCODE_CTRL || wScanCode == SCANCODE_ALT) 1469 bExtKey = FALSE; 1470 } 1471 1472 if (bExtKey) 1473 pKeyNames = pKbdTbl->pKeyNamesExt; 1474 else 1475 pKeyNames = pKbdTbl->pKeyNames; 1476 1477 for (i = 0; pKeyNames[i].pwsz; i++) 1478 { 1479 if (pKeyNames[i].vsc == wScanCode) 1480 { 1481 pKeyName = pKeyNames[i].pwsz; 1482 break; 1483 } 1484 } 1485 1486 if (!pKeyName) 1487 { 1488 WORD wVk = IntVscToVk(wScanCode, pKbdTbl); 1489 1490 if (wVk) 1491 { 1492 KeyNameBuf[0] = IntVkToChar(wVk, pKbdTbl); 1493 KeyNameBuf[1] = 0; 1494 if (KeyNameBuf[0]) 1495 pKeyName = KeyNameBuf; 1496 } 1497 } 1498 1499 if (pKeyName) 1500 { 1501 cchKeyName = wcslen(pKeyName); 1502 if (cchKeyName > (cchSize - 1UL)) 1503 cchKeyName = cchSize - 1UL; // Don't count '\0' 1504 1505 _SEH2_TRY 1506 { 1507 ProbeForWrite(lpString, (cchKeyName + 1) * sizeof(WCHAR), 1); 1508 RtlCopyMemory(lpString, pKeyName, cchKeyName * sizeof(WCHAR)); 1509 lpString[cchKeyName] = UNICODE_NULL; 1510 dwRet = cchKeyName; 1511 } 1512 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1513 { 1514 SetLastNtError(_SEH2_GetExceptionCode()); 1515 } 1516 _SEH2_END; 1517 } 1518 else 1519 { 1520 EngSetLastError(ERROR_INVALID_PARAMETER); 1521 } 1522 1523 cleanup: 1524 UserLeave(); 1525 TRACE("Leave NtUserGetKeyNameText, ret=%lu\n", dwRet); 1526 return dwRet; 1527 } 1528 1529 /* 1530 * UserGetKeyboardType 1531 * 1532 * Returns some keyboard specific information 1533 */ 1534 DWORD FASTCALL 1535 UserGetKeyboardType( 1536 DWORD dwTypeFlag) 1537 { 1538 switch (dwTypeFlag) 1539 { 1540 case 0: /* Keyboard type */ 1541 return (DWORD)gKeyboardInfo.KeyboardIdentifier.Type; 1542 case 1: /* Keyboard Subtype */ 1543 return (DWORD)gKeyboardInfo.KeyboardIdentifier.Subtype; 1544 case 2: /* Number of F-keys */ 1545 return (DWORD)gKeyboardInfo.NumberOfFunctionKeys; 1546 default: 1547 ERR("Unknown type!\n"); 1548 return 0; /* Note: we don't have to set last error here */ 1549 } 1550 } 1551 1552 /* 1553 * NtUserVkKeyScanEx 1554 * 1555 * Based on IntTranslateChar, instead of processing VirtualKey match, 1556 * look for wChar match. 1557 */ 1558 DWORD 1559 APIENTRY 1560 NtUserVkKeyScanEx( 1561 WCHAR wch, 1562 HKL dwhkl, 1563 BOOL bUsehKL) 1564 { 1565 PKBDTABLES pKbdTbl; 1566 PVK_TO_WCHAR_TABLE pVkToWchTbl; 1567 PVK_TO_WCHARS10 pVkToWch; 1568 PKL pKl = NULL; 1569 DWORD i, dwModBits = 0, dwModNumber = 0, Ret = (DWORD)-1; 1570 1571 TRACE("NtUserVkKeyScanEx() wch %u, KbdLayout 0x%p\n", wch, dwhkl); 1572 UserEnterShared(); 1573 1574 if (bUsehKL) 1575 { 1576 // Use given keyboard layout 1577 if (dwhkl) 1578 pKl = UserHklToKbl(dwhkl); 1579 } 1580 else 1581 { 1582 // Use thread keyboard layout 1583 pKl = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->KeyboardLayout; 1584 } 1585 1586 if (!pKl) 1587 goto Exit; 1588 1589 pKbdTbl = pKl->spkf->pKbdTbl; 1590 1591 // Interate through all VkToWchar tables while pVkToWchars is not NULL 1592 for (i = 0; pKbdTbl->pVkToWcharTable[i].pVkToWchars; i++) 1593 { 1594 pVkToWchTbl = &pKbdTbl->pVkToWcharTable[i]; 1595 pVkToWch = (PVK_TO_WCHARS10)(pVkToWchTbl->pVkToWchars); 1596 1597 // Interate through all virtual keys 1598 while (pVkToWch->VirtualKey) 1599 { 1600 for (dwModNumber = 0; dwModNumber < pVkToWchTbl->nModifications; dwModNumber++) 1601 { 1602 if (pVkToWch->wch[dwModNumber] == wch) 1603 { 1604 dwModBits = pKbdTbl->pCharModifiers->ModNumber[dwModNumber]; 1605 TRACE("i %lu wC %04x: dwModBits %08x dwModNumber %08x MaxModBits %08x\n", 1606 i, wch, dwModBits, dwModNumber, pKbdTbl->pCharModifiers->wMaxModBits); 1607 Ret = (dwModBits << 8) | (pVkToWch->VirtualKey & 0xFF); 1608 goto Exit; 1609 } 1610 } 1611 pVkToWch = (PVK_TO_WCHARS10)(((BYTE *)pVkToWch) + pVkToWchTbl->cbSize); 1612 } 1613 } 1614 Exit: 1615 UserLeave(); 1616 return Ret; 1617 } 1618 1619 /* EOF */ 1620