Merge from amd64 branch:
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / keyboard.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * PURPOSE: Messages
23 * FILE: subsys/win32k/ntuser/keyboard.c
24 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * REVISION HISTORY:
26 * 06-06-2001 CSH Created
27 */
28
29 /* INCLUDES ******************************************************************/
30
31 #include <w32k.h>
32
33 #define NDEBUG
34 #include <debug.h>
35
36
37 /* Lock modifiers */
38 #define CAPITAL_BIT 0x80000000
39 #define NUMLOCK_BIT 0x40000000
40 #define MOD_BITS_MASK 0x3fffffff
41 #define MOD_KCTRL 0x02
42 /* Key States */
43 #define KS_DOWN_MASK 0xc0
44 #define KS_DOWN_BIT 0x80
45 #define KS_LOCK_BIT 0x01
46 /* lParam bits */
47 #define LP_EXT_BIT (1<<24)
48 /* From kbdxx.c -- Key changes with numlock */
49 #define KNUMP 0x400
50
51
52 BYTE gQueueKeyStateTable[256];
53
54
55
56 /* FUNCTIONS *****************************************************************/
57
58 /* Initialization -- Right now, just zero the key state and init the lock */
59 NTSTATUS FASTCALL InitKeyboardImpl(VOID)
60 {
61 RtlZeroMemory(&gQueueKeyStateTable,0x100);
62 return STATUS_SUCCESS;
63 }
64
65 /*** Statics used by TranslateMessage ***/
66
67 /*** Shift state code was out of hand, sorry. --- arty */
68
69 static UINT DontDistinguishShifts( UINT ret )
70 {
71 if( ret == VK_LSHIFT || ret == VK_RSHIFT )
72 ret = VK_LSHIFT;
73 if( ret == VK_LCONTROL || ret == VK_RCONTROL )
74 ret = VK_LCONTROL;
75 if( ret == VK_LMENU || ret == VK_RMENU )
76 ret = VK_LMENU;
77 return ret;
78 }
79
80 static VOID APIENTRY SetKeyState(DWORD key, DWORD vk, DWORD ext, BOOL down)
81 {
82 ASSERT(vk <= 0xff);
83
84 /* Special handling for toggles like numpad and caps lock */
85 if (vk == VK_CAPITAL || vk == VK_NUMLOCK)
86 {
87 if (down)
88 gQueueKeyStateTable[vk] ^= KS_LOCK_BIT;
89 }
90
91 if (ext && vk == VK_LSHIFT)
92 vk = VK_RSHIFT;
93 if (ext && vk == VK_LCONTROL)
94 vk = VK_RCONTROL;
95 if (ext && vk == VK_LMENU)
96 vk = VK_RMENU;
97
98 if (down)
99 gQueueKeyStateTable[vk] |= KS_DOWN_BIT;
100 else
101 gQueueKeyStateTable[vk] &= ~KS_DOWN_MASK;
102
103 if (vk == VK_LSHIFT || vk == VK_RSHIFT)
104 {
105 if ((gQueueKeyStateTable[VK_LSHIFT] & KS_DOWN_BIT) ||
106 (gQueueKeyStateTable[VK_RSHIFT] & KS_DOWN_BIT))
107 {
108 gQueueKeyStateTable[VK_SHIFT] |= KS_DOWN_BIT;
109 }
110 else
111 {
112 gQueueKeyStateTable[VK_SHIFT] &= ~KS_DOWN_MASK;
113 }
114 }
115
116 if (vk == VK_LCONTROL || vk == VK_RCONTROL)
117 {
118 if ((gQueueKeyStateTable[VK_LCONTROL] & KS_DOWN_BIT) ||
119 (gQueueKeyStateTable[VK_RCONTROL] & KS_DOWN_BIT))
120 {
121 gQueueKeyStateTable[VK_CONTROL] |= KS_DOWN_BIT;
122 }
123 else
124 {
125 gQueueKeyStateTable[VK_CONTROL] &= ~KS_DOWN_MASK;
126 }
127 }
128
129 if (vk == VK_LMENU || vk == VK_RMENU)
130 {
131 if ((gQueueKeyStateTable[VK_LMENU] & KS_DOWN_BIT) ||
132 (gQueueKeyStateTable[VK_RMENU] & KS_DOWN_BIT))
133 {
134 gQueueKeyStateTable[VK_MENU] |= KS_DOWN_BIT;
135 }
136 else
137 {
138 gQueueKeyStateTable[VK_MENU] &= ~KS_DOWN_MASK;
139 }
140 }
141 }
142
143 VOID DumpKeyState( PBYTE KeyState )
144 {
145 int i;
146
147 DbgPrint( "KeyState { " );
148 for( i = 0; i < 0x100; i++ )
149 {
150 if( KeyState[i] )
151 DbgPrint( "%02x(%02x) ", i, KeyState[i] );
152 }
153 DbgPrint( "};\n" );
154 }
155
156 static BYTE KeysSet( PKBDTABLES pkKT, PBYTE KeyState,
157 int FakeModLeft, int FakeModRight )
158 {
159 if( !KeyState || !pkKT )
160 return 0;
161
162 /* Search special codes first */
163 if( FakeModLeft && KeyState[FakeModLeft] )
164 return KeyState[FakeModLeft];
165 else if( FakeModRight && KeyState[FakeModRight] )
166 return KeyState[FakeModRight];
167
168 return 0;
169 }
170
171 /* Search the keyboard layout modifiers table for the shift bit. I don't
172 * want to count on the shift bit not moving, because it can be specified
173 * in the layout */
174
175 static DWORD FASTCALL GetShiftBit( PKBDTABLES pkKT, DWORD Vk )
176 {
177 int i;
178
179 for( i = 0; pkKT->pCharModifiers->pVkToBit[i].Vk; i++ )
180 if( pkKT->pCharModifiers->pVkToBit[i].Vk == Vk )
181 return pkKT->pCharModifiers->pVkToBit[i].ModBits;
182
183 return 0;
184 }
185
186 static DWORD ModBits( PKBDTABLES pkKT, PBYTE KeyState )
187 {
188 DWORD ModBits = 0;
189
190 if( !KeyState )
191 return 0;
192
193 /* DumpKeyState( KeyState ); */
194
195 if (KeysSet( pkKT, KeyState, VK_LSHIFT, VK_RSHIFT ) &
196 KS_DOWN_BIT)
197 ModBits |= GetShiftBit( pkKT, VK_SHIFT );
198
199 if (KeysSet( pkKT, KeyState, VK_SHIFT, 0 ) &
200 KS_DOWN_BIT)
201 ModBits |= GetShiftBit( pkKT, VK_SHIFT );
202
203 if (KeysSet( pkKT, KeyState, VK_LCONTROL, VK_RCONTROL ) &
204 KS_DOWN_BIT )
205 ModBits |= GetShiftBit( pkKT, VK_CONTROL );
206
207 if (KeysSet( pkKT, KeyState, VK_CONTROL, 0 ) &
208 KS_DOWN_BIT )
209 ModBits |= GetShiftBit( pkKT, VK_CONTROL );
210
211 if (KeysSet( pkKT, KeyState, VK_LMENU, VK_RMENU ) &
212 KS_DOWN_BIT )
213 ModBits |= GetShiftBit( pkKT, VK_MENU );
214
215 /* Handle Alt+Gr */
216 if (pkKT->fLocalFlags & 0x1)
217 if (KeysSet( pkKT, KeyState, VK_RMENU, 0 ) &
218 KS_DOWN_BIT)
219 ModBits |= GetShiftBit( pkKT, VK_CONTROL );
220
221 /* Deal with VK_CAPITAL */
222 if (KeysSet( pkKT, KeyState, VK_CAPITAL, 0 ) & KS_LOCK_BIT)
223 {
224 ModBits |= CAPITAL_BIT;
225 }
226
227 /* Deal with VK_NUMLOCK */
228 if (KeysSet( pkKT, KeyState, VK_NUMLOCK, 0 ) & KS_LOCK_BIT)
229 {
230 ModBits |= NUMLOCK_BIT;
231 }
232
233 DPRINT( "Current Mod Bits: %x\n", ModBits );
234
235 return ModBits;
236 }
237
238 static BOOL TryToTranslateChar(WORD wVirtKey,
239 DWORD ModBits,
240 PBOOL pbDead,
241 PBOOL pbLigature,
242 PWCHAR pwcTranslatedChar,
243 PKBDTABLES keyLayout )
244 {
245 PVK_TO_WCHAR_TABLE vtwTbl;
246 PVK_TO_WCHARS10 vkPtr;
247 size_t size_this_entry;
248 int nMod;
249 DWORD CapsMod = 0, CapsState = 0;
250
251 CapsState = ModBits & ~MOD_BITS_MASK;
252 ModBits = ModBits & MOD_BITS_MASK;
253
254 DPRINT ( "TryToTranslate: %04x %x\n", wVirtKey, ModBits );
255
256 if (ModBits > keyLayout->pCharModifiers->wMaxModBits)
257 {
258 return FALSE;
259 }
260
261 for (nMod = 0; keyLayout->pVkToWcharTable[nMod].nModifications; nMod++)
262 {
263 vtwTbl = &keyLayout->pVkToWcharTable[nMod];
264 size_this_entry = vtwTbl->cbSize;
265 vkPtr = (PVK_TO_WCHARS10)((BYTE *)vtwTbl->pVkToWchars);
266 while(vkPtr->VirtualKey)
267 {
268 if( wVirtKey == (vkPtr->VirtualKey & 0xff) )
269 {
270 CapsMod = keyLayout->pCharModifiers->ModNumber
271 [ModBits ^
272 ((CapsState & CAPITAL_BIT) ? vkPtr->Attributes : 0)];
273
274 if( CapsMod >= keyLayout->pVkToWcharTable[nMod].nModifications )
275 {
276 return FALSE;
277 }
278
279 if( vkPtr->wch[CapsMod] == WCH_NONE )
280 {
281 return FALSE;
282 }
283
284 *pbDead = vkPtr->wch[CapsMod] == WCH_DEAD;
285 *pbLigature = vkPtr->wch[CapsMod] == WCH_LGTR;
286 *pwcTranslatedChar = vkPtr->wch[CapsMod];
287
288 DPRINT("%d %04x: CapsMod %08x CapsState %08x Char %04x\n",
289 nMod, wVirtKey,
290 CapsMod, CapsState, *pwcTranslatedChar);
291
292 if( *pbDead )
293 {
294 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
295 if( vkPtr->VirtualKey != 0xff )
296 {
297 DPRINT( "Found dead key with no trailer in the table.\n" );
298 DPRINT( "VK: %04x, ADDR: %p\n", wVirtKey, vkPtr );
299 return FALSE;
300 }
301 *pwcTranslatedChar = vkPtr->wch[CapsMod];
302 }
303 return TRUE;
304 }
305 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
306 }
307 }
308 return FALSE;
309 }
310
311 static
312 int APIENTRY
313 ToUnicodeInner(UINT wVirtKey,
314 UINT wScanCode,
315 PBYTE lpKeyState,
316 LPWSTR pwszBuff,
317 int cchBuff,
318 UINT wFlags,
319 PKBDTABLES pkKT)
320 {
321 WCHAR wcTranslatedChar;
322 BOOL bDead;
323 BOOL bLigature;
324
325 if( !pkKT )
326 return 0;
327
328 if( TryToTranslateChar( wVirtKey,
329 ModBits( pkKT, lpKeyState ),
330 &bDead,
331 &bLigature,
332 &wcTranslatedChar,
333 pkKT ) )
334 {
335 if( bLigature )
336 {
337 DPRINT("Not handling ligature (yet)\n" );
338 return 0;
339 }
340
341 if( cchBuff > 0 )
342 pwszBuff[0] = wcTranslatedChar;
343
344 return bDead ? -1 : 1;
345 }
346
347 return 0;
348 }
349
350
351 DWORD FASTCALL UserGetKeyState(DWORD key)
352 {
353 DWORD ret = 0;
354
355 if( key < 0x100 )
356 {
357 ret = ((DWORD)(gQueueKeyStateTable[key] & KS_DOWN_BIT) << 8 ) |
358 (gQueueKeyStateTable[key] & KS_LOCK_BIT);
359 }
360
361 return ret;
362 }
363
364
365 SHORT
366 APIENTRY
367 NtUserGetKeyState(
368 INT key)
369 {
370 DECLARE_RETURN(DWORD);
371
372 DPRINT("Enter NtUserGetKeyState\n");
373 UserEnterExclusive();
374
375 RETURN(UserGetKeyState(key));
376
377 CLEANUP:
378 DPRINT("Leave NtUserGetKeyState, ret=%i\n",_ret_);
379 UserLeave();
380 END_CLEANUP;
381 }
382
383
384
385 DWORD FASTCALL UserGetAsyncKeyState(DWORD key)
386 {
387 DWORD ret = 0;
388
389 if( key < 0x100 )
390 {
391 ret = ((DWORD)(gQueueKeyStateTable[key] & KS_DOWN_BIT) << 8 ) |
392 (gQueueKeyStateTable[key] & KS_LOCK_BIT);
393 }
394
395 return ret;
396 }
397
398
399
400 SHORT
401 APIENTRY
402 NtUserGetAsyncKeyState(
403 INT key)
404 {
405 DECLARE_RETURN(SHORT);
406
407 DPRINT("Enter NtUserGetAsyncKeyState\n");
408 UserEnterExclusive();
409
410 RETURN((SHORT)UserGetAsyncKeyState(key));
411
412 CLEANUP:
413 DPRINT("Leave NtUserGetAsyncKeyState, ret=%i\n",_ret_);
414 UserLeave();
415 END_CLEANUP;
416 }
417
418
419
420 BOOL FASTCALL
421 IntTranslateKbdMessage(LPMSG lpMsg,
422 HKL dwhkl)
423 {
424 PTHREADINFO pti;
425 static INT dead_char = 0;
426 LONG UState = 0;
427 WCHAR wp[2] = { 0 };
428 MSG NewMsg = { 0 };
429 PKBDTABLES keyLayout;
430 BOOL Result = FALSE;
431 DWORD ScanCode = 0;
432
433 pti = PsGetCurrentThreadWin32Thread();
434 keyLayout = pti->KeyboardLayout->KBTables;
435 if( !keyLayout )
436 return FALSE;
437
438 if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN)
439 return FALSE;
440
441 ScanCode = (lpMsg->lParam >> 16) & 0xff;
442
443 /* All messages have to contain the cursor point. */
444 IntGetCursorLocation(pti->Desktop->WindowStation,
445 &NewMsg.pt);
446
447 UState = ToUnicodeInner(lpMsg->wParam, HIWORD(lpMsg->lParam) & 0xff,
448 gQueueKeyStateTable, wp, 2, 0,
449 keyLayout );
450
451 if (UState == 1)
452 {
453 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
454 if (dead_char)
455 {
456 ULONG i;
457 WCHAR first, second;
458 DPRINT("PREVIOUS DEAD CHAR: %c\n", dead_char);
459
460 for( i = 0; keyLayout->pDeadKey[i].dwBoth; i++ )
461 {
462 first = keyLayout->pDeadKey[i].dwBoth >> 16;
463 second = keyLayout->pDeadKey[i].dwBoth;
464 if (first == dead_char && second == wp[0])
465 {
466 wp[0] = keyLayout->pDeadKey[i].wchComposed;
467 dead_char = 0;
468 break;
469 }
470 }
471
472 DPRINT("FINAL CHAR: %c\n", wp[0]);
473 }
474
475 if (dead_char)
476 {
477 NewMsg.hwnd = lpMsg->hwnd;
478 NewMsg.wParam = dead_char;
479 NewMsg.lParam = lpMsg->lParam;
480 dead_char = 0;
481 MsqPostMessage(pti->MessageQueue, &NewMsg, FALSE, QS_KEY);
482 }
483
484 NewMsg.hwnd = lpMsg->hwnd;
485 NewMsg.wParam = wp[0];
486 NewMsg.lParam = lpMsg->lParam;
487 DPRINT( "CHAR='%c' %04x %08x\n", wp[0], wp[0], lpMsg->lParam );
488 MsqPostMessage(pti->MessageQueue, &NewMsg, FALSE, QS_KEY);
489 Result = TRUE;
490 }
491 else if (UState == -1)
492 {
493 NewMsg.message =
494 (lpMsg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
495 NewMsg.hwnd = lpMsg->hwnd;
496 NewMsg.wParam = wp[0];
497 NewMsg.lParam = lpMsg->lParam;
498 dead_char = wp[0];
499 MsqPostMessage(pti->MessageQueue, &NewMsg, FALSE, QS_KEY);
500 Result = TRUE;
501 }
502
503 return Result;
504 }
505
506 DWORD
507 APIENTRY
508 NtUserGetKeyboardState(
509 LPBYTE lpKeyState)
510 {
511 BOOL Result = TRUE;
512 DECLARE_RETURN(DWORD);
513
514 DPRINT("Enter NtUserGetKeyboardState\n");
515 UserEnterShared();
516
517 if (lpKeyState)
518 {
519 if(!NT_SUCCESS(MmCopyToCaller(lpKeyState, gQueueKeyStateTable, 256)))
520 Result = FALSE;
521 }
522
523 RETURN(Result);
524
525 CLEANUP:
526 DPRINT("Leave NtUserGetKeyboardState, ret=%i\n",_ret_);
527 UserLeave();
528 END_CLEANUP;
529 }
530
531 BOOL
532 APIENTRY
533 NtUserSetKeyboardState(LPBYTE lpKeyState)
534 {
535 BOOL Result = TRUE;
536 DECLARE_RETURN(DWORD);
537
538 DPRINT("Enter NtUserSetKeyboardState\n");
539 UserEnterExclusive();
540
541 if (lpKeyState)
542 {
543 if(! NT_SUCCESS(MmCopyFromCaller(gQueueKeyStateTable, lpKeyState, 256)))
544 Result = FALSE;
545 }
546
547 RETURN(Result);
548
549 CLEANUP:
550 DPRINT("Leave NtUserSetKeyboardState, ret=%i\n",_ret_);
551 UserLeave();
552 END_CLEANUP;
553 }
554
555 static UINT VkToScan( UINT Code, BOOL ExtCode, PKBDTABLES pkKT )
556 {
557 int i;
558
559 for( i = 0; i < pkKT->bMaxVSCtoVK; i++ )
560 {
561 if( pkKT->pusVSCtoVK[i] == Code )
562 {
563 return i;
564 }
565 }
566
567 return 0;
568 }
569
570 UINT ScanToVk( UINT Code, BOOL ExtKey, PKBDTABLES pkKT )
571 {
572 if( !pkKT )
573 {
574 DPRINT("ScanToVk: No layout\n");
575 return 0;
576 }
577
578 if( ExtKey )
579 {
580 int i;
581
582 for( i = 0; pkKT->pVSCtoVK_E0[i].Vsc; i++ )
583 {
584 if( pkKT->pVSCtoVK_E0[i].Vsc == Code )
585 return pkKT->pVSCtoVK_E0[i].Vk & 0xff;
586 }
587 for( i = 0; pkKT->pVSCtoVK_E1[i].Vsc; i++ )
588 {
589 if( pkKT->pVSCtoVK_E1[i].Vsc == Code )
590 return pkKT->pVSCtoVK_E1[i].Vk & 0xff;
591 }
592
593 return 0;
594 }
595 else
596 {
597 if( Code >= pkKT->bMaxVSCtoVK )
598 {
599 return 0;
600 }
601 return pkKT->pusVSCtoVK[Code] & 0xff;
602 }
603 }
604
605 /*
606 * Map a virtual key code, or virtual scan code, to a scan code, key code,
607 * or unshifted unicode character.
608 *
609 * Code: See Below
610 * Type:
611 * 0 -- Code is a virtual key code that is converted into a virtual scan code
612 * that does not distinguish between left and right shift keys.
613 * 1 -- Code is a virtual scan code that is converted into a virtual key code
614 * that does not distinguish between left and right shift keys.
615 * 2 -- Code is a virtual key code that is converted into an unshifted unicode
616 * character.
617 * 3 -- Code is a virtual scan code that is converted into a virtual key code
618 * that distinguishes left and right shift keys.
619 * KeyLayout: Keyboard layout handle (currently, unused)
620 *
621 * @implemented
622 */
623
624 static UINT IntMapVirtualKeyEx( UINT Code, UINT Type, PKBDTABLES keyLayout )
625 {
626 UINT ret = 0;
627
628 switch( Type )
629 {
630 case 0:
631 if( Code == VK_RSHIFT )
632 Code = VK_LSHIFT;
633 if( Code == VK_RMENU )
634 Code = VK_LMENU;
635 if( Code == VK_RCONTROL )
636 Code = VK_LCONTROL;
637 ret = VkToScan( Code, FALSE, keyLayout );
638 break;
639
640 case 1:
641 ret =
642 DontDistinguishShifts
643 (IntMapVirtualKeyEx( Code, 3, keyLayout ) );
644 break;
645
646 case 2:
647 {
648 WCHAR wp[2] = {0};
649
650 ret = VkToScan( Code, FALSE, keyLayout );
651 ToUnicodeInner( Code, ret, 0, wp, 2, 0, keyLayout );
652 ret = wp[0];
653 }
654 break;
655
656 case 3:
657
658 ret = ScanToVk( Code, FALSE, keyLayout );
659 break;
660 }
661
662 return ret;
663 }
664
665 UINT
666 APIENTRY
667 NtUserMapVirtualKeyEx( UINT Code, UINT Type, DWORD keyboardId, HKL dwhkl )
668 {
669 PTHREADINFO pti;
670 PKBDTABLES keyLayout;
671 DECLARE_RETURN(UINT);
672
673 DPRINT("Enter NtUserMapVirtualKeyEx\n");
674 UserEnterExclusive();
675
676 pti = PsGetCurrentThreadWin32Thread();
677 keyLayout = pti ? pti->KeyboardLayout->KBTables : 0;
678
679 if( !keyLayout )
680 RETURN(0);
681
682 RETURN(IntMapVirtualKeyEx( Code, Type, keyLayout ));
683
684 CLEANUP:
685 DPRINT("Leave NtUserMapVirtualKeyEx, ret=%i\n",_ret_);
686 UserLeave();
687 END_CLEANUP;
688 }
689
690
691 int
692 APIENTRY
693 NtUserToUnicodeEx(
694 UINT wVirtKey,
695 UINT wScanCode,
696 PBYTE lpKeyState,
697 LPWSTR pwszBuff,
698 int cchBuff,
699 UINT wFlags,
700 HKL dwhkl )
701 {
702 PTHREADINFO pti;
703 BYTE KeyStateBuf[0x100];
704 PWCHAR OutPwszBuff = 0;
705 int ret = 0;
706 DECLARE_RETURN(int);
707
708 DPRINT("Enter NtUserSetKeyboardState\n");
709 UserEnterShared();//faxme: this syscall doesnt seem to need any locking...
710
711
712 if( !NT_SUCCESS(MmCopyFromCaller(KeyStateBuf,
713 lpKeyState,
714 sizeof(KeyStateBuf))) )
715 {
716 DPRINT1( "Couldn't copy key state from caller.\n" );
717 RETURN(0);
718 }
719
720 /* Virtual code is correct and key is pressed currently? */
721 if (wVirtKey < 0x100 && KeyStateBuf[wVirtKey] & KS_DOWN_BIT)
722 {
723 OutPwszBuff = ExAllocatePoolWithTag(NonPagedPool,sizeof(WCHAR) * cchBuff, TAG_STRING);
724 if( !OutPwszBuff )
725 {
726 DPRINT1( "ExAllocatePool(%d) failed\n", sizeof(WCHAR) * cchBuff);
727 RETURN(0);
728 }
729 RtlZeroMemory( OutPwszBuff, sizeof( WCHAR ) * cchBuff );
730
731 pti = PsGetCurrentThreadWin32Thread();
732 ret = ToUnicodeInner( wVirtKey,
733 wScanCode,
734 KeyStateBuf,
735 OutPwszBuff,
736 cchBuff,
737 wFlags,
738 pti ? pti->KeyboardLayout->KBTables : 0 );
739
740 MmCopyToCaller(pwszBuff,OutPwszBuff,sizeof(WCHAR)*cchBuff);
741 ExFreePoolWithTag(OutPwszBuff, TAG_STRING);
742 }
743 else
744 ret = 0;
745
746 RETURN(ret);
747
748 CLEANUP:
749 DPRINT("Leave NtUserSetKeyboardState, ret=%i\n",_ret_);
750 UserLeave();
751 END_CLEANUP;
752 }
753
754 static int W32kSimpleToupper( int ch )
755 {
756 if( ch >= 'a' && ch <= 'z' )
757 ch = ch - 'a' + 'A';
758 return ch;
759 }
760
761 DWORD
762 APIENTRY
763 NtUserGetKeyNameText( LONG lParam, LPWSTR lpString, int nSize )
764 {
765 PTHREADINFO pti;
766 int i;
767 DWORD ret = 0;
768 UINT CareVk = 0;
769 UINT VkCode = 0;
770 UINT ScanCode = (lParam >> 16) & 0xff;
771 BOOL ExtKey = lParam & (1<<24) ? TRUE : FALSE;
772 PKBDTABLES keyLayout;
773 VSC_LPWSTR *KeyNames;
774 DECLARE_RETURN(DWORD);
775
776 DPRINT("Enter NtUserGetKeyNameText\n");
777 UserEnterShared();
778
779 pti = PsGetCurrentThreadWin32Thread();
780 keyLayout = pti ? pti->KeyboardLayout->KBTables : 0;
781
782 if( !keyLayout || nSize < 1 )
783 RETURN(0);
784
785 if( lParam & (1<<25) )
786 {
787 CareVk = VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
788 if( VkCode == VK_LSHIFT || VkCode == VK_RSHIFT )
789 VkCode = VK_LSHIFT;
790 if( VkCode == VK_LCONTROL || VkCode == VK_RCONTROL )
791 VkCode = VK_LCONTROL;
792 if( VkCode == VK_LMENU || VkCode == VK_RMENU )
793 VkCode = VK_LMENU;
794 }
795 else
796 {
797 VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
798 }
799
800 KeyNames = 0;
801
802 if( CareVk != VkCode )
803 ScanCode = VkToScan( VkCode, ExtKey, keyLayout );
804
805 if( ExtKey )
806 KeyNames = keyLayout->pKeyNamesExt;
807 else
808 KeyNames = keyLayout->pKeyNames;
809
810 for( i = 0; KeyNames[i].pwsz; i++ )
811 {
812 if( KeyNames[i].vsc == ScanCode )
813 {
814 UINT StrLen = wcslen(KeyNames[i].pwsz);
815 UINT StrMax = StrLen > (nSize - 1) ? (nSize - 1) : StrLen;
816 WCHAR null_wc = 0;
817 if( NT_SUCCESS( MmCopyToCaller( lpString,
818 KeyNames[i].pwsz,
819 StrMax * sizeof(WCHAR) ) ) &&
820 NT_SUCCESS( MmCopyToCaller( lpString + StrMax,
821 &null_wc,
822 sizeof( WCHAR ) ) ) )
823 {
824 ret = StrMax;
825 break;
826 }
827 }
828 }
829
830 if( ret == 0 )
831 {
832 WCHAR UCName[2];
833
834 UCName[0] = W32kSimpleToupper(IntMapVirtualKeyEx( VkCode, 2, keyLayout ));
835 UCName[1] = 0;
836 ret = 1;
837
838 if( !NT_SUCCESS(MmCopyToCaller( lpString, UCName, 2 * sizeof(WCHAR) )) )
839 RETURN(0);
840 }
841
842 RETURN(ret);
843
844 CLEANUP:
845 DPRINT("Leave NtUserGetKeyNameText, ret=%i\n",_ret_);
846 UserLeave();
847 END_CLEANUP;
848 }
849
850 /*
851 * Filter this message according to the current key layout, setting wParam
852 * appropriately.
853 */
854
855 VOID FASTCALL
856 W32kKeyProcessMessage(LPMSG Msg,
857 PKBDTABLES KeyboardLayout,
858 BYTE Prefix)
859 {
860 DWORD ScanCode = 0, ModifierBits = 0;
861 DWORD i = 0;
862 DWORD BaseMapping = 0;
863 DWORD RawVk = 0;
864 static WORD NumpadConversion[][2] =
865 { { VK_DELETE, VK_DECIMAL },
866 { VK_INSERT, VK_NUMPAD0 },
867 { VK_END, VK_NUMPAD1 },
868 { VK_DOWN, VK_NUMPAD2 },
869 { VK_NEXT, VK_NUMPAD3 },
870 { VK_LEFT, VK_NUMPAD4 },
871 { VK_CLEAR, VK_NUMPAD5 },
872 { VK_RIGHT, VK_NUMPAD6 },
873 { VK_HOME, VK_NUMPAD7 },
874 { VK_UP, VK_NUMPAD8 },
875 { VK_PRIOR, VK_NUMPAD9 },
876 { 0,0 } };
877 PVSC_VK VscVkTable = NULL;
878
879 if( !KeyboardLayout || !Msg ||
880 (Msg->message != WM_KEYDOWN && Msg->message != WM_SYSKEYDOWN &&
881 Msg->message != WM_KEYUP && Msg->message != WM_SYSKEYUP) )
882 {
883 return;
884 }
885
886 /* arty -- handle numpad -- On real windows, the actual key produced
887 * by the messaging layer is different based on the state of numlock. */
888 ModifierBits = ModBits(KeyboardLayout,gQueueKeyStateTable);
889
890 /* Get the raw scan code, so we can look up whether the key is a numpad
891 * key
892 *
893 * Shift and the LP_EXT_BIT cancel. */
894 ScanCode = (Msg->lParam >> 16) & 0xff;
895 BaseMapping = Msg->wParam =
896 IntMapVirtualKeyEx( ScanCode, 1, KeyboardLayout );
897 if( Prefix == 0 )
898 {
899 if( ScanCode >= KeyboardLayout->bMaxVSCtoVK )
900 RawVk = 0xff;
901 else
902 RawVk = KeyboardLayout->pusVSCtoVK[ScanCode];
903 }
904 else
905 {
906 if( Prefix == 0xE0 )
907 {
908 /* ignore shift codes */
909 if( ScanCode == 0x2A || ScanCode == 0x36 )
910 {
911 return;
912 }
913 VscVkTable = KeyboardLayout->pVSCtoVK_E0;
914 }
915 else if( Prefix == 0xE1 )
916 {
917 VscVkTable = KeyboardLayout->pVSCtoVK_E1;
918 }
919
920 RawVk = 0xff;
921 while (VscVkTable->Vsc)
922 {
923 if( VscVkTable->Vsc == ScanCode )
924 {
925 RawVk = VscVkTable->Vk;
926 }
927 VscVkTable++;
928 }
929 }
930
931 if ((ModifierBits & NUMLOCK_BIT) &&
932 !(ModifierBits & GetShiftBit(KeyboardLayout, VK_SHIFT)) &&
933 (RawVk & KNUMP) &&
934 !(Msg->lParam & LP_EXT_BIT))
935 {
936 /* The key in question is a numpad key. Search for a translation. */
937 for (i = 0; NumpadConversion[i][0]; i++)
938 {
939 if ((BaseMapping & 0xff) == NumpadConversion[i][0]) /* RawVk? */
940 {
941 Msg->wParam = NumpadConversion[i][1];
942 break;
943 }
944 }
945 }
946
947 DPRINT("Key: [%04x -> %04x]\n", BaseMapping, Msg->wParam);
948
949 /* Now that we have the VK, we can set the keymap appropriately
950 * This is a better place for this code, as it's guaranteed to be
951 * run, unlike translate message. */
952 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN)
953 {
954 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
955 TRUE ); /* Strike key */
956 }
957 else if (Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
958 {
959 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
960 FALSE ); /* Release key */
961 }
962
963 /* We need to unset SYSKEYDOWN if the ALT key is an ALT+Gr */
964 if( gQueueKeyStateTable[VK_RMENU] & KS_DOWN_BIT )
965 {
966 if( Msg->message == WM_SYSKEYDOWN )
967 Msg->message = WM_KEYDOWN;
968 else
969 Msg->message = WM_KEYUP;
970 }
971
972 }
973
974
975
976 DWORD FASTCALL
977 UserGetKeyboardType(
978 DWORD TypeFlag)
979 {
980 switch(TypeFlag)
981 {
982 case 0: /* Keyboard type */
983 return 4; /* AT-101 */
984 case 1: /* Keyboard Subtype */
985 return 0; /* There are no defined subtypes */
986 case 2: /* Number of F-keys */
987 return 12; /* We're doing an 101 for now, so return 12 F-keys */
988 default:
989 DPRINT1("Unknown type!\n");
990 return 0; /* The book says 0 here, so 0 */
991 }
992 }
993
994
995 /*
996 Based on TryToTranslateChar, instead of processing VirtualKey match,
997 look for wChar match.
998 */
999 DWORD
1000 APIENTRY
1001 NtUserVkKeyScanEx(
1002 WCHAR wChar,
1003 HKL hKeyboardLayout,
1004 BOOL UsehKL ) // TRUE from KeyboardLayout, FALSE from pkbl = (THREADINFO)->KeyboardLayout
1005 {
1006 PKBDTABLES KeyLayout;
1007 PVK_TO_WCHAR_TABLE vtwTbl;
1008 PVK_TO_WCHARS10 vkPtr;
1009 size_t size_this_entry;
1010 int nMod;
1011 PKBL pkbl = NULL;
1012 DWORD CapsMod = 0, CapsState = 0, Ret = -1;
1013
1014 DPRINT("NtUserVkKeyScanEx() wChar %d, KbdLayout 0x%p\n", wChar, hKeyboardLayout);
1015 UserEnterShared();
1016
1017 if (UsehKL)
1018 {
1019 if ( !hKeyboardLayout || !(pkbl = UserHklToKbl(hKeyboardLayout)))
1020 goto Exit;
1021 }
1022 else // From VkKeyScanAW it is FALSE so KeyboardLayout is white noise.
1023 {
1024 pkbl = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->KeyboardLayout;
1025 }
1026
1027 KeyLayout = pkbl->KBTables;
1028
1029 for (nMod = 0; KeyLayout->pVkToWcharTable[nMod].nModifications; nMod++)
1030 {
1031 vtwTbl = &KeyLayout->pVkToWcharTable[nMod];
1032 size_this_entry = vtwTbl->cbSize;
1033 vkPtr = (PVK_TO_WCHARS10)((BYTE *)vtwTbl->pVkToWchars);
1034
1035 while(vkPtr->VirtualKey)
1036 {
1037 /*
1038 0x01 Shift key
1039 0x02 Ctrl key
1040 0x04 Alt key
1041 Should have only 8 valid possibilities. Including zero.
1042 */
1043 for(CapsState = 0; CapsState < vtwTbl->nModifications; CapsState++)
1044 {
1045 if(vkPtr->wch[CapsState] == wChar)
1046 {
1047 CapsMod = KeyLayout->pCharModifiers->ModNumber[CapsState];
1048 DPRINT("nMod %d wC %04x: CapsMod %08x CapsState %08x MaxModBits %08x\n",
1049 nMod, wChar, CapsMod, CapsState, KeyLayout->pCharModifiers->wMaxModBits);
1050 Ret = ((CapsMod << 8)|(vkPtr->VirtualKey & 0xff));
1051 goto Exit;
1052 }
1053 }
1054 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
1055 }
1056 }
1057 Exit:
1058 UserLeave();
1059 return Ret;
1060 }
1061
1062
1063 /* EOF */