xref: /reactos/win32ss/user/ntuser/keyboard.c (revision 5efb6e3d)
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 CODE_SEG("INIT")
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 || Key < 0)
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 
815     if (PRIMARYLANGID(gusLanguageID) == LANG_JAPANESE)
816     {
817         /* Japanese special! */
818         if (IS_KEY_DOWN(gafAsyncKeyState, VK_SHIFT))
819         {
820             if (wSimpleVk == VK_OEM_ATTN)
821                 wSimpleVk = VK_CAPITAL;
822             else if (wSimpleVk == VK_OEM_COPY)
823                 wSimpleVk = VK_OEM_FINISH;
824         }
825     }
826 
827     bWasSimpleDown = IS_KEY_DOWN(gafAsyncKeyState, wSimpleVk);
828 
829     /* Update key without shifts */
830     wVk2 = IntFixVk(wSimpleVk, !bExt);
831     bIsSimpleDown = bIsDown || IS_KEY_DOWN(gafAsyncKeyState, wVk2);
832     UpdateAsyncKeyState(wSimpleVk, bIsSimpleDown);
833 
834     if (bIsDown)
835     {
836         /* Update keyboard LEDs */
837         IntKeyboardUpdateLeds(ghKeyboardDevice,
838                               wSimpleVk,
839                               wScanCode);
840     }
841 
842     /* Call WH_KEYBOARD_LL hook */
843     if (co_CallLowLevelKeyboardHook(wVk, wScanCode, dwFlags, bInjected, dwTime, dwExtraInfo))
844     {
845         ERR("Kbd msg dropped by WH_KEYBOARD_LL hook\n");
846         bPostMsg = FALSE;
847     }
848 
849     /* Check if this is a hotkey */
850     if (co_UserProcessHotKeys(wSimpleVk, bIsDown)) //// Check if this is correct, refer to hotkey sequence message tests.
851     {
852         TRACE("HotKey Processed\n");
853         bPostMsg = FALSE;
854     }
855 
856     wFixedVk = IntFixVk(wSimpleVk, bExt); /* LSHIFT + EXT = RSHIFT */
857     if (wSimpleVk == VK_SHIFT) /* shift can't be extended */
858         bExt = FALSE;
859 
860     /* If we have a focus queue, post a keyboard message */
861     pFocusQueue = IntGetFocusMessageQueue();
862     TRACE("ProcessKeyEvent Q 0x%p Active pWnd 0x%p Focus pWnd 0x%p\n",
863            pFocusQueue,
864            (pFocusQueue ?  pFocusQueue->spwndActive : 0),
865            (pFocusQueue ?  pFocusQueue->spwndFocus : 0));
866 
867     /* If it is F10 or ALT is down and CTRL is up, it's a system key */
868     if ( wVk == VK_F10 ||
869         (wSimpleVk == VK_MENU && bMenuDownRecently) ||
870         (IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) &&
871         !IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL)) ||
872          // See MSDN WM_SYSKEYDOWN/UP fixes last wine Win test_keyboard_input.
873         (pFocusQueue && !pFocusQueue->spwndFocus) )
874     {
875         bMenuDownRecently = FALSE; // reset
876         if (bIsDown)
877         {
878             Msg.message = WM_SYSKEYDOWN;
879             if (wSimpleVk == VK_MENU)
880             {
881                 // Note: If only LALT is pressed WM_SYSKEYUP is generated instead of WM_KEYUP
882                 bMenuDownRecently = TRUE;
883             }
884         }
885         else
886             Msg.message = WM_SYSKEYUP;
887     }
888     else
889     {
890         if (bIsDown)
891             Msg.message = WM_KEYDOWN;
892         else
893             Msg.message = WM_KEYUP;
894     }
895 
896     /* Update async state of not simplified vk here.
897        See user32_apitest:GetKeyState */
898     UpdateAsyncKeyState(wFixedVk, bIsDown);
899 
900     /* Alt-Tab/Esc Check. Use FocusQueue or RIT Queue */
901     if (bIsSimpleDown && !bWasSimpleDown &&
902         IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) &&
903         !IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL) &&
904         (wVk == VK_ESCAPE || wVk == VK_TAB))
905     {
906        TRACE("Alt-Tab/Esc Pressed wParam %x\n",wVk);
907     }
908 
909     if (bIsDown && wVk == VK_SNAPSHOT)
910     {
911         if (pFocusQueue &&
912             IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) &&
913             !IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL))
914         {
915             // Snap from Active Window, Focus can be null.
916             SnapWindow(pFocusQueue->spwndActive ? UserHMGetHandle(pFocusQueue->spwndActive) : 0);
917         }
918         else
919             SnapWindow(NULL); // Snap Desktop.
920     }
921     else if (pFocusQueue && bPostMsg)
922     {
923         PWND Wnd = pFocusQueue->spwndFocus; // SysInit.....
924 
925         pti = pFocusQueue->ptiKeyboard;
926 
927         if (!Wnd && pFocusQueue->spwndActive) // SysInit.....
928         {
929            // Going with Active. WM_SYSKEYXXX last wine Win test_keyboard_input.
930            Wnd = pFocusQueue->spwndActive;
931         }
932         if (Wnd) pti = Wnd->head.pti;
933 
934         /* Init message */
935         Msg.hwnd = Wnd ? UserHMGetHandle(Wnd) : NULL;
936         Msg.wParam = wFixedVk & 0xFF; /* Note: It's simplified by msg queue */
937         Msg.lParam = MAKELPARAM(1, wScanCode);
938         Msg.time = dwTime;
939         Msg.pt = gpsi->ptCursor;
940 
941         if ( Msg.message == WM_KEYDOWN || Msg.message == WM_SYSKEYDOWN )
942         {
943            if ( (Msg.wParam == VK_SHIFT ||
944                  Msg.wParam == VK_CONTROL ||
945                  Msg.wParam == VK_MENU ) &&
946                !IS_KEY_DOWN(gafAsyncKeyState, Msg.wParam))
947            {
948               ERR("Set last input\n");
949               //ptiLastInput = pti;
950            }
951         }
952 
953         /* If it is VK_PACKET, high word of wParam is used for wchar */
954         if (!bPacket)
955         {
956             if (bExt)
957                 Msg.lParam |= KF_EXTENDED << 16;
958             if (IS_KEY_DOWN(gafAsyncKeyState, VK_MENU))
959                 Msg.lParam |= KF_ALTDOWN << 16;
960             if (bWasSimpleDown)
961                 Msg.lParam |= KF_REPEAT << 16;
962             if (!bIsDown)
963                 Msg.lParam |= KF_UP << 16;
964             /* FIXME: Set KF_DLGMODE and KF_MENUMODE when needed */
965             if (pFocusQueue->QF_flags & QF_DIALOGACTIVE)
966                 Msg.lParam |= KF_DLGMODE << 16;
967             if (pFocusQueue->MenuOwner) // pti->pMenuState->fMenuStarted
968                 Msg.lParam |= KF_MENUMODE << 16;
969         }
970 
971         // Post mouse move before posting key buttons, to keep it syned.
972         if (pFocusQueue->QF_flags & QF_MOUSEMOVED)
973         {
974            IntCoalesceMouseMove(pti);
975         }
976 
977         /* Post a keyboard message */
978         TRACE("Posting keyboard msg %u wParam 0x%x lParam 0x%x\n", Msg.message, Msg.wParam, Msg.lParam);
979         if (!Wnd) {ERR("Window is NULL\n");}
980         MsqPostMessage(pti, &Msg, TRUE, QS_KEY, 0, dwExtraInfo);
981     }
982     return TRUE;
983 }
984 
985 BOOL NTAPI
986 UserSendKeyboardInput(KEYBDINPUT *pKbdInput, BOOL bInjected)
987 {
988     WORD wScanCode, wVk;
989     PKL pKl = NULL;
990     PKBDTABLES pKbdTbl;
991     PUSER_MESSAGE_QUEUE pFocusQueue;
992     DWORD dwTime;
993     BOOL bExt = (pKbdInput->dwFlags & KEYEVENTF_EXTENDEDKEY) ? TRUE : FALSE;
994 
995     gppiInputProvider = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->ppi;
996 
997     /* Find the target thread whose locale is in effect */
998     pFocusQueue = IntGetFocusMessageQueue();
999 
1000     if (pFocusQueue && pFocusQueue->ptiKeyboard)
1001     {
1002         pKl = pFocusQueue->ptiKeyboard->KeyboardLayout;
1003     }
1004 
1005     if (!pKl)
1006         pKl = W32kGetDefaultKeyLayout();
1007     if (!pKl)
1008     {
1009         ERR("No keyboard layout!\n");
1010         return FALSE;
1011     }
1012 
1013     pKbdTbl = pKl->spkf->pKbdTbl;
1014 
1015     /* Note: wScan field is always used */
1016     wScanCode = pKbdInput->wScan;
1017 
1018     if (pKbdInput->dwFlags & KEYEVENTF_UNICODE)
1019     {
1020         /* Generate WM_KEYDOWN msg with wParam == VK_PACKET and
1021            high order word of lParam == pKbdInput->wScan */
1022         wVk = VK_PACKET;
1023     }
1024     else
1025     {
1026         wScanCode &= 0x7F;
1027         if (pKbdInput->dwFlags & KEYEVENTF_SCANCODE)
1028         {
1029             /* Don't ignore invalid scan codes */
1030             wVk = IntVscToVk(wScanCode | (bExt ? 0xE000 : 0), pKbdTbl);
1031             if (!wVk) /* use 0xFF if vsc is invalid */
1032                 wVk = 0xFF;
1033         }
1034         else
1035         {
1036             wVk = pKbdInput->wVk;
1037         }
1038 
1039         /* Remove all virtual key flags (KBDEXT, KBDMULTIVK, KBDSPECIAL, KBDNUMPAD) */
1040         wVk &= 0xFF;
1041     }
1042 
1043     /* If time is given, use it */
1044     if (pKbdInput->time)
1045         dwTime = pKbdInput->time;
1046     else
1047     {
1048         dwTime = EngGetTickCount32();
1049     }
1050 
1051     if (wVk == VK_RMENU && (pKbdTbl->fLocaleFlags & KLLF_ALTGR))
1052     {
1053         /* For AltGr keyboards RALT generates CTRL events */
1054         ProcessKeyEvent(VK_LCONTROL, 0, pKbdInput->dwFlags & KEYEVENTF_KEYUP, bInjected, dwTime, 0);
1055     }
1056 
1057     /* Finally process this key */
1058     return ProcessKeyEvent(wVk, wScanCode, pKbdInput->dwFlags, bInjected, dwTime, pKbdInput->dwExtraInfo);
1059 }
1060 
1061 /*
1062  * UserProcessKeyboardInput
1063  *
1064  * Process raw keyboard input data
1065  */
1066 VOID NTAPI
1067 UserProcessKeyboardInput(
1068     PKEYBOARD_INPUT_DATA pKbdInputData)
1069 {
1070     WORD wScanCode, wVk;
1071     PKL pKl = NULL;
1072     PKBDTABLES pKbdTbl;
1073     PUSER_MESSAGE_QUEUE pFocusQueue;
1074 
1075     /* Calculate scan code with prefix */
1076     wScanCode = pKbdInputData->MakeCode & 0x7F;
1077     if (pKbdInputData->Flags & KEY_E0)
1078         wScanCode |= 0xE000;
1079     if (pKbdInputData->Flags & KEY_E1)
1080         wScanCode |= 0xE100;
1081 
1082     /* Find the target thread whose locale is in effect */
1083     pFocusQueue = IntGetFocusMessageQueue();
1084 
1085     if (pFocusQueue && pFocusQueue->ptiKeyboard)
1086     {
1087         pKl = pFocusQueue->ptiKeyboard->KeyboardLayout;
1088     }
1089 
1090     if (!pKl)
1091         pKl = W32kGetDefaultKeyLayout();
1092     if (!pKl)
1093         return;
1094 
1095     pKbdTbl = pKl->spkf->pKbdTbl;
1096 
1097     /* Convert scan code to virtual key.
1098        Note: We could call UserSendKeyboardInput using scan code,
1099              but it wouldn't interpret E1 key(s) properly */
1100     wVk = IntVscToVk(wScanCode, pKbdTbl);
1101     TRACE("UserProcessKeyboardInput: %x (break: %u) -> %x\n",
1102           wScanCode, (pKbdInputData->Flags & KEY_BREAK) ? 1u : 0, wVk);
1103 
1104     if (wVk)
1105     {
1106         KEYBDINPUT KbdInput;
1107 
1108         /* Support numlock */
1109         if ((wVk & KBDNUMPAD) && IS_KEY_LOCKED(gafAsyncKeyState, VK_NUMLOCK))
1110         {
1111             wVk = IntTranslateNumpadKey(wVk & 0xFF);
1112         }
1113 
1114         /* Send keyboard input */
1115         KbdInput.wVk = wVk & 0xFF;
1116         KbdInput.wScan = wScanCode & 0x7F;
1117         KbdInput.dwFlags = 0;
1118         if (pKbdInputData->Flags & KEY_BREAK)
1119             KbdInput.dwFlags |= KEYEVENTF_KEYUP;
1120 
1121         if (wVk & KBDEXT)
1122             KbdInput.dwFlags |= KEYEVENTF_EXTENDEDKEY;
1123         //
1124         // Based on wine input:test_Input_blackbox this is okay. It seems the
1125         // bit did not get set and more research is needed. Now the right
1126         // shift works.
1127         //
1128         if (wVk == VK_RSHIFT)
1129             KbdInput.dwFlags |= KEYEVENTF_EXTENDEDKEY;
1130 
1131         KbdInput.time = 0;
1132         KbdInput.dwExtraInfo = pKbdInputData->ExtraInformation;
1133         UserSendKeyboardInput(&KbdInput, FALSE);
1134 
1135         /* E1 keys don't have break code */
1136         if (pKbdInputData->Flags & KEY_E1)
1137         {
1138             /* Send key up event */
1139             KbdInput.dwFlags |= KEYEVENTF_KEYUP;
1140             UserSendKeyboardInput(&KbdInput, FALSE);
1141         }
1142     }
1143 }
1144 
1145 /*
1146  * IntTranslateKbdMessage
1147  *
1148  * Addes WM_(SYS)CHAR messages to message queue if message
1149  * describes key which produce character.
1150  */
1151 BOOL FASTCALL
1152 IntTranslateKbdMessage(LPMSG lpMsg,
1153                        UINT flags)
1154 {
1155     PTHREADINFO pti;
1156     INT cch = 0, i;
1157     WCHAR wch[3] = { 0 };
1158     MSG NewMsg = { 0 };
1159     PKBDTABLES pKbdTbl;
1160     BOOL bResult = FALSE;
1161 
1162     switch(lpMsg->message)
1163     {
1164        case WM_KEYDOWN:
1165        case WM_KEYUP:
1166        case WM_SYSKEYDOWN:
1167        case WM_SYSKEYUP:
1168           break;
1169        default:
1170           return FALSE;
1171     }
1172 
1173     pti = PsGetCurrentThreadWin32Thread();
1174 
1175     if (!pti->KeyboardLayout)
1176     {
1177         PKL pDefKL = W32kGetDefaultKeyLayout();
1178         UserAssignmentLock((PVOID*)&(pti->KeyboardLayout), pDefKL);
1179         if (pDefKL)
1180         {
1181             pti->pClientInfo->hKL = pDefKL->hkl;
1182             pKbdTbl = pDefKL->spkf->pKbdTbl;
1183         }
1184         else
1185         {
1186             pti->pClientInfo->hKL = NULL;
1187             pKbdTbl = NULL;
1188         }
1189     }
1190     else
1191        pKbdTbl = pti->KeyboardLayout->spkf->pKbdTbl;
1192     if (!pKbdTbl)
1193         return FALSE;
1194 
1195     if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN)
1196         return FALSE;
1197 
1198     /* Init pt, hwnd and time msg fields */
1199     NewMsg.pt = gpsi->ptCursor;
1200     NewMsg.hwnd = lpMsg->hwnd;
1201     NewMsg.time = EngGetTickCount32();
1202 
1203     TRACE("Enter IntTranslateKbdMessage msg %s, vk %x\n",
1204         lpMsg->message == WM_SYSKEYDOWN ? "WM_SYSKEYDOWN" : "WM_KEYDOWN", lpMsg->wParam);
1205 
1206     if (lpMsg->wParam == VK_PACKET)
1207     {
1208         NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
1209         NewMsg.wParam = HIWORD(lpMsg->lParam);
1210         NewMsg.lParam = LOWORD(lpMsg->lParam);
1211         MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0, 0);
1212         return TRUE;
1213     }
1214 
1215     cch = IntToUnicodeEx(lpMsg->wParam,
1216                          HIWORD(lpMsg->lParam) & 0xFF,
1217                          pti->MessageQueue->afKeyState,
1218                          wch,
1219                          sizeof(wch) / sizeof(wch[0]),
1220                          0,
1221                          pKbdTbl);
1222 
1223     if (cch)
1224     {
1225         if (cch > 0) /* Normal characters */
1226             NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
1227         else /* Dead character */
1228         {
1229             cch = -cch;
1230             NewMsg.message =
1231                 (lpMsg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
1232         }
1233         NewMsg.lParam = lpMsg->lParam;
1234 
1235         /* Send all characters */
1236         for (i = 0; i < cch; ++i)
1237         {
1238             TRACE("Msg: %x '%lc' (%04x) %08x\n", NewMsg.message, wch[i], wch[i], NewMsg.lParam);
1239             NewMsg.wParam = wch[i];
1240             MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0, 0);
1241         }
1242         bResult = TRUE;
1243     }
1244 
1245     TRACE("Leave IntTranslateKbdMessage ret %d, cch %d, msg %x, wch %x\n",
1246         bResult, cch, NewMsg.message, NewMsg.wParam);
1247     return bResult;
1248 }
1249 
1250 /*
1251  * Map a virtual key code, or virtual scan code, to a scan code, key code,
1252  * or unshifted unicode character.
1253  *
1254  * Code: See Below
1255  * Type:
1256  * 0 -- Code is a virtual key code that is converted into a virtual scan code
1257  *      that does not distinguish between left and right shift keys.
1258  * 1 -- Code is a virtual scan code that is converted into a virtual key code
1259  *      that does not distinguish between left and right shift keys.
1260  * 2 -- Code is a virtual key code that is converted into an unshifted unicode
1261  *      character.
1262  * 3 -- Code is a virtual scan code that is converted into a virtual key code
1263  *      that distinguishes left and right shift keys.
1264  * KeyLayout: Keyboard layout handle
1265  *
1266  * @implemented
1267  */
1268 static UINT
1269 IntMapVirtualKeyEx(UINT uCode, UINT Type, PKBDTABLES pKbdTbl)
1270 {
1271     UINT uRet = 0;
1272 
1273     switch (Type)
1274     {
1275         case MAPVK_VK_TO_VSC:
1276             uCode = IntFixVk(uCode, FALSE);
1277             uRet = IntVkToVsc(uCode, pKbdTbl);
1278             if (uRet > 0xFF) // Fail for scancodes with prefix (e0, e1)
1279                 uRet = 0;
1280             break;
1281 
1282         case MAPVK_VSC_TO_VK:
1283             uRet = IntVscToVk(uCode, pKbdTbl) & 0xFF;
1284             uRet = IntSimplifyVk(uRet);
1285             break;
1286 
1287         case MAPVK_VK_TO_CHAR:
1288             uRet = (UINT)IntVkToChar(uCode, pKbdTbl);
1289         break;
1290 
1291         case MAPVK_VSC_TO_VK_EX:
1292             uRet = IntVscToVk(uCode, pKbdTbl) & 0xFF;
1293             break;
1294 
1295         case MAPVK_VK_TO_VSC_EX:
1296             uRet = IntVkToVsc(uCode, pKbdTbl);
1297             break;
1298 
1299         default:
1300             EngSetLastError(ERROR_INVALID_PARAMETER);
1301             ERR("Wrong type value: %u\n", Type);
1302     }
1303 
1304     return uRet;
1305 }
1306 
1307 /*
1308  * NtUserMapVirtualKeyEx
1309  *
1310  * Map a virtual key code, or virtual scan code, to a scan code, key code,
1311  * or unshifted unicode character. See IntMapVirtualKeyEx.
1312  */
1313 UINT
1314 APIENTRY
1315 NtUserMapVirtualKeyEx(UINT uCode, UINT uType, DWORD keyboardId, HKL dwhkl)
1316 {
1317     PKBDTABLES pKbdTbl = NULL;
1318     UINT ret = 0;
1319 
1320     TRACE("Enter NtUserMapVirtualKeyEx\n");
1321     UserEnterShared();
1322 
1323     if (!dwhkl)
1324     {
1325         PTHREADINFO pti;
1326 
1327         pti = PsGetCurrentThreadWin32Thread();
1328         if (pti && pti->KeyboardLayout)
1329             pKbdTbl = pti->KeyboardLayout->spkf->pKbdTbl;
1330     }
1331     else
1332     {
1333         PKL pKl;
1334 
1335         pKl = UserHklToKbl(dwhkl);
1336         if (pKl)
1337             pKbdTbl = pKl->spkf->pKbdTbl;
1338     }
1339 
1340     if (pKbdTbl)
1341         ret = IntMapVirtualKeyEx(uCode, uType, pKbdTbl);
1342 
1343     UserLeave();
1344     TRACE("Leave NtUserMapVirtualKeyEx, ret=%u\n", ret);
1345     return ret;
1346 }
1347 
1348 /*
1349  * NtUserToUnicodeEx
1350  *
1351  * Translates virtual key to characters
1352  */
1353 int
1354 APIENTRY
1355 NtUserToUnicodeEx(
1356     UINT wVirtKey,
1357     UINT wScanCode,
1358     PBYTE pKeyStateUnsafe,
1359     LPWSTR pwszBuffUnsafe,
1360     INT cchBuff,
1361     UINT wFlags,
1362     HKL dwhkl)
1363 {
1364     PTHREADINFO pti;
1365     BYTE afKeyState[256 * 2 / 8] = {0};
1366     PWCHAR pwszBuff = NULL;
1367     INT i, iRet = 0;
1368     PKL pKl = NULL;
1369     NTSTATUS Status = STATUS_SUCCESS;
1370 
1371     TRACE("Enter NtUserSetKeyboardState\n");
1372 
1373     /* Return 0 if SC_KEY_UP bit is set */
1374     if (wScanCode & SC_KEY_UP || wVirtKey >= 0x100)
1375     {
1376         ERR("Invalid parameter\n");
1377         return 0;
1378     }
1379 
1380     _SEH2_TRY
1381     {
1382         /* Probe and copy key state to smaller bitmap */
1383         ProbeForRead(pKeyStateUnsafe, 256 * sizeof(BYTE), 1);
1384         for (i = 0; i < 256; ++i)
1385         {
1386             if (pKeyStateUnsafe[i] & KS_DOWN_BIT)
1387                 SET_KEY_DOWN(afKeyState, i, TRUE);
1388             if (pKeyStateUnsafe[i] & KS_LOCK_BIT)
1389                 SET_KEY_LOCKED(afKeyState, i, TRUE);
1390         }
1391     }
1392     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1393     {
1394         ERR("Cannot copy key state\n");
1395         SetLastNtError(_SEH2_GetExceptionCode());
1396         _SEH2_YIELD(return 0);
1397     }
1398     _SEH2_END;
1399 
1400     pwszBuff = ExAllocatePoolWithTag(NonPagedPool, sizeof(WCHAR) * cchBuff, TAG_STRING);
1401     if (!pwszBuff)
1402     {
1403         ERR("ExAllocatePoolWithTag(%u) failed\n", sizeof(WCHAR) * cchBuff);
1404         return 0;
1405     }
1406     RtlZeroMemory(pwszBuff, sizeof(WCHAR) * cchBuff);
1407 
1408     UserEnterExclusive(); // Note: We modify wchDead static variable
1409 
1410     if (dwhkl)
1411         pKl = UserHklToKbl(dwhkl);
1412 
1413     if (!pKl)
1414     {
1415         pti = PsGetCurrentThreadWin32Thread();
1416         pKl = pti->KeyboardLayout;
1417     }
1418 
1419     if (pKl)
1420     {
1421         iRet = IntToUnicodeEx(wVirtKey,
1422                             wScanCode,
1423                             afKeyState,
1424                             pwszBuff,
1425                             cchBuff,
1426                             wFlags,
1427                             pKl->spkf->pKbdTbl);
1428 
1429         if (iRet)
1430         {
1431             Status = MmCopyToCaller(pwszBuffUnsafe, pwszBuff, cchBuff * sizeof(WCHAR));
1432         }
1433     }
1434     else
1435     {
1436         ERR("No keyboard layout ?!\n");
1437         Status = STATUS_INVALID_HANDLE;
1438     }
1439 
1440     ExFreePoolWithTag(pwszBuff, TAG_STRING);
1441 
1442     if (!NT_SUCCESS(Status))
1443     {
1444         iRet = 0;
1445         SetLastNtError(Status);
1446     }
1447 
1448     UserLeave();
1449     TRACE("Leave NtUserSetKeyboardState, ret=%i\n", iRet);
1450     return iRet;
1451 }
1452 
1453 /*
1454  * NtUserGetKeyNameText
1455  *
1456  * Gets key name from keyboard layout
1457  */
1458 DWORD
1459 APIENTRY
1460 NtUserGetKeyNameText(LONG lParam, LPWSTR lpString, int cchSize)
1461 {
1462     PTHREADINFO pti;
1463     DWORD i, dwRet = 0;
1464     SIZE_T cchKeyName;
1465     WORD wScanCode = (lParam >> 16) & 0xFF;
1466     BOOL bExtKey = (HIWORD(lParam) & KF_EXTENDED) ? TRUE : FALSE;
1467     PKBDTABLES pKbdTbl;
1468     VSC_LPWSTR *pKeyNames = NULL;
1469     CONST WCHAR *pKeyName = NULL;
1470     WCHAR KeyNameBuf[2];
1471 
1472     TRACE("Enter NtUserGetKeyNameText\n");
1473 
1474     UserEnterShared();
1475 
1476     /* Get current keyboard layout */
1477     pti = PsGetCurrentThreadWin32Thread();
1478     pKbdTbl = pti ? pti->KeyboardLayout->spkf->pKbdTbl : 0;
1479 
1480     if (!pKbdTbl || cchSize < 1)
1481     {
1482         ERR("Invalid parameter\n");
1483         goto cleanup;
1484     }
1485 
1486     /* "Do not care" flag */
1487     if(lParam & LP_DO_NOT_CARE_BIT)
1488     {
1489         /* Note: We could do vsc -> vk -> vsc conversion, instead of using
1490                  hardcoded scan codes, but it's not what Windows does */
1491         if (wScanCode == SCANCODE_RSHIFT && !bExtKey)
1492             wScanCode = SCANCODE_LSHIFT;
1493         else if (wScanCode == SCANCODE_CTRL || wScanCode == SCANCODE_ALT)
1494             bExtKey = FALSE;
1495     }
1496 
1497     if (bExtKey)
1498         pKeyNames = pKbdTbl->pKeyNamesExt;
1499     else
1500         pKeyNames = pKbdTbl->pKeyNames;
1501 
1502     for (i = 0; pKeyNames[i].pwsz; i++)
1503     {
1504         if (pKeyNames[i].vsc == wScanCode)
1505         {
1506             pKeyName = pKeyNames[i].pwsz;
1507             break;
1508         }
1509     }
1510 
1511     if (!pKeyName)
1512     {
1513         WORD wVk = IntVscToVk(wScanCode, pKbdTbl);
1514 
1515         if (wVk)
1516         {
1517             KeyNameBuf[0] = IntVkToChar(wVk, pKbdTbl);
1518             KeyNameBuf[1] = 0;
1519             if (KeyNameBuf[0])
1520                 pKeyName = KeyNameBuf;
1521         }
1522     }
1523 
1524     if (pKeyName)
1525     {
1526         cchKeyName = wcslen(pKeyName);
1527         if (cchKeyName > (cchSize - 1UL))
1528             cchKeyName = cchSize - 1UL; // Don't count '\0'
1529 
1530         _SEH2_TRY
1531         {
1532             ProbeForWrite(lpString, (cchKeyName + 1) * sizeof(WCHAR), 1);
1533             RtlCopyMemory(lpString, pKeyName, cchKeyName * sizeof(WCHAR));
1534             lpString[cchKeyName] = UNICODE_NULL;
1535             dwRet = cchKeyName;
1536         }
1537         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1538         {
1539             SetLastNtError(_SEH2_GetExceptionCode());
1540         }
1541         _SEH2_END;
1542     }
1543     else
1544     {
1545         EngSetLastError(ERROR_INVALID_PARAMETER);
1546     }
1547 
1548 cleanup:
1549     UserLeave();
1550     TRACE("Leave NtUserGetKeyNameText, ret=%lu\n", dwRet);
1551     return dwRet;
1552 }
1553 
1554 /*
1555  * UserGetKeyboardType
1556  *
1557  * Returns some keyboard specific information
1558  */
1559 DWORD FASTCALL
1560 UserGetKeyboardType(
1561     DWORD dwTypeFlag)
1562 {
1563     switch (dwTypeFlag)
1564     {
1565         case 0:        /* Keyboard type */
1566             return (DWORD)gKeyboardInfo.KeyboardIdentifier.Type;
1567         case 1:        /* Keyboard Subtype */
1568             return (DWORD)gKeyboardInfo.KeyboardIdentifier.Subtype;
1569         case 2:        /* Number of F-keys */
1570             return (DWORD)gKeyboardInfo.NumberOfFunctionKeys;
1571         default:
1572             ERR("Unknown type!\n");
1573             return 0;    /* Note: we don't have to set last error here */
1574     }
1575 }
1576 
1577 /*
1578  * NtUserVkKeyScanEx
1579  *
1580  * Based on IntTranslateChar, instead of processing VirtualKey match,
1581  * look for wChar match.
1582  */
1583 DWORD
1584 APIENTRY
1585 NtUserVkKeyScanEx(
1586     WCHAR wch,
1587     HKL dwhkl,
1588     BOOL bUsehKL)
1589 {
1590     PKBDTABLES pKbdTbl;
1591     PVK_TO_WCHAR_TABLE pVkToWchTbl;
1592     PVK_TO_WCHARS10 pVkToWch;
1593     PKL pKl = NULL;
1594     DWORD i, dwModBits = 0, dwModNumber = 0, Ret = (DWORD)-1;
1595 
1596     TRACE("NtUserVkKeyScanEx() wch %u, KbdLayout 0x%p\n", wch, dwhkl);
1597     UserEnterShared();
1598 
1599     if (bUsehKL)
1600     {
1601         // Use given keyboard layout
1602         if (dwhkl)
1603             pKl = UserHklToKbl(dwhkl);
1604     }
1605     else
1606     {
1607         // Use thread keyboard layout
1608         pKl = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->KeyboardLayout;
1609     }
1610 
1611     if (!pKl)
1612         goto Exit;
1613 
1614     pKbdTbl = pKl->spkf->pKbdTbl;
1615 
1616     // Interate through all VkToWchar tables while pVkToWchars is not NULL
1617     for (i = 0; pKbdTbl->pVkToWcharTable[i].pVkToWchars; i++)
1618     {
1619         pVkToWchTbl = &pKbdTbl->pVkToWcharTable[i];
1620         pVkToWch = (PVK_TO_WCHARS10)(pVkToWchTbl->pVkToWchars);
1621 
1622         // Interate through all virtual keys
1623         while (pVkToWch->VirtualKey)
1624         {
1625             for (dwModNumber = 0; dwModNumber < pVkToWchTbl->nModifications; dwModNumber++)
1626             {
1627                 if (pVkToWch->wch[dwModNumber] == wch)
1628                 {
1629                     dwModBits = pKbdTbl->pCharModifiers->ModNumber[dwModNumber];
1630                     TRACE("i %lu wC %04x: dwModBits %08x dwModNumber %08x MaxModBits %08x\n",
1631                           i, wch, dwModBits, dwModNumber, pKbdTbl->pCharModifiers->wMaxModBits);
1632                     Ret = (dwModBits << 8) | (pVkToWch->VirtualKey & 0xFF);
1633                     goto Exit;
1634                 }
1635             }
1636             pVkToWch = (PVK_TO_WCHARS10)(((BYTE *)pVkToWch) + pVkToWchTbl->cbSize);
1637         }
1638     }
1639 Exit:
1640     UserLeave();
1641     return Ret;
1642 }
1643 
1644 /* EOF */
1645