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