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