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