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