Merge from branch ReactX to Trunk,
[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 STDCALL 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 (KeysSet( pkKT, KeyState, VK_RMENU, 0 ) &
217 KS_DOWN_BIT )
218 ModBits |= GetShiftBit( pkKT, VK_CONTROL );
219
220 /* Deal with VK_CAPITAL */
221 if (KeysSet( pkKT, KeyState, VK_CAPITAL, 0 ) & KS_LOCK_BIT)
222 {
223 ModBits |= CAPITAL_BIT;
224 }
225
226 /* Deal with VK_NUMLOCK */
227 if (KeysSet( pkKT, KeyState, VK_NUMLOCK, 0 ) & KS_LOCK_BIT)
228 {
229 ModBits |= NUMLOCK_BIT;
230 }
231
232 DPRINT( "Current Mod Bits: %x\n", ModBits );
233
234 return ModBits;
235 }
236
237 static BOOL TryToTranslateChar(WORD wVirtKey,
238 DWORD ModBits,
239 PBOOL pbDead,
240 PBOOL pbLigature,
241 PWCHAR pwcTranslatedChar,
242 PKBDTABLES keyLayout )
243 {
244 PVK_TO_WCHAR_TABLE vtwTbl;
245 PVK_TO_WCHARS10 vkPtr;
246 size_t size_this_entry;
247 int nMod;
248 DWORD CapsMod = 0, CapsState = 0;
249
250 CapsState = ModBits & ~MOD_BITS_MASK;
251 ModBits = ModBits & MOD_BITS_MASK;
252
253 DPRINT ( "TryToTranslate: %04x %x\n", wVirtKey, ModBits );
254
255 if (ModBits > keyLayout->pCharModifiers->wMaxModBits)
256 {
257 return FALSE;
258 }
259
260 for (nMod = 0; keyLayout->pVkToWcharTable[nMod].nModifications; nMod++)
261 {
262 vtwTbl = &keyLayout->pVkToWcharTable[nMod];
263 size_this_entry = vtwTbl->cbSize;
264 vkPtr = (PVK_TO_WCHARS10)((BYTE *)vtwTbl->pVkToWchars);
265 while(vkPtr->VirtualKey)
266 {
267 if( wVirtKey == (vkPtr->VirtualKey & 0xff) )
268 {
269 CapsMod = keyLayout->pCharModifiers->ModNumber
270 [ModBits ^
271 ((CapsState & CAPITAL_BIT) ? vkPtr->Attributes : 0)];
272
273 if( CapsMod >= keyLayout->pVkToWcharTable[nMod].nModifications )
274 {
275 return FALSE;
276 }
277
278 if( vkPtr->wch[CapsMod] == WCH_NONE )
279 {
280 return FALSE;
281 }
282
283 *pbDead = vkPtr->wch[CapsMod] == WCH_DEAD;
284 *pbLigature = vkPtr->wch[CapsMod] == WCH_LGTR;
285 *pwcTranslatedChar = vkPtr->wch[CapsMod];
286
287 DPRINT("%d %04x: CapsMod %08x CapsState %08x Char %04x\n",
288 nMod, wVirtKey,
289 CapsMod, CapsState, *pwcTranslatedChar);
290
291 if( *pbDead )
292 {
293 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
294 if( vkPtr->VirtualKey != 0xff )
295 {
296 DPRINT( "Found dead key with no trailer in the table.\n" );
297 DPRINT( "VK: %04x, ADDR: %08x\n", wVirtKey, (int)vkPtr );
298 return FALSE;
299 }
300 *pwcTranslatedChar = vkPtr->wch[CapsMod];
301 }
302 return TRUE;
303 }
304 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
305 }
306 }
307 return FALSE;
308 }
309
310 static
311 int STDCALL
312 ToUnicodeInner(UINT wVirtKey,
313 UINT wScanCode,
314 PBYTE lpKeyState,
315 LPWSTR pwszBuff,
316 int cchBuff,
317 UINT wFlags,
318 PKBDTABLES pkKT)
319 {
320 WCHAR wcTranslatedChar;
321 BOOL bDead;
322 BOOL bLigature;
323
324 if( !pkKT )
325 return 0;
326
327 if( TryToTranslateChar( wVirtKey,
328 ModBits( pkKT, lpKeyState ),
329 &bDead,
330 &bLigature,
331 &wcTranslatedChar,
332 pkKT ) )
333 {
334 if( bLigature )
335 {
336 DPRINT("Not handling ligature (yet)\n" );
337 return 0;
338 }
339
340 if( cchBuff > 0 )
341 pwszBuff[0] = wcTranslatedChar;
342
343 return bDead ? -1 : 1;
344 }
345
346 return 0;
347 }
348
349
350 DWORD FASTCALL UserGetKeyState(DWORD key)
351 {
352 DWORD ret = 0;
353
354 if( key < 0x100 )
355 {
356 ret = ((DWORD)(gQueueKeyStateTable[key] & KS_DOWN_BIT) << 8 ) |
357 (gQueueKeyStateTable[key] & KS_LOCK_BIT);
358 }
359
360 return ret;
361 }
362
363
364 DWORD
365 STDCALL
366 NtUserGetKeyState(
367 DWORD key)
368 {
369 DECLARE_RETURN(DWORD);
370
371 DPRINT("Enter NtUserGetKeyState\n");
372 UserEnterExclusive();
373
374 RETURN(UserGetKeyState(key));
375
376 CLEANUP:
377 DPRINT("Leave NtUserGetKeyState, ret=%i\n",_ret_);
378 UserLeave();
379 END_CLEANUP;
380 }
381
382
383
384 DWORD FASTCALL UserGetAsyncKeyState(DWORD key)
385 {
386 DWORD ret = 0;
387
388 if( key < 0x100 )
389 {
390 ret = ((DWORD)(gQueueKeyStateTable[key] & KS_DOWN_BIT) << 8 ) |
391 (gQueueKeyStateTable[key] & KS_LOCK_BIT);
392 }
393
394 return ret;
395 }
396
397
398
399 DWORD
400 STDCALL
401 NtUserGetAsyncKeyState(
402 DWORD key)
403 {
404 DECLARE_RETURN(DWORD);
405
406 DPRINT("Enter NtUserGetAsyncKeyState\n");
407 UserEnterExclusive();
408
409 RETURN(UserGetAsyncKeyState(key));
410
411 CLEANUP:
412 DPRINT("Leave NtUserGetAsyncKeyState, ret=%i\n",_ret_);
413 UserLeave();
414 END_CLEANUP;
415 }
416
417
418
419 BOOL FASTCALL
420 IntTranslateKbdMessage(LPMSG lpMsg,
421 HKL dwhkl)
422 {
423 static INT dead_char = 0;
424 LONG UState = 0;
425 WCHAR wp[2] = { 0 };
426 MSG NewMsg = { 0 };
427 PKBDTABLES keyLayout;
428 BOOL Result = FALSE;
429 DWORD ScanCode = 0;
430
431
432 keyLayout = PsGetCurrentThreadWin32Thread()->KeyboardLayout->KBTables;
433 if( !keyLayout )
434 return FALSE;
435
436 if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN)
437 return FALSE;
438
439 ScanCode = (lpMsg->lParam >> 16) & 0xff;
440
441 /* All messages have to contain the cursor point. */
442 IntGetCursorLocation(PsGetCurrentThreadWin32Thread()->Desktop->WindowStation,
443 &NewMsg.pt);
444
445 UState = ToUnicodeInner(lpMsg->wParam, HIWORD(lpMsg->lParam) & 0xff,
446 gQueueKeyStateTable, wp, 2, 0,
447 keyLayout );
448
449 if (UState == 1)
450 {
451 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
452 if (dead_char)
453 {
454 ULONG i;
455 WCHAR first, second;
456 DPRINT("PREVIOUS DEAD CHAR: %c\n", dead_char);
457
458 for( i = 0; keyLayout->pDeadKey[i].dwBoth; i++ )
459 {
460 first = keyLayout->pDeadKey[i].dwBoth >> 16;
461 second = keyLayout->pDeadKey[i].dwBoth;
462 if (first == dead_char && second == wp[0])
463 {
464 wp[0] = keyLayout->pDeadKey[i].wchComposed;
465 dead_char = 0;
466 break;
467 }
468 }
469
470 DPRINT("FINAL CHAR: %c\n", wp[0]);
471 }
472
473 if (dead_char)
474 {
475 NewMsg.hwnd = lpMsg->hwnd;
476 NewMsg.wParam = dead_char;
477 NewMsg.lParam = lpMsg->lParam;
478 dead_char = 0;
479 MsqPostMessage(PsGetCurrentThreadWin32Thread()->MessageQueue, &NewMsg, FALSE, QS_KEY);
480 }
481
482 NewMsg.hwnd = lpMsg->hwnd;
483 NewMsg.wParam = wp[0];
484 NewMsg.lParam = lpMsg->lParam;
485 DPRINT( "CHAR='%c' %04x %08x\n", wp[0], wp[0], lpMsg->lParam );
486 MsqPostMessage(PsGetCurrentThreadWin32Thread()->MessageQueue, &NewMsg, FALSE, QS_KEY);
487 Result = TRUE;
488 }
489 else if (UState == -1)
490 {
491 NewMsg.message =
492 (lpMsg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
493 NewMsg.hwnd = lpMsg->hwnd;
494 NewMsg.wParam = wp[0];
495 NewMsg.lParam = lpMsg->lParam;
496 dead_char = wp[0];
497 MsqPostMessage(PsGetCurrentThreadWin32Thread()->MessageQueue, &NewMsg, FALSE, QS_KEY);
498 Result = TRUE;
499 }
500
501 return Result;
502 }
503
504 DWORD
505 STDCALL
506 NtUserGetKeyboardState(
507 LPBYTE lpKeyState)
508 {
509 BOOL Result = TRUE;
510 DECLARE_RETURN(DWORD);
511
512 DPRINT("Enter NtUserGetKeyboardState\n");
513 UserEnterShared();
514
515 if (lpKeyState)
516 {
517 if(!NT_SUCCESS(MmCopyToCaller(lpKeyState, gQueueKeyStateTable, 256)))
518 Result = FALSE;
519 }
520
521 RETURN(Result);
522
523 CLEANUP:
524 DPRINT("Leave NtUserGetKeyboardState, ret=%i\n",_ret_);
525 UserLeave();
526 END_CLEANUP;
527 }
528
529 DWORD
530 STDCALL
531 NtUserSetKeyboardState(LPBYTE lpKeyState)
532 {
533 BOOL Result = TRUE;
534 DECLARE_RETURN(DWORD);
535
536 DPRINT("Enter NtUserSetKeyboardState\n");
537 UserEnterExclusive();
538
539 if (lpKeyState)
540 {
541 if(! NT_SUCCESS(MmCopyFromCaller(gQueueKeyStateTable, lpKeyState, 256)))
542 Result = FALSE;
543 }
544
545 RETURN(Result);
546
547 CLEANUP:
548 DPRINT("Leave NtUserSetKeyboardState, ret=%i\n",_ret_);
549 UserLeave();
550 END_CLEANUP;
551 }
552
553 static UINT VkToScan( UINT Code, BOOL ExtCode, PKBDTABLES pkKT )
554 {
555 int i;
556
557 for( i = 0; i < pkKT->bMaxVSCtoVK; i++ )
558 {
559 if( pkKT->pusVSCtoVK[i] == Code )
560 {
561 return i;
562 }
563 }
564
565 return 0;
566 }
567
568 UINT ScanToVk( UINT Code, BOOL ExtKey, PKBDTABLES pkKT )
569 {
570 if( !pkKT )
571 {
572 DPRINT("ScanToVk: No layout\n");
573 return 0;
574 }
575
576 if( ExtKey )
577 {
578 int i;
579
580 for( i = 0; pkKT->pVSCtoVK_E0[i].Vsc; i++ )
581 {
582 if( pkKT->pVSCtoVK_E0[i].Vsc == Code )
583 return pkKT->pVSCtoVK_E0[i].Vk & 0xff;
584 }
585 for( i = 0; pkKT->pVSCtoVK_E1[i].Vsc; i++ )
586 {
587 if( pkKT->pVSCtoVK_E1[i].Vsc == Code )
588 return pkKT->pVSCtoVK_E1[i].Vk & 0xff;
589 }
590
591 return 0;
592 }
593 else
594 {
595 if( Code >= pkKT->bMaxVSCtoVK )
596 {
597 return 0;
598 }
599 return pkKT->pusVSCtoVK[Code] & 0xff;
600 }
601 }
602
603 /*
604 * Map a virtual key code, or virtual scan code, to a scan code, key code,
605 * or unshifted unicode character.
606 *
607 * Code: See Below
608 * Type:
609 * 0 -- Code is a virtual key code that is converted into a virtual scan code
610 * that does not distinguish between left and right shift keys.
611 * 1 -- Code is a virtual scan code that is converted into a virtual key code
612 * that does not distinguish between left and right shift keys.
613 * 2 -- Code is a virtual key code that is converted into an unshifted unicode
614 * character.
615 * 3 -- Code is a virtual scan code that is converted into a virtual key code
616 * that distinguishes left and right shift keys.
617 * KeyLayout: Keyboard layout handle (currently, unused)
618 *
619 * @implemented
620 */
621
622 static UINT IntMapVirtualKeyEx( UINT Code, UINT Type, PKBDTABLES keyLayout )
623 {
624 UINT ret = 0;
625
626 switch( Type )
627 {
628 case 0:
629 if( Code == VK_RSHIFT )
630 Code = VK_LSHIFT;
631 if( Code == VK_RMENU )
632 Code = VK_LMENU;
633 if( Code == VK_RCONTROL )
634 Code = VK_LCONTROL;
635 ret = VkToScan( Code, FALSE, keyLayout );
636 break;
637
638 case 1:
639 ret =
640 DontDistinguishShifts
641 (IntMapVirtualKeyEx( Code, 3, keyLayout ) );
642 break;
643
644 case 2:
645 {
646 WCHAR wp[2] = {0};
647
648 ret = VkToScan( Code, FALSE, keyLayout );
649 ToUnicodeInner( Code, ret, 0, wp, 2, 0, keyLayout );
650 ret = wp[0];
651 }
652 break;
653
654 case 3:
655
656 ret = ScanToVk( Code, FALSE, keyLayout );
657 break;
658 }
659
660 return ret;
661 }
662
663 UINT
664 STDCALL
665 NtUserMapVirtualKeyEx( UINT Code, UINT Type, DWORD keyboardId, HKL dwhkl )
666 {
667 PKBDTABLES keyLayout;
668 DECLARE_RETURN(UINT);
669
670 DPRINT("Enter NtUserMapVirtualKeyEx\n");
671 UserEnterExclusive();
672
673 keyLayout = PsGetCurrentThreadWin32Thread() ? PsGetCurrentThreadWin32Thread()->KeyboardLayout->KBTables : 0;
674
675 if( !keyLayout )
676 RETURN(0);
677
678 RETURN(IntMapVirtualKeyEx( Code, Type, keyLayout ));
679
680 CLEANUP:
681 DPRINT("Leave NtUserMapVirtualKeyEx, ret=%i\n",_ret_);
682 UserLeave();
683 END_CLEANUP;
684 }
685
686
687 int
688 STDCALL
689 NtUserToUnicodeEx(
690 UINT wVirtKey,
691 UINT wScanCode,
692 PBYTE lpKeyState,
693 LPWSTR pwszBuff,
694 int cchBuff,
695 UINT wFlags,
696 HKL dwhkl )
697 {
698 BYTE KeyStateBuf[0x100];
699 PWCHAR OutPwszBuff = 0;
700 int ret = 0;
701 DECLARE_RETURN(int);
702
703 DPRINT("Enter NtUserSetKeyboardState\n");
704 UserEnterShared();//faxme: this syscall doesnt seem to need any locking...
705
706
707 if( !NT_SUCCESS(MmCopyFromCaller(KeyStateBuf,
708 lpKeyState,
709 sizeof(KeyStateBuf))) )
710 {
711 DPRINT1( "Couldn't copy key state from caller.\n" );
712 RETURN(0);
713 }
714 OutPwszBuff = ExAllocatePoolWithTag(NonPagedPool,sizeof(WCHAR) * cchBuff, TAG_STRING);
715 if( !OutPwszBuff )
716 {
717 DPRINT1( "ExAllocatePool(%d) failed\n", sizeof(WCHAR) * cchBuff);
718 RETURN(0);
719 }
720 RtlZeroMemory( OutPwszBuff, sizeof( WCHAR ) * cchBuff );
721
722 ret = ToUnicodeInner( wVirtKey,
723 wScanCode,
724 KeyStateBuf,
725 OutPwszBuff,
726 cchBuff,
727 wFlags,
728 PsGetCurrentThreadWin32Thread() ?
729 PsGetCurrentThreadWin32Thread()->KeyboardLayout->KBTables : 0 );
730
731 MmCopyToCaller(pwszBuff,OutPwszBuff,sizeof(WCHAR)*cchBuff);
732 ExFreePool(OutPwszBuff);
733
734 RETURN(ret);
735
736 CLEANUP:
737 DPRINT("Leave NtUserSetKeyboardState, ret=%i\n",_ret_);
738 UserLeave();
739 END_CLEANUP;
740 }
741
742 static int W32kSimpleToupper( int ch )
743 {
744 if( ch >= 'a' && ch <= 'z' )
745 ch = ch - 'a' + 'A';
746 return ch;
747 }
748
749 DWORD
750 STDCALL
751 NtUserGetKeyNameText( LONG lParam, LPWSTR lpString, int nSize )
752 {
753 int i;
754 DWORD ret = 0;
755 UINT CareVk = 0;
756 UINT VkCode = 0;
757 UINT ScanCode = (lParam >> 16) & 0xff;
758 BOOL ExtKey = lParam & (1<<24) ? TRUE : FALSE;
759 PKBDTABLES keyLayout;
760 VSC_LPWSTR *KeyNames;
761 DECLARE_RETURN(DWORD);
762
763 DPRINT("Enter NtUserGetKeyNameText\n");
764 UserEnterShared();
765
766 keyLayout = PsGetCurrentThreadWin32Thread() ?
767 PsGetCurrentThreadWin32Thread()->KeyboardLayout->KBTables : 0;
768
769 if( !keyLayout || nSize < 1 )
770 RETURN(0);
771
772 if( lParam & (1<<25) )
773 {
774 CareVk = VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
775 if( VkCode == VK_LSHIFT || VkCode == VK_RSHIFT )
776 VkCode = VK_LSHIFT;
777 if( VkCode == VK_LCONTROL || VkCode == VK_RCONTROL )
778 VkCode = VK_LCONTROL;
779 if( VkCode == VK_LMENU || VkCode == VK_RMENU )
780 VkCode = VK_LMENU;
781 }
782 else
783 {
784 VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
785 }
786
787 KeyNames = 0;
788
789 if( CareVk != VkCode )
790 ScanCode = VkToScan( VkCode, ExtKey, keyLayout );
791
792 if( ExtKey )
793 KeyNames = keyLayout->pKeyNamesExt;
794 else
795 KeyNames = keyLayout->pKeyNames;
796
797 for( i = 0; KeyNames[i].pwsz; i++ )
798 {
799 if( KeyNames[i].vsc == ScanCode )
800 {
801 UINT StrLen = wcslen(KeyNames[i].pwsz);
802 UINT StrMax = StrLen > (nSize - 1) ? (nSize - 1) : StrLen;
803 WCHAR null_wc = 0;
804 if( NT_SUCCESS( MmCopyToCaller( lpString,
805 KeyNames[i].pwsz,
806 StrMax * sizeof(WCHAR) ) ) &&
807 NT_SUCCESS( MmCopyToCaller( lpString + StrMax,
808 &null_wc,
809 sizeof( WCHAR ) ) ) )
810 {
811 ret = StrMax;
812 break;
813 }
814 }
815 }
816
817 if( ret == 0 )
818 {
819 WCHAR UCName[2];
820
821 UCName[0] = W32kSimpleToupper(IntMapVirtualKeyEx( VkCode, 2, keyLayout ));
822 UCName[1] = 0;
823 ret = 1;
824
825 if( !NT_SUCCESS(MmCopyToCaller( lpString, UCName, 2 * sizeof(WCHAR) )) )
826 RETURN(0);
827 }
828
829 RETURN(ret);
830
831 CLEANUP:
832 DPRINT("Leave NtUserGetKeyNameText, ret=%i\n",_ret_);
833 UserLeave();
834 END_CLEANUP;
835 }
836
837 /*
838 * Filter this message according to the current key layout, setting wParam
839 * appropriately.
840 */
841
842 VOID FASTCALL
843 W32kKeyProcessMessage(LPMSG Msg,
844 PKBDTABLES KeyboardLayout,
845 BYTE Prefix)
846 {
847 DWORD ScanCode = 0, ModifierBits = 0;
848 DWORD i = 0;
849 DWORD BaseMapping = 0;
850 DWORD RawVk = 0;
851 static WORD NumpadConversion[][2] =
852 { { VK_DELETE, VK_DECIMAL },
853 { VK_INSERT, VK_NUMPAD0 },
854 { VK_END, VK_NUMPAD1 },
855 { VK_DOWN, VK_NUMPAD2 },
856 { VK_NEXT, VK_NUMPAD3 },
857 { VK_LEFT, VK_NUMPAD4 },
858 { VK_CLEAR, VK_NUMPAD5 },
859 { VK_RIGHT, VK_NUMPAD6 },
860 { VK_HOME, VK_NUMPAD7 },
861 { VK_UP, VK_NUMPAD8 },
862 { VK_PRIOR, VK_NUMPAD9 },
863 { 0,0 } };
864 PVSC_VK VscVkTable = NULL;
865
866 if( !KeyboardLayout || !Msg ||
867 (Msg->message != WM_KEYDOWN && Msg->message != WM_SYSKEYDOWN &&
868 Msg->message != WM_KEYUP && Msg->message != WM_SYSKEYUP) )
869 {
870 return;
871 }
872
873 /* arty -- handle numpad -- On real windows, the actual key produced
874 * by the messaging layer is different based on the state of numlock. */
875 ModifierBits = ModBits(KeyboardLayout,gQueueKeyStateTable);
876
877 /* Get the raw scan code, so we can look up whether the key is a numpad
878 * key
879 *
880 * Shift and the LP_EXT_BIT cancel. */
881 ScanCode = (Msg->lParam >> 16) & 0xff;
882 BaseMapping = Msg->wParam =
883 IntMapVirtualKeyEx( ScanCode, 1, KeyboardLayout );
884 if( Prefix == 0 )
885 {
886 if( ScanCode >= KeyboardLayout->bMaxVSCtoVK )
887 RawVk = 0xff;
888 else
889 RawVk = KeyboardLayout->pusVSCtoVK[ScanCode];
890 }
891 else
892 {
893 if( Prefix == 0xE0 )
894 {
895 /* ignore shift codes */
896 if( ScanCode == 0x2A || ScanCode == 0x36 )
897 {
898 return;
899 }
900 VscVkTable = KeyboardLayout->pVSCtoVK_E0;
901 }
902 else if( Prefix == 0xE1 )
903 {
904 VscVkTable = KeyboardLayout->pVSCtoVK_E1;
905 }
906
907 RawVk = 0xff;
908 while (VscVkTable->Vsc)
909 {
910 if( VscVkTable->Vsc == ScanCode )
911 {
912 RawVk = VscVkTable->Vk;
913 }
914 VscVkTable++;
915 }
916 }
917
918 if ((ModifierBits & NUMLOCK_BIT) &&
919 !(ModifierBits & GetShiftBit(KeyboardLayout, VK_SHIFT)) &&
920 (RawVk & KNUMP) &&
921 !(Msg->lParam & LP_EXT_BIT))
922 {
923 /* The key in question is a numpad key. Search for a translation. */
924 for (i = 0; NumpadConversion[i][0]; i++)
925 {
926 if ((BaseMapping & 0xff) == NumpadConversion[i][0]) /* RawVk? */
927 {
928 Msg->wParam = NumpadConversion[i][1];
929 break;
930 }
931 }
932 }
933
934 DPRINT("Key: [%04x -> %04x]\n", BaseMapping, Msg->wParam);
935
936 /* Now that we have the VK, we can set the keymap appropriately
937 * This is a better place for this code, as it's guaranteed to be
938 * run, unlike translate message. */
939 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN)
940 {
941 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
942 TRUE ); /* Strike key */
943 }
944 else if (Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
945 {
946 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
947 FALSE ); /* Release key */
948 }
949
950 /* We need to unset SYSKEYDOWN if the ALT key is an ALT+Gr */
951 if( gQueueKeyStateTable[VK_RMENU] & KS_DOWN_BIT )
952 {
953 if( Msg->message == WM_SYSKEYDOWN )
954 Msg->message = WM_KEYDOWN;
955 else
956 Msg->message = WM_KEYUP;
957 }
958
959 }
960
961
962
963 DWORD FASTCALL
964 UserGetKeyboardType(
965 DWORD TypeFlag)
966 {
967 switch(TypeFlag)
968 {
969 case 0: /* Keyboard type */
970 return 4; /* AT-101 */
971 case 1: /* Keyboard Subtype */
972 return 0; /* There are no defined subtypes */
973 case 2: /* Number of F-keys */
974 return 12; /* We're doing an 101 for now, so return 12 F-keys */
975 default:
976 DPRINT1("Unknown type!\n");
977 return 0; /* The book says 0 here, so 0 */
978 }
979 }
980
981
982 /*
983 Based on TryToTranslateChar, instead of processing VirtualKey match,
984 look for wChar match.
985 */
986 DWORD
987 STDCALL
988 NtUserVkKeyScanEx(
989 WCHAR wChar,
990 HKL KeyboardLayout,
991 DWORD Unknown2)
992 {
993 /* FIXME: currently, this routine doesnt seem to need any locking */
994 PKBDTABLES KeyLayout;
995 PVK_TO_WCHAR_TABLE vtwTbl;
996 PVK_TO_WCHARS10 vkPtr;
997 size_t size_this_entry;
998 int nMod;
999 DWORD CapsMod = 0, CapsState = 0;
1000
1001 DPRINT("NtUserVkKeyScanEx() wChar %d, KbdLayout 0x%p\n", wChar, KeyboardLayout);
1002
1003 if(!KeyboardLayout)
1004 return -1;
1005 KeyLayout = UserHklToKbl(KeyboardLayout)->KBTables;
1006
1007 for (nMod = 0; KeyLayout->pVkToWcharTable[nMod].nModifications; nMod++)
1008 {
1009 vtwTbl = &KeyLayout->pVkToWcharTable[nMod];
1010 size_this_entry = vtwTbl->cbSize;
1011 vkPtr = (PVK_TO_WCHARS10)((BYTE *)vtwTbl->pVkToWchars);
1012
1013 while(vkPtr->VirtualKey)
1014 {
1015 /*
1016 0x01 Shift key
1017 0x02 Ctrl key
1018 0x04 Alt key
1019 Should have only 8 valid possibilities. Including zero.
1020 */
1021 for(CapsState = 0; CapsState < vtwTbl->nModifications; CapsState++)
1022 {
1023 if(vkPtr->wch[CapsState] == wChar)
1024 {
1025 CapsMod = KeyLayout->pCharModifiers->ModNumber[CapsState];
1026 DPRINT("nMod %d wC %04x: CapsMod %08x CapsState %08x MaxModBits %08x\n",
1027 nMod, wChar, CapsMod, CapsState, KeyLayout->pCharModifiers->wMaxModBits);
1028 return ((CapsMod << 8)|(vkPtr->VirtualKey & 0xff));
1029 }
1030 }
1031 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
1032 }
1033 }
1034 return -1;
1035 }
1036
1037
1038 /* EOF */