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