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