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