xref: /reactos/win32ss/user/ntuser/keyboard.c (revision 7dd081e9)
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
InitKeyboardImpl(VOID)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
IntKeyboardGetIndicatorTrans(HANDLE hKeyboardDevice,PKEYBOARD_INDICATOR_TRANSLATION * ppIndicatorTrans)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
IntKeyboardUpdateLeds(HANDLE hKeyboardDevice,WORD wVk,WORD wScanCode)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
UserInitKeyboard(HANDLE hKeyboardDevice)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
IntSimplifyVk(WORD wVk)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
IntFixVk(WORD wVk,BOOL bExt)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
IntTranslateNumpadKey(WORD wVk)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
IntGetModBits(PKBDTABLES pKbdTbl,PBYTE pKeyState)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
IntTranslateChar(WORD wVirtKey,PBYTE pKeyState,PBOOL pbDead,PBOOL pbLigature,PWCHAR pwcTranslatedChar,PKBDTABLES pKbdTbl)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
IntToUnicodeEx(UINT wVirtKey,UINT wScanCode,PBYTE pKeyState,LPWSTR pwszBuff,int cchBuff,UINT wFlags,PKBDTABLES pKbdTbl)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
IntVkToVsc(WORD wVk,PKBDTABLES pKbdTbl)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
IntVscToVk(WORD wScanCode,PKBDTABLES pKbdTbl)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
IntVkToChar(WORD wVk,PKBDTABLES pKbdTbl)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
NtUserGetAsyncKeyState(INT Key)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
UpdateAsyncKeyState(WORD wVk,BOOL bIsDown)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
co_CallLowLevelKeyboardHook(WORD wVk,WORD wScanCode,DWORD dwFlags,BOOL bInjected,DWORD dwTime,DWORD dwExtraInfo)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
SnapWindow(HWND hWnd)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
IntGetNextKL(_In_ PKL pKL,_In_ BOOL bNext,_In_ BOOL bSameLang)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
IntLanguageToggle(_In_ PUSER_MESSAGE_QUEUE pFocusQueue,_In_ BOOL bSameLang,_In_ INT nKeyState)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
IntCheckLanguageToggle(_In_ PUSER_MESSAGE_QUEUE pFocusQueue,_In_ BOOL bIsDown,_In_ WORD wVk,_Inout_ PINT pKeyState)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
ProcessKeyEvent(WORD wVk,WORD wScanCode,DWORD dwFlags,BOOL bInjected,DWORD dwTime,DWORD dwExtraInfo)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
UserSendKeyboardInput(KEYBDINPUT * pKbdInput,BOOL bInjected)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
UserProcessKeyboardInput(PKEYBOARD_INPUT_DATA pKbdInputData)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
IntTranslateKbdMessage(LPMSG lpMsg,UINT flags)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
IntMapVirtualKeyEx(UINT uCode,UINT Type,PKBDTABLES pKbdTbl)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
NtUserMapVirtualKeyEx(UINT uCode,UINT uType,DWORD keyboardId,HKL dwhkl)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
NtUserToUnicodeEx(UINT wVirtKey,UINT wScanCode,PBYTE pKeyStateUnsafe,LPWSTR pwszBuffUnsafe,INT cchBuff,UINT wFlags,HKL dwhkl)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
NtUserGetKeyNameText(LONG lParam,LPWSTR lpString,int cchSize)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
UserGetKeyboardType(DWORD dwTypeFlag)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
NtUserVkKeyScanEx(WCHAR wch,HKL dwhkl,BOOL bUsehKL)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