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