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