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