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