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 || Key < 0) 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 815 if (PRIMARYLANGID(gusLanguageID) == LANG_JAPANESE) 816 { 817 /* Japanese special! */ 818 if (IS_KEY_DOWN(gafAsyncKeyState, VK_SHIFT)) 819 { 820 if (wSimpleVk == VK_OEM_ATTN) 821 wSimpleVk = VK_CAPITAL; 822 else if (wSimpleVk == VK_OEM_COPY) 823 wSimpleVk = VK_OEM_FINISH; 824 } 825 } 826 827 bWasSimpleDown = IS_KEY_DOWN(gafAsyncKeyState, wSimpleVk); 828 829 /* Update key without shifts */ 830 wVk2 = IntFixVk(wSimpleVk, !bExt); 831 bIsSimpleDown = bIsDown || IS_KEY_DOWN(gafAsyncKeyState, wVk2); 832 UpdateAsyncKeyState(wSimpleVk, bIsSimpleDown); 833 834 if (bIsDown) 835 { 836 /* Update keyboard LEDs */ 837 IntKeyboardUpdateLeds(ghKeyboardDevice, 838 wSimpleVk, 839 wScanCode); 840 } 841 842 /* Call WH_KEYBOARD_LL hook */ 843 if (co_CallLowLevelKeyboardHook(wVk, wScanCode, dwFlags, bInjected, dwTime, dwExtraInfo)) 844 { 845 ERR("Kbd msg dropped by WH_KEYBOARD_LL hook\n"); 846 bPostMsg = FALSE; 847 } 848 849 /* Check if this is a hotkey */ 850 if (co_UserProcessHotKeys(wSimpleVk, bIsDown)) //// Check if this is correct, refer to hotkey sequence message tests. 851 { 852 TRACE("HotKey Processed\n"); 853 bPostMsg = FALSE; 854 } 855 856 wFixedVk = IntFixVk(wSimpleVk, bExt); /* LSHIFT + EXT = RSHIFT */ 857 if (wSimpleVk == VK_SHIFT) /* shift can't be extended */ 858 bExt = FALSE; 859 860 /* If we have a focus queue, post a keyboard message */ 861 pFocusQueue = IntGetFocusMessageQueue(); 862 TRACE("ProcessKeyEvent Q 0x%p Active pWnd 0x%p Focus pWnd 0x%p\n", 863 pFocusQueue, 864 (pFocusQueue ? pFocusQueue->spwndActive : 0), 865 (pFocusQueue ? pFocusQueue->spwndFocus : 0)); 866 867 /* If it is F10 or ALT is down and CTRL is up, it's a system key */ 868 if ( wVk == VK_F10 || 869 (wSimpleVk == VK_MENU && bMenuDownRecently) || 870 (IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) && 871 !IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL)) || 872 // See MSDN WM_SYSKEYDOWN/UP fixes last wine Win test_keyboard_input. 873 (pFocusQueue && !pFocusQueue->spwndFocus) ) 874 { 875 bMenuDownRecently = FALSE; // reset 876 if (bIsDown) 877 { 878 Msg.message = WM_SYSKEYDOWN; 879 if (wSimpleVk == VK_MENU) 880 { 881 // Note: If only LALT is pressed WM_SYSKEYUP is generated instead of WM_KEYUP 882 bMenuDownRecently = TRUE; 883 } 884 } 885 else 886 Msg.message = WM_SYSKEYUP; 887 } 888 else 889 { 890 if (bIsDown) 891 Msg.message = WM_KEYDOWN; 892 else 893 Msg.message = WM_KEYUP; 894 } 895 896 /* Update async state of not simplified vk here. 897 See user32_apitest:GetKeyState */ 898 UpdateAsyncKeyState(wFixedVk, bIsDown); 899 900 /* Alt-Tab/Esc Check. Use FocusQueue or RIT Queue */ 901 if (bIsSimpleDown && !bWasSimpleDown && 902 IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) && 903 !IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL) && 904 (wVk == VK_ESCAPE || wVk == VK_TAB)) 905 { 906 TRACE("Alt-Tab/Esc Pressed wParam %x\n",wVk); 907 } 908 909 if (bIsDown && wVk == VK_SNAPSHOT) 910 { 911 if (pFocusQueue && 912 IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) && 913 !IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL)) 914 { 915 // Snap from Active Window, Focus can be null. 916 SnapWindow(pFocusQueue->spwndActive ? UserHMGetHandle(pFocusQueue->spwndActive) : 0); 917 } 918 else 919 SnapWindow(NULL); // Snap Desktop. 920 } 921 else if (pFocusQueue && bPostMsg) 922 { 923 PWND Wnd = pFocusQueue->spwndFocus; // SysInit..... 924 925 pti = pFocusQueue->ptiKeyboard; 926 927 if (!Wnd && pFocusQueue->spwndActive) // SysInit..... 928 { 929 // Going with Active. WM_SYSKEYXXX last wine Win test_keyboard_input. 930 Wnd = pFocusQueue->spwndActive; 931 } 932 if (Wnd) pti = Wnd->head.pti; 933 934 /* Init message */ 935 Msg.hwnd = Wnd ? UserHMGetHandle(Wnd) : NULL; 936 Msg.wParam = wFixedVk & 0xFF; /* Note: It's simplified by msg queue */ 937 Msg.lParam = MAKELPARAM(1, wScanCode); 938 Msg.time = dwTime; 939 Msg.pt = gpsi->ptCursor; 940 941 if ( Msg.message == WM_KEYDOWN || Msg.message == WM_SYSKEYDOWN ) 942 { 943 if ( (Msg.wParam == VK_SHIFT || 944 Msg.wParam == VK_CONTROL || 945 Msg.wParam == VK_MENU ) && 946 !IS_KEY_DOWN(gafAsyncKeyState, Msg.wParam)) 947 { 948 ERR("Set last input\n"); 949 //ptiLastInput = pti; 950 } 951 } 952 953 /* If it is VK_PACKET, high word of wParam is used for wchar */ 954 if (!bPacket) 955 { 956 if (bExt) 957 Msg.lParam |= KF_EXTENDED << 16; 958 if (IS_KEY_DOWN(gafAsyncKeyState, VK_MENU)) 959 Msg.lParam |= KF_ALTDOWN << 16; 960 if (bWasSimpleDown) 961 Msg.lParam |= KF_REPEAT << 16; 962 if (!bIsDown) 963 Msg.lParam |= KF_UP << 16; 964 /* FIXME: Set KF_DLGMODE and KF_MENUMODE when needed */ 965 if (pFocusQueue->QF_flags & QF_DIALOGACTIVE) 966 Msg.lParam |= KF_DLGMODE << 16; 967 if (pFocusQueue->MenuOwner) // pti->pMenuState->fMenuStarted 968 Msg.lParam |= KF_MENUMODE << 16; 969 } 970 971 // Post mouse move before posting key buttons, to keep it syned. 972 if (pFocusQueue->QF_flags & QF_MOUSEMOVED) 973 { 974 IntCoalesceMouseMove(pti); 975 } 976 977 /* Post a keyboard message */ 978 TRACE("Posting keyboard msg %u wParam 0x%x lParam 0x%x\n", Msg.message, Msg.wParam, Msg.lParam); 979 if (!Wnd) {ERR("Window is NULL\n");} 980 MsqPostMessage(pti, &Msg, TRUE, QS_KEY, 0, dwExtraInfo); 981 } 982 return TRUE; 983 } 984 985 BOOL NTAPI 986 UserSendKeyboardInput(KEYBDINPUT *pKbdInput, BOOL bInjected) 987 { 988 WORD wScanCode, wVk; 989 PKL pKl = NULL; 990 PKBDTABLES pKbdTbl; 991 PUSER_MESSAGE_QUEUE pFocusQueue; 992 DWORD dwTime; 993 BOOL bExt = (pKbdInput->dwFlags & KEYEVENTF_EXTENDEDKEY) ? TRUE : FALSE; 994 995 gppiInputProvider = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->ppi; 996 997 /* Find the target thread whose locale is in effect */ 998 pFocusQueue = IntGetFocusMessageQueue(); 999 1000 if (pFocusQueue && pFocusQueue->ptiKeyboard) 1001 { 1002 pKl = pFocusQueue->ptiKeyboard->KeyboardLayout; 1003 } 1004 1005 if (!pKl) 1006 pKl = W32kGetDefaultKeyLayout(); 1007 if (!pKl) 1008 { 1009 ERR("No keyboard layout!\n"); 1010 return FALSE; 1011 } 1012 1013 pKbdTbl = pKl->spkf->pKbdTbl; 1014 1015 /* Note: wScan field is always used */ 1016 wScanCode = pKbdInput->wScan; 1017 1018 if (pKbdInput->dwFlags & KEYEVENTF_UNICODE) 1019 { 1020 /* Generate WM_KEYDOWN msg with wParam == VK_PACKET and 1021 high order word of lParam == pKbdInput->wScan */ 1022 wVk = VK_PACKET; 1023 } 1024 else 1025 { 1026 wScanCode &= 0x7F; 1027 if (pKbdInput->dwFlags & KEYEVENTF_SCANCODE) 1028 { 1029 /* Don't ignore invalid scan codes */ 1030 wVk = IntVscToVk(wScanCode | (bExt ? 0xE000 : 0), pKbdTbl); 1031 if (!wVk) /* use 0xFF if vsc is invalid */ 1032 wVk = 0xFF; 1033 } 1034 else 1035 { 1036 wVk = pKbdInput->wVk; 1037 } 1038 1039 /* Remove all virtual key flags (KBDEXT, KBDMULTIVK, KBDSPECIAL, KBDNUMPAD) */ 1040 wVk &= 0xFF; 1041 } 1042 1043 /* If time is given, use it */ 1044 if (pKbdInput->time) 1045 dwTime = pKbdInput->time; 1046 else 1047 { 1048 dwTime = EngGetTickCount32(); 1049 } 1050 1051 if (wVk == VK_RMENU && (pKbdTbl->fLocaleFlags & KLLF_ALTGR)) 1052 { 1053 /* For AltGr keyboards RALT generates CTRL events */ 1054 ProcessKeyEvent(VK_LCONTROL, 0, pKbdInput->dwFlags & KEYEVENTF_KEYUP, bInjected, dwTime, 0); 1055 } 1056 1057 /* Finally process this key */ 1058 return ProcessKeyEvent(wVk, wScanCode, pKbdInput->dwFlags, bInjected, dwTime, pKbdInput->dwExtraInfo); 1059 } 1060 1061 /* 1062 * UserProcessKeyboardInput 1063 * 1064 * Process raw keyboard input data 1065 */ 1066 VOID NTAPI 1067 UserProcessKeyboardInput( 1068 PKEYBOARD_INPUT_DATA pKbdInputData) 1069 { 1070 WORD wScanCode, wVk; 1071 PKL pKl = NULL; 1072 PKBDTABLES pKbdTbl; 1073 PUSER_MESSAGE_QUEUE pFocusQueue; 1074 1075 /* Calculate scan code with prefix */ 1076 wScanCode = pKbdInputData->MakeCode & 0x7F; 1077 if (pKbdInputData->Flags & KEY_E0) 1078 wScanCode |= 0xE000; 1079 if (pKbdInputData->Flags & KEY_E1) 1080 wScanCode |= 0xE100; 1081 1082 /* Find the target thread whose locale is in effect */ 1083 pFocusQueue = IntGetFocusMessageQueue(); 1084 1085 if (pFocusQueue && pFocusQueue->ptiKeyboard) 1086 { 1087 pKl = pFocusQueue->ptiKeyboard->KeyboardLayout; 1088 } 1089 1090 if (!pKl) 1091 pKl = W32kGetDefaultKeyLayout(); 1092 if (!pKl) 1093 return; 1094 1095 pKbdTbl = pKl->spkf->pKbdTbl; 1096 1097 /* Convert scan code to virtual key. 1098 Note: We could call UserSendKeyboardInput using scan code, 1099 but it wouldn't interpret E1 key(s) properly */ 1100 wVk = IntVscToVk(wScanCode, pKbdTbl); 1101 TRACE("UserProcessKeyboardInput: %x (break: %u) -> %x\n", 1102 wScanCode, (pKbdInputData->Flags & KEY_BREAK) ? 1u : 0, wVk); 1103 1104 if (wVk) 1105 { 1106 KEYBDINPUT KbdInput; 1107 1108 /* Support numlock */ 1109 if ((wVk & KBDNUMPAD) && IS_KEY_LOCKED(gafAsyncKeyState, VK_NUMLOCK)) 1110 { 1111 wVk = IntTranslateNumpadKey(wVk & 0xFF); 1112 } 1113 1114 /* Send keyboard input */ 1115 KbdInput.wVk = wVk & 0xFF; 1116 KbdInput.wScan = wScanCode & 0x7F; 1117 KbdInput.dwFlags = 0; 1118 if (pKbdInputData->Flags & KEY_BREAK) 1119 KbdInput.dwFlags |= KEYEVENTF_KEYUP; 1120 1121 if (wVk & KBDEXT) 1122 KbdInput.dwFlags |= KEYEVENTF_EXTENDEDKEY; 1123 // 1124 // Based on wine input:test_Input_blackbox this is okay. It seems the 1125 // bit did not get set and more research is needed. Now the right 1126 // shift works. 1127 // 1128 if (wVk == VK_RSHIFT) 1129 KbdInput.dwFlags |= KEYEVENTF_EXTENDEDKEY; 1130 1131 KbdInput.time = 0; 1132 KbdInput.dwExtraInfo = pKbdInputData->ExtraInformation; 1133 UserSendKeyboardInput(&KbdInput, FALSE); 1134 1135 /* E1 keys don't have break code */ 1136 if (pKbdInputData->Flags & KEY_E1) 1137 { 1138 /* Send key up event */ 1139 KbdInput.dwFlags |= KEYEVENTF_KEYUP; 1140 UserSendKeyboardInput(&KbdInput, FALSE); 1141 } 1142 } 1143 } 1144 1145 /* 1146 * IntTranslateKbdMessage 1147 * 1148 * Addes WM_(SYS)CHAR messages to message queue if message 1149 * describes key which produce character. 1150 */ 1151 BOOL FASTCALL 1152 IntTranslateKbdMessage(LPMSG lpMsg, 1153 UINT flags) 1154 { 1155 PTHREADINFO pti; 1156 INT cch = 0, i; 1157 WCHAR wch[3] = { 0 }; 1158 MSG NewMsg = { 0 }; 1159 PKBDTABLES pKbdTbl; 1160 BOOL bResult = FALSE; 1161 1162 switch(lpMsg->message) 1163 { 1164 case WM_KEYDOWN: 1165 case WM_KEYUP: 1166 case WM_SYSKEYDOWN: 1167 case WM_SYSKEYUP: 1168 break; 1169 default: 1170 return FALSE; 1171 } 1172 1173 pti = PsGetCurrentThreadWin32Thread(); 1174 1175 if (!pti->KeyboardLayout) 1176 { 1177 PKL pDefKL = W32kGetDefaultKeyLayout(); 1178 UserAssignmentLock((PVOID*)&(pti->KeyboardLayout), pDefKL); 1179 if (pDefKL) 1180 { 1181 pti->pClientInfo->hKL = pDefKL->hkl; 1182 pKbdTbl = pDefKL->spkf->pKbdTbl; 1183 } 1184 else 1185 { 1186 pti->pClientInfo->hKL = NULL; 1187 pKbdTbl = NULL; 1188 } 1189 } 1190 else 1191 pKbdTbl = pti->KeyboardLayout->spkf->pKbdTbl; 1192 if (!pKbdTbl) 1193 return FALSE; 1194 1195 if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN) 1196 return FALSE; 1197 1198 /* Init pt, hwnd and time msg fields */ 1199 NewMsg.pt = gpsi->ptCursor; 1200 NewMsg.hwnd = lpMsg->hwnd; 1201 NewMsg.time = EngGetTickCount32(); 1202 1203 TRACE("Enter IntTranslateKbdMessage msg %s, vk %x\n", 1204 lpMsg->message == WM_SYSKEYDOWN ? "WM_SYSKEYDOWN" : "WM_KEYDOWN", lpMsg->wParam); 1205 1206 if (lpMsg->wParam == VK_PACKET) 1207 { 1208 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR; 1209 NewMsg.wParam = HIWORD(lpMsg->lParam); 1210 NewMsg.lParam = LOWORD(lpMsg->lParam); 1211 MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0, 0); 1212 return TRUE; 1213 } 1214 1215 cch = IntToUnicodeEx(lpMsg->wParam, 1216 HIWORD(lpMsg->lParam) & 0xFF, 1217 pti->MessageQueue->afKeyState, 1218 wch, 1219 sizeof(wch) / sizeof(wch[0]), 1220 0, 1221 pKbdTbl); 1222 1223 if (cch) 1224 { 1225 if (cch > 0) /* Normal characters */ 1226 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR; 1227 else /* Dead character */ 1228 { 1229 cch = -cch; 1230 NewMsg.message = 1231 (lpMsg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR; 1232 } 1233 NewMsg.lParam = lpMsg->lParam; 1234 1235 /* Send all characters */ 1236 for (i = 0; i < cch; ++i) 1237 { 1238 TRACE("Msg: %x '%lc' (%04x) %08x\n", NewMsg.message, wch[i], wch[i], NewMsg.lParam); 1239 NewMsg.wParam = wch[i]; 1240 MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0, 0); 1241 } 1242 bResult = TRUE; 1243 } 1244 1245 TRACE("Leave IntTranslateKbdMessage ret %d, cch %d, msg %x, wch %x\n", 1246 bResult, cch, NewMsg.message, NewMsg.wParam); 1247 return bResult; 1248 } 1249 1250 /* 1251 * Map a virtual key code, or virtual scan code, to a scan code, key code, 1252 * or unshifted unicode character. 1253 * 1254 * Code: See Below 1255 * Type: 1256 * 0 -- Code is a virtual key code that is converted into a virtual scan code 1257 * that does not distinguish between left and right shift keys. 1258 * 1 -- Code is a virtual scan code that is converted into a virtual key code 1259 * that does not distinguish between left and right shift keys. 1260 * 2 -- Code is a virtual key code that is converted into an unshifted unicode 1261 * character. 1262 * 3 -- Code is a virtual scan code that is converted into a virtual key code 1263 * that distinguishes left and right shift keys. 1264 * KeyLayout: Keyboard layout handle 1265 * 1266 * @implemented 1267 */ 1268 static UINT 1269 IntMapVirtualKeyEx(UINT uCode, UINT Type, PKBDTABLES pKbdTbl) 1270 { 1271 UINT uRet = 0; 1272 1273 switch (Type) 1274 { 1275 case MAPVK_VK_TO_VSC: 1276 uCode = IntFixVk(uCode, FALSE); 1277 uRet = IntVkToVsc(uCode, pKbdTbl); 1278 if (uRet > 0xFF) // Fail for scancodes with prefix (e0, e1) 1279 uRet = 0; 1280 break; 1281 1282 case MAPVK_VSC_TO_VK: 1283 uRet = IntVscToVk(uCode, pKbdTbl) & 0xFF; 1284 uRet = IntSimplifyVk(uRet); 1285 break; 1286 1287 case MAPVK_VK_TO_CHAR: 1288 uRet = (UINT)IntVkToChar(uCode, pKbdTbl); 1289 break; 1290 1291 case MAPVK_VSC_TO_VK_EX: 1292 uRet = IntVscToVk(uCode, pKbdTbl) & 0xFF; 1293 break; 1294 1295 case MAPVK_VK_TO_VSC_EX: 1296 uRet = IntVkToVsc(uCode, pKbdTbl); 1297 break; 1298 1299 default: 1300 EngSetLastError(ERROR_INVALID_PARAMETER); 1301 ERR("Wrong type value: %u\n", Type); 1302 } 1303 1304 return uRet; 1305 } 1306 1307 /* 1308 * NtUserMapVirtualKeyEx 1309 * 1310 * Map a virtual key code, or virtual scan code, to a scan code, key code, 1311 * or unshifted unicode character. See IntMapVirtualKeyEx. 1312 */ 1313 UINT 1314 APIENTRY 1315 NtUserMapVirtualKeyEx(UINT uCode, UINT uType, DWORD keyboardId, HKL dwhkl) 1316 { 1317 PKBDTABLES pKbdTbl = NULL; 1318 UINT ret = 0; 1319 1320 TRACE("Enter NtUserMapVirtualKeyEx\n"); 1321 UserEnterShared(); 1322 1323 if (!dwhkl) 1324 { 1325 PTHREADINFO pti; 1326 1327 pti = PsGetCurrentThreadWin32Thread(); 1328 if (pti && pti->KeyboardLayout) 1329 pKbdTbl = pti->KeyboardLayout->spkf->pKbdTbl; 1330 } 1331 else 1332 { 1333 PKL pKl; 1334 1335 pKl = UserHklToKbl(dwhkl); 1336 if (pKl) 1337 pKbdTbl = pKl->spkf->pKbdTbl; 1338 } 1339 1340 if (pKbdTbl) 1341 ret = IntMapVirtualKeyEx(uCode, uType, pKbdTbl); 1342 1343 UserLeave(); 1344 TRACE("Leave NtUserMapVirtualKeyEx, ret=%u\n", ret); 1345 return ret; 1346 } 1347 1348 /* 1349 * NtUserToUnicodeEx 1350 * 1351 * Translates virtual key to characters 1352 */ 1353 int 1354 APIENTRY 1355 NtUserToUnicodeEx( 1356 UINT wVirtKey, 1357 UINT wScanCode, 1358 PBYTE pKeyStateUnsafe, 1359 LPWSTR pwszBuffUnsafe, 1360 INT cchBuff, 1361 UINT wFlags, 1362 HKL dwhkl) 1363 { 1364 PTHREADINFO pti; 1365 BYTE afKeyState[256 * 2 / 8] = {0}; 1366 PWCHAR pwszBuff = NULL; 1367 INT i, iRet = 0; 1368 PKL pKl = NULL; 1369 NTSTATUS Status = STATUS_SUCCESS; 1370 1371 TRACE("Enter NtUserSetKeyboardState\n"); 1372 1373 /* Return 0 if SC_KEY_UP bit is set */ 1374 if (wScanCode & SC_KEY_UP || wVirtKey >= 0x100) 1375 { 1376 ERR("Invalid parameter\n"); 1377 return 0; 1378 } 1379 1380 _SEH2_TRY 1381 { 1382 /* Probe and copy key state to smaller bitmap */ 1383 ProbeForRead(pKeyStateUnsafe, 256 * sizeof(BYTE), 1); 1384 for (i = 0; i < 256; ++i) 1385 { 1386 if (pKeyStateUnsafe[i] & KS_DOWN_BIT) 1387 SET_KEY_DOWN(afKeyState, i, TRUE); 1388 if (pKeyStateUnsafe[i] & KS_LOCK_BIT) 1389 SET_KEY_LOCKED(afKeyState, i, TRUE); 1390 } 1391 } 1392 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1393 { 1394 ERR("Cannot copy key state\n"); 1395 SetLastNtError(_SEH2_GetExceptionCode()); 1396 _SEH2_YIELD(return 0); 1397 } 1398 _SEH2_END; 1399 1400 pwszBuff = ExAllocatePoolWithTag(NonPagedPool, sizeof(WCHAR) * cchBuff, TAG_STRING); 1401 if (!pwszBuff) 1402 { 1403 ERR("ExAllocatePoolWithTag(%u) failed\n", sizeof(WCHAR) * cchBuff); 1404 return 0; 1405 } 1406 RtlZeroMemory(pwszBuff, sizeof(WCHAR) * cchBuff); 1407 1408 UserEnterExclusive(); // Note: We modify wchDead static variable 1409 1410 if (dwhkl) 1411 pKl = UserHklToKbl(dwhkl); 1412 1413 if (!pKl) 1414 { 1415 pti = PsGetCurrentThreadWin32Thread(); 1416 pKl = pti->KeyboardLayout; 1417 } 1418 1419 if (pKl) 1420 { 1421 iRet = IntToUnicodeEx(wVirtKey, 1422 wScanCode, 1423 afKeyState, 1424 pwszBuff, 1425 cchBuff, 1426 wFlags, 1427 pKl->spkf->pKbdTbl); 1428 1429 if (iRet) 1430 { 1431 Status = MmCopyToCaller(pwszBuffUnsafe, pwszBuff, cchBuff * sizeof(WCHAR)); 1432 } 1433 } 1434 else 1435 { 1436 ERR("No keyboard layout ?!\n"); 1437 Status = STATUS_INVALID_HANDLE; 1438 } 1439 1440 ExFreePoolWithTag(pwszBuff, TAG_STRING); 1441 1442 if (!NT_SUCCESS(Status)) 1443 { 1444 iRet = 0; 1445 SetLastNtError(Status); 1446 } 1447 1448 UserLeave(); 1449 TRACE("Leave NtUserSetKeyboardState, ret=%i\n", iRet); 1450 return iRet; 1451 } 1452 1453 /* 1454 * NtUserGetKeyNameText 1455 * 1456 * Gets key name from keyboard layout 1457 */ 1458 DWORD 1459 APIENTRY 1460 NtUserGetKeyNameText(LONG lParam, LPWSTR lpString, int cchSize) 1461 { 1462 PTHREADINFO pti; 1463 DWORD i, dwRet = 0; 1464 SIZE_T cchKeyName; 1465 WORD wScanCode = (lParam >> 16) & 0xFF; 1466 BOOL bExtKey = (HIWORD(lParam) & KF_EXTENDED) ? TRUE : FALSE; 1467 PKBDTABLES pKbdTbl; 1468 VSC_LPWSTR *pKeyNames = NULL; 1469 CONST WCHAR *pKeyName = NULL; 1470 WCHAR KeyNameBuf[2]; 1471 1472 TRACE("Enter NtUserGetKeyNameText\n"); 1473 1474 UserEnterShared(); 1475 1476 /* Get current keyboard layout */ 1477 pti = PsGetCurrentThreadWin32Thread(); 1478 pKbdTbl = pti ? pti->KeyboardLayout->spkf->pKbdTbl : 0; 1479 1480 if (!pKbdTbl || cchSize < 1) 1481 { 1482 ERR("Invalid parameter\n"); 1483 goto cleanup; 1484 } 1485 1486 /* "Do not care" flag */ 1487 if(lParam & LP_DO_NOT_CARE_BIT) 1488 { 1489 /* Note: We could do vsc -> vk -> vsc conversion, instead of using 1490 hardcoded scan codes, but it's not what Windows does */ 1491 if (wScanCode == SCANCODE_RSHIFT && !bExtKey) 1492 wScanCode = SCANCODE_LSHIFT; 1493 else if (wScanCode == SCANCODE_CTRL || wScanCode == SCANCODE_ALT) 1494 bExtKey = FALSE; 1495 } 1496 1497 if (bExtKey) 1498 pKeyNames = pKbdTbl->pKeyNamesExt; 1499 else 1500 pKeyNames = pKbdTbl->pKeyNames; 1501 1502 for (i = 0; pKeyNames[i].pwsz; i++) 1503 { 1504 if (pKeyNames[i].vsc == wScanCode) 1505 { 1506 pKeyName = pKeyNames[i].pwsz; 1507 break; 1508 } 1509 } 1510 1511 if (!pKeyName) 1512 { 1513 WORD wVk = IntVscToVk(wScanCode, pKbdTbl); 1514 1515 if (wVk) 1516 { 1517 KeyNameBuf[0] = IntVkToChar(wVk, pKbdTbl); 1518 KeyNameBuf[1] = 0; 1519 if (KeyNameBuf[0]) 1520 pKeyName = KeyNameBuf; 1521 } 1522 } 1523 1524 if (pKeyName) 1525 { 1526 cchKeyName = wcslen(pKeyName); 1527 if (cchKeyName > (cchSize - 1UL)) 1528 cchKeyName = cchSize - 1UL; // Don't count '\0' 1529 1530 _SEH2_TRY 1531 { 1532 ProbeForWrite(lpString, (cchKeyName + 1) * sizeof(WCHAR), 1); 1533 RtlCopyMemory(lpString, pKeyName, cchKeyName * sizeof(WCHAR)); 1534 lpString[cchKeyName] = UNICODE_NULL; 1535 dwRet = cchKeyName; 1536 } 1537 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1538 { 1539 SetLastNtError(_SEH2_GetExceptionCode()); 1540 } 1541 _SEH2_END; 1542 } 1543 else 1544 { 1545 EngSetLastError(ERROR_INVALID_PARAMETER); 1546 } 1547 1548 cleanup: 1549 UserLeave(); 1550 TRACE("Leave NtUserGetKeyNameText, ret=%lu\n", dwRet); 1551 return dwRet; 1552 } 1553 1554 /* 1555 * UserGetKeyboardType 1556 * 1557 * Returns some keyboard specific information 1558 */ 1559 DWORD FASTCALL 1560 UserGetKeyboardType( 1561 DWORD dwTypeFlag) 1562 { 1563 switch (dwTypeFlag) 1564 { 1565 case 0: /* Keyboard type */ 1566 return (DWORD)gKeyboardInfo.KeyboardIdentifier.Type; 1567 case 1: /* Keyboard Subtype */ 1568 return (DWORD)gKeyboardInfo.KeyboardIdentifier.Subtype; 1569 case 2: /* Number of F-keys */ 1570 return (DWORD)gKeyboardInfo.NumberOfFunctionKeys; 1571 default: 1572 ERR("Unknown type!\n"); 1573 return 0; /* Note: we don't have to set last error here */ 1574 } 1575 } 1576 1577 /* 1578 * NtUserVkKeyScanEx 1579 * 1580 * Based on IntTranslateChar, instead of processing VirtualKey match, 1581 * look for wChar match. 1582 */ 1583 DWORD 1584 APIENTRY 1585 NtUserVkKeyScanEx( 1586 WCHAR wch, 1587 HKL dwhkl, 1588 BOOL bUsehKL) 1589 { 1590 PKBDTABLES pKbdTbl; 1591 PVK_TO_WCHAR_TABLE pVkToWchTbl; 1592 PVK_TO_WCHARS10 pVkToWch; 1593 PKL pKl = NULL; 1594 DWORD i, dwModBits = 0, dwModNumber = 0, Ret = (DWORD)-1; 1595 1596 TRACE("NtUserVkKeyScanEx() wch %u, KbdLayout 0x%p\n", wch, dwhkl); 1597 UserEnterShared(); 1598 1599 if (bUsehKL) 1600 { 1601 // Use given keyboard layout 1602 if (dwhkl) 1603 pKl = UserHklToKbl(dwhkl); 1604 } 1605 else 1606 { 1607 // Use thread keyboard layout 1608 pKl = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->KeyboardLayout; 1609 } 1610 1611 if (!pKl) 1612 goto Exit; 1613 1614 pKbdTbl = pKl->spkf->pKbdTbl; 1615 1616 // Interate through all VkToWchar tables while pVkToWchars is not NULL 1617 for (i = 0; pKbdTbl->pVkToWcharTable[i].pVkToWchars; i++) 1618 { 1619 pVkToWchTbl = &pKbdTbl->pVkToWcharTable[i]; 1620 pVkToWch = (PVK_TO_WCHARS10)(pVkToWchTbl->pVkToWchars); 1621 1622 // Interate through all virtual keys 1623 while (pVkToWch->VirtualKey) 1624 { 1625 for (dwModNumber = 0; dwModNumber < pVkToWchTbl->nModifications; dwModNumber++) 1626 { 1627 if (pVkToWch->wch[dwModNumber] == wch) 1628 { 1629 dwModBits = pKbdTbl->pCharModifiers->ModNumber[dwModNumber]; 1630 TRACE("i %lu wC %04x: dwModBits %08x dwModNumber %08x MaxModBits %08x\n", 1631 i, wch, dwModBits, dwModNumber, pKbdTbl->pCharModifiers->wMaxModBits); 1632 Ret = (dwModBits << 8) | (pVkToWch->VirtualKey & 0xFF); 1633 goto Exit; 1634 } 1635 } 1636 pVkToWch = (PVK_TO_WCHARS10)(((BYTE *)pVkToWch) + pVkToWchTbl->cbSize); 1637 } 1638 } 1639 Exit: 1640 UserLeave(); 1641 return Ret; 1642 } 1643 1644 /* EOF */ 1645