Merge aicom-network-branch (without NDIS changes for now)
[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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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_SHIFT;
73 if( ret == VK_LCONTROL || ret == VK_RCONTROL )
74 ret = VK_CONTROL;
75 if( ret == VK_LMENU || ret == VK_RMENU )
76 ret = VK_MENU;
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_KEYFIRST || lpMsg->message > WM_KEYLAST)
439 return FALSE;
440 if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN)
441 return FALSE;
442
443 /* All messages have to contain the cursor point. */
444 NewMsg.pt = gpsi->ptCursor;
445
446 switch (lpMsg->wParam)
447 {
448 case VK_PACKET:
449 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
450 NewMsg.hwnd = lpMsg->hwnd;
451 NewMsg.wParam = HIWORD(lpMsg->lParam);
452 NewMsg.lParam = LOWORD(lpMsg->lParam);
453 MsqPostMessage(pti->MessageQueue, &NewMsg, FALSE, QS_KEY);
454 return TRUE;
455 }
456
457 ScanCode = (lpMsg->lParam >> 16) & 0xff;
458
459 UState = ToUnicodeInner(lpMsg->wParam, HIWORD(lpMsg->lParam) & 0xff,
460 gQueueKeyStateTable, wp, 2, 0,
461 keyLayout );
462
463 if (UState == 1)
464 {
465 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
466 if (dead_char)
467 {
468 ULONG i;
469 WCHAR first, second;
470 DPRINT("PREVIOUS DEAD CHAR: %c\n", dead_char);
471
472 for( i = 0; keyLayout->pDeadKey[i].dwBoth; i++ )
473 {
474 first = keyLayout->pDeadKey[i].dwBoth >> 16;
475 second = keyLayout->pDeadKey[i].dwBoth;
476 if (first == dead_char && second == wp[0])
477 {
478 wp[0] = keyLayout->pDeadKey[i].wchComposed;
479 dead_char = 0;
480 break;
481 }
482 }
483
484 DPRINT("FINAL CHAR: %c\n", wp[0]);
485 }
486
487 if (dead_char)
488 {
489 NewMsg.hwnd = lpMsg->hwnd;
490 NewMsg.wParam = dead_char;
491 NewMsg.lParam = lpMsg->lParam;
492 dead_char = 0;
493 MsqPostMessage(pti->MessageQueue, &NewMsg, FALSE, QS_KEY);
494 }
495
496 NewMsg.hwnd = lpMsg->hwnd;
497 NewMsg.wParam = wp[0];
498 NewMsg.lParam = lpMsg->lParam;
499 DPRINT( "CHAR='%c' %04x %08x\n", wp[0], wp[0], lpMsg->lParam );
500 MsqPostMessage(pti->MessageQueue, &NewMsg, FALSE, QS_KEY);
501 Result = TRUE;
502 }
503 else if (UState == -1)
504 {
505 NewMsg.message =
506 (lpMsg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
507 NewMsg.hwnd = lpMsg->hwnd;
508 NewMsg.wParam = wp[0];
509 NewMsg.lParam = lpMsg->lParam;
510 dead_char = wp[0];
511 MsqPostMessage(pti->MessageQueue, &NewMsg, FALSE, QS_KEY);
512 Result = TRUE;
513 }
514
515 return Result;
516 }
517
518 DWORD
519 APIENTRY
520 NtUserGetKeyboardState(
521 LPBYTE lpKeyState)
522 {
523 BOOL Result = TRUE;
524 DECLARE_RETURN(DWORD);
525
526 DPRINT("Enter NtUserGetKeyboardState\n");
527 UserEnterShared();
528
529 if (lpKeyState)
530 {
531 if(!NT_SUCCESS(MmCopyToCaller(lpKeyState, gQueueKeyStateTable, 256)))
532 Result = FALSE;
533 }
534
535 RETURN(Result);
536
537 CLEANUP:
538 DPRINT("Leave NtUserGetKeyboardState, ret=%i\n",_ret_);
539 UserLeave();
540 END_CLEANUP;
541 }
542
543 BOOL
544 APIENTRY
545 NtUserSetKeyboardState(LPBYTE lpKeyState)
546 {
547 BOOL Result = TRUE;
548 DECLARE_RETURN(DWORD);
549
550 DPRINT("Enter NtUserSetKeyboardState\n");
551 UserEnterExclusive();
552
553 if (lpKeyState)
554 {
555 if(! NT_SUCCESS(MmCopyFromCaller(gQueueKeyStateTable, lpKeyState, 256)))
556 Result = FALSE;
557 }
558
559 RETURN(Result);
560
561 CLEANUP:
562 DPRINT("Leave NtUserSetKeyboardState, ret=%i\n",_ret_);
563 UserLeave();
564 END_CLEANUP;
565 }
566
567 static UINT VkToScan( UINT Code, BOOL ExtCode, PKBDTABLES pkKT )
568 {
569 int i;
570
571 for( i = 0; i < pkKT->bMaxVSCtoVK; i++ )
572 {
573 if( pkKT->pusVSCtoVK[i] == Code )
574 {
575 return i;
576 }
577 }
578
579 return 0;
580 }
581
582 UINT ScanToVk( UINT Code, BOOL ExtKey, PKBDTABLES pkKT )
583 {
584 if( !pkKT )
585 {
586 DPRINT("ScanToVk: No layout\n");
587 return 0;
588 }
589
590 if( ExtKey )
591 {
592 int i;
593
594 for( i = 0; pkKT->pVSCtoVK_E0[i].Vsc; i++ )
595 {
596 if( pkKT->pVSCtoVK_E0[i].Vsc == Code )
597 return pkKT->pVSCtoVK_E0[i].Vk & 0xff;
598 }
599 for( i = 0; pkKT->pVSCtoVK_E1[i].Vsc; i++ )
600 {
601 if( pkKT->pVSCtoVK_E1[i].Vsc == Code )
602 return pkKT->pVSCtoVK_E1[i].Vk & 0xff;
603 }
604
605 return 0;
606 }
607 else
608 {
609 if( Code >= pkKT->bMaxVSCtoVK )
610 {
611 return 0;
612 }
613 return pkKT->pusVSCtoVK[Code] & 0xff;
614 }
615 }
616
617 /*
618 * Map a virtual key code, or virtual scan code, to a scan code, key code,
619 * or unshifted unicode character.
620 *
621 * Code: See Below
622 * Type:
623 * 0 -- Code is a virtual key code that is converted into a virtual scan code
624 * that does not distinguish between left and right shift keys.
625 * 1 -- Code is a virtual scan code that is converted into a virtual key code
626 * that does not distinguish between left and right shift keys.
627 * 2 -- Code is a virtual key code that is converted into an unshifted unicode
628 * character.
629 * 3 -- Code is a virtual scan code that is converted into a virtual key code
630 * that distinguishes left and right shift keys.
631 * KeyLayout: Keyboard layout handle (currently, unused)
632 *
633 * @implemented
634 */
635
636 static UINT IntMapVirtualKeyEx( UINT Code, UINT Type, PKBDTABLES keyLayout )
637 {
638 UINT ret = 0;
639
640 switch( Type )
641 {
642 case 0:
643 if( Code == VK_SHIFT )
644 Code = VK_LSHIFT;
645 if( Code == VK_MENU )
646 Code = VK_LMENU;
647 if( Code == VK_CONTROL )
648 Code = VK_LCONTROL;
649 ret = VkToScan( Code, FALSE, keyLayout );
650 break;
651
652 case 1:
653 ret =
654 DontDistinguishShifts
655 (IntMapVirtualKeyEx( Code, 3, keyLayout ) );
656 break;
657
658 case 2:
659 {
660 WCHAR wp[2] = {0};
661
662 ret = VkToScan( Code, FALSE, keyLayout );
663 ToUnicodeInner( Code, ret, 0, wp, 2, 0, keyLayout );
664 ret = wp[0];
665 }
666 break;
667
668 case 3:
669
670 ret = ScanToVk( Code, FALSE, keyLayout );
671 break;
672 }
673
674 return ret;
675 }
676
677 UINT
678 APIENTRY
679 NtUserMapVirtualKeyEx( UINT Code, UINT Type, DWORD keyboardId, HKL dwhkl )
680 {
681 PTHREADINFO pti;
682 PKBDTABLES keyLayout;
683 DECLARE_RETURN(UINT);
684
685 DPRINT("Enter NtUserMapVirtualKeyEx\n");
686 UserEnterExclusive();
687
688 pti = PsGetCurrentThreadWin32Thread();
689 keyLayout = pti ? pti->KeyboardLayout->KBTables : 0;
690
691 if( !keyLayout )
692 RETURN(0);
693
694 RETURN(IntMapVirtualKeyEx( Code, Type, keyLayout ));
695
696 CLEANUP:
697 DPRINT("Leave NtUserMapVirtualKeyEx, ret=%i\n",_ret_);
698 UserLeave();
699 END_CLEANUP;
700 }
701
702
703 int
704 APIENTRY
705 NtUserToUnicodeEx(
706 UINT wVirtKey,
707 UINT wScanCode,
708 PBYTE lpKeyState,
709 LPWSTR pwszBuff,
710 int cchBuff,
711 UINT wFlags,
712 HKL dwhkl )
713 {
714 PTHREADINFO pti;
715 BYTE KeyStateBuf[0x100];
716 PWCHAR OutPwszBuff = 0;
717 int ret = 0;
718 DECLARE_RETURN(int);
719
720 DPRINT("Enter NtUserSetKeyboardState\n");
721 UserEnterShared();//fixme: this syscall doesnt seem to need any locking...
722
723
724 if( !NT_SUCCESS(MmCopyFromCaller(KeyStateBuf,
725 lpKeyState,
726 sizeof(KeyStateBuf))) )
727 {
728 DPRINT1( "Couldn't copy key state from caller.\n" );
729 RETURN(0);
730 }
731
732 /* Virtual code is correct and key is pressed currently? */
733 if (wVirtKey < 0x100 && KeyStateBuf[wVirtKey] & KS_DOWN_BIT)
734 {
735 OutPwszBuff = ExAllocatePoolWithTag(NonPagedPool,sizeof(WCHAR) * cchBuff, TAG_STRING);
736 if( !OutPwszBuff )
737 {
738 DPRINT1( "ExAllocatePool(%d) failed\n", sizeof(WCHAR) * cchBuff);
739 RETURN(0);
740 }
741 RtlZeroMemory( OutPwszBuff, sizeof( WCHAR ) * cchBuff );
742
743 pti = PsGetCurrentThreadWin32Thread();
744 ret = ToUnicodeInner( wVirtKey,
745 wScanCode,
746 KeyStateBuf,
747 OutPwszBuff,
748 cchBuff,
749 wFlags,
750 pti ? pti->KeyboardLayout->KBTables : 0 );
751
752 MmCopyToCaller(pwszBuff,OutPwszBuff,sizeof(WCHAR)*cchBuff);
753 ExFreePoolWithTag(OutPwszBuff, TAG_STRING);
754 }
755 else
756 ret = 0;
757
758 RETURN(ret);
759
760 CLEANUP:
761 DPRINT("Leave NtUserSetKeyboardState, ret=%i\n",_ret_);
762 UserLeave();
763 END_CLEANUP;
764 }
765
766 static int W32kSimpleToupper( int ch )
767 {
768 if( ch >= 'a' && ch <= 'z' )
769 ch = ch - 'a' + 'A';
770 return ch;
771 }
772
773 DWORD
774 APIENTRY
775 NtUserGetKeyNameText( LONG lParam, LPWSTR lpString, int nSize )
776 {
777 PTHREADINFO pti;
778 int i;
779 DWORD ret = 0;
780 UINT CareVk = 0;
781 UINT VkCode = 0;
782 UINT ScanCode = (lParam >> 16) & 0xff;
783 BOOL ExtKey = lParam & (1<<24) ? TRUE : FALSE;
784 PKBDTABLES keyLayout;
785 VSC_LPWSTR *KeyNames;
786 DECLARE_RETURN(DWORD);
787
788 DPRINT("Enter NtUserGetKeyNameText\n");
789 UserEnterShared();
790
791 pti = PsGetCurrentThreadWin32Thread();
792 keyLayout = pti ? pti->KeyboardLayout->KBTables : 0;
793
794 if( !keyLayout || nSize < 1 )
795 RETURN(0);
796
797 if( lParam & (1<<25) )
798 {
799 CareVk = VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
800 switch (VkCode)
801 {
802 case VK_RSHIFT:
803 ScanCode |= 0x100;
804 case VK_LSHIFT:
805 VkCode = VK_SHIFT;
806 break;
807 case VK_LCONTROL:
808 case VK_RCONTROL:
809 VkCode = VK_CONTROL;
810 break;
811 case VK_LMENU:
812 case VK_RMENU:
813 VkCode = VK_MENU;
814 break;
815 }
816 }
817 else
818 {
819 VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
820 }
821
822 KeyNames = 0;
823
824 if( CareVk != VkCode )
825 ScanCode = VkToScan( VkCode, ExtKey, keyLayout );
826
827 if( ExtKey )
828 KeyNames = keyLayout->pKeyNamesExt;
829 else
830 KeyNames = keyLayout->pKeyNames;
831
832 for( i = 0; KeyNames[i].pwsz; i++ )
833 {
834 if( KeyNames[i].vsc == ScanCode )
835 {
836 UINT StrLen = wcslen(KeyNames[i].pwsz);
837 UINT StrMax = StrLen > (nSize - 1) ? (nSize - 1) : StrLen;
838 WCHAR null_wc = 0;
839 if( NT_SUCCESS( MmCopyToCaller( lpString,
840 KeyNames[i].pwsz,
841 StrMax * sizeof(WCHAR) ) ) &&
842 NT_SUCCESS( MmCopyToCaller( lpString + StrMax,
843 &null_wc,
844 sizeof( WCHAR ) ) ) )
845 {
846 ret = StrMax;
847 break;
848 }
849 }
850 }
851
852 if( ret == 0 )
853 {
854 WCHAR UCName[2];
855
856 UCName[0] = W32kSimpleToupper(IntMapVirtualKeyEx( VkCode, 2, keyLayout ));
857 UCName[1] = 0;
858 ret = 1;
859
860 if( !NT_SUCCESS(MmCopyToCaller( lpString, UCName, 2 * sizeof(WCHAR) )) )
861 RETURN(0);
862 }
863
864 RETURN(ret);
865
866 CLEANUP:
867 DPRINT("Leave NtUserGetKeyNameText, ret=%i\n",_ret_);
868 UserLeave();
869 END_CLEANUP;
870 }
871
872 /*
873 * Filter this message according to the current key layout, setting wParam
874 * appropriately.
875 */
876
877 VOID FASTCALL
878 W32kKeyProcessMessage(LPMSG Msg,
879 PKBDTABLES KeyboardLayout,
880 BYTE Prefix)
881 {
882 DWORD ScanCode = 0, ModifierBits = 0;
883 DWORD i = 0;
884 DWORD BaseMapping = 0;
885 DWORD RawVk = 0;
886 static WORD NumpadConversion[][2] =
887 { { VK_DELETE, VK_DECIMAL },
888 { VK_INSERT, VK_NUMPAD0 },
889 { VK_END, VK_NUMPAD1 },
890 { VK_DOWN, VK_NUMPAD2 },
891 { VK_NEXT, VK_NUMPAD3 },
892 { VK_LEFT, VK_NUMPAD4 },
893 { VK_CLEAR, VK_NUMPAD5 },
894 { VK_RIGHT, VK_NUMPAD6 },
895 { VK_HOME, VK_NUMPAD7 },
896 { VK_UP, VK_NUMPAD8 },
897 { VK_PRIOR, VK_NUMPAD9 },
898 { 0,0 } };
899 PVSC_VK VscVkTable = NULL;
900
901 if( !KeyboardLayout || !Msg ||
902 (Msg->message != WM_KEYDOWN && Msg->message != WM_SYSKEYDOWN &&
903 Msg->message != WM_KEYUP && Msg->message != WM_SYSKEYUP) )
904 {
905 return;
906 }
907
908 /* arty -- handle numpad -- On real windows, the actual key produced
909 * by the messaging layer is different based on the state of numlock. */
910 ModifierBits = ModBits(KeyboardLayout,gQueueKeyStateTable);
911
912 /* Get the raw scan code, so we can look up whether the key is a numpad
913 * key
914 *
915 * Shift and the LP_EXT_BIT cancel. */
916 ScanCode = (Msg->lParam >> 16) & 0xff;
917 BaseMapping = Msg->wParam =
918 IntMapVirtualKeyEx( ScanCode, 1, KeyboardLayout );
919 if( Prefix == 0 )
920 {
921 if( ScanCode >= KeyboardLayout->bMaxVSCtoVK )
922 RawVk = 0xff;
923 else
924 RawVk = KeyboardLayout->pusVSCtoVK[ScanCode];
925 }
926 else
927 {
928 if( Prefix == 0xE0 )
929 {
930 /* ignore shift codes */
931 if( ScanCode == 0x2A || ScanCode == 0x36 )
932 {
933 return;
934 }
935 VscVkTable = KeyboardLayout->pVSCtoVK_E0;
936 }
937 else if( Prefix == 0xE1 )
938 {
939 VscVkTable = KeyboardLayout->pVSCtoVK_E1;
940 }
941
942 RawVk = 0xff;
943 while (VscVkTable->Vsc)
944 {
945 if( VscVkTable->Vsc == ScanCode )
946 {
947 RawVk = VscVkTable->Vk;
948 }
949 VscVkTable++;
950 }
951 }
952
953 if ((ModifierBits & NUMLOCK_BIT) &&
954 !(ModifierBits & GetShiftBit(KeyboardLayout, VK_SHIFT)) &&
955 (RawVk & KNUMP) &&
956 !(Msg->lParam & LP_EXT_BIT))
957 {
958 /* The key in question is a numpad key. Search for a translation. */
959 for (i = 0; NumpadConversion[i][0]; i++)
960 {
961 if ((BaseMapping & 0xff) == NumpadConversion[i][0]) /* RawVk? */
962 {
963 Msg->wParam = NumpadConversion[i][1];
964 break;
965 }
966 }
967 }
968
969 DPRINT("Key: [%04x -> %04x]\n", BaseMapping, Msg->wParam);
970
971 /* Now that we have the VK, we can set the keymap appropriately
972 * This is a better place for this code, as it's guaranteed to be
973 * run, unlike translate message. */
974 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN)
975 {
976 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
977 TRUE ); /* Strike key */
978 }
979 else if (Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
980 {
981 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
982 FALSE ); /* Release key */
983 }
984
985 /* We need to unset SYSKEYDOWN if the ALT key is an ALT+Gr */
986 if( gQueueKeyStateTable[VK_RMENU] & KS_DOWN_BIT )
987 {
988 if( Msg->message == WM_SYSKEYDOWN )
989 Msg->message = WM_KEYDOWN;
990 else
991 Msg->message = WM_KEYUP;
992 }
993
994 }
995
996
997
998 DWORD FASTCALL
999 UserGetKeyboardType(
1000 DWORD TypeFlag)
1001 {
1002 switch(TypeFlag)
1003 {
1004 case 0: /* Keyboard type */
1005 return 4; /* AT-101 */
1006 case 1: /* Keyboard Subtype */
1007 return 0; /* There are no defined subtypes */
1008 case 2: /* Number of F-keys */
1009 return 12; /* We're doing an 101 for now, so return 12 F-keys */
1010 default:
1011 DPRINT1("Unknown type!\n");
1012 return 0; /* The book says 0 here, so 0 */
1013 }
1014 }
1015
1016
1017 /*
1018 Based on TryToTranslateChar, instead of processing VirtualKey match,
1019 look for wChar match.
1020 */
1021 DWORD
1022 APIENTRY
1023 NtUserVkKeyScanEx(
1024 WCHAR wChar,
1025 HKL hKeyboardLayout,
1026 BOOL UsehKL ) // TRUE from KeyboardLayout, FALSE from pkbl = (THREADINFO)->KeyboardLayout
1027 {
1028 PKBDTABLES KeyLayout;
1029 PVK_TO_WCHAR_TABLE vtwTbl;
1030 PVK_TO_WCHARS10 vkPtr;
1031 size_t size_this_entry;
1032 int nMod;
1033 PKBL pkbl = NULL;
1034 DWORD CapsMod = 0, CapsState = 0, Ret = -1;
1035
1036 DPRINT("NtUserVkKeyScanEx() wChar %d, KbdLayout 0x%p\n", wChar, hKeyboardLayout);
1037 UserEnterShared();
1038
1039 if (UsehKL)
1040 {
1041 if ( !hKeyboardLayout || !(pkbl = UserHklToKbl(hKeyboardLayout)))
1042 goto Exit;
1043 }
1044 else // From VkKeyScanAW it is FALSE so KeyboardLayout is white noise.
1045 {
1046 pkbl = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->KeyboardLayout;
1047 }
1048
1049 KeyLayout = pkbl->KBTables;
1050
1051 for (nMod = 0; KeyLayout->pVkToWcharTable[nMod].nModifications; nMod++)
1052 {
1053 vtwTbl = &KeyLayout->pVkToWcharTable[nMod];
1054 size_this_entry = vtwTbl->cbSize;
1055 vkPtr = (PVK_TO_WCHARS10)((BYTE *)vtwTbl->pVkToWchars);
1056
1057 while(vkPtr->VirtualKey)
1058 {
1059 /*
1060 0x01 Shift key
1061 0x02 Ctrl key
1062 0x04 Alt key
1063 Should have only 8 valid possibilities. Including zero.
1064 */
1065 for(CapsState = 0; CapsState < vtwTbl->nModifications; CapsState++)
1066 {
1067 if(vkPtr->wch[CapsState] == wChar)
1068 {
1069 CapsMod = KeyLayout->pCharModifiers->ModNumber[CapsState];
1070 DPRINT("nMod %d wC %04x: CapsMod %08x CapsState %08x MaxModBits %08x\n",
1071 nMod, wChar, CapsMod, CapsState, KeyLayout->pCharModifiers->wMaxModBits);
1072 Ret = ((CapsMod << 8)|(vkPtr->VirtualKey & 0xff));
1073 goto Exit;
1074 }
1075 }
1076 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
1077 }
1078 }
1079 Exit:
1080 UserLeave();
1081 return Ret;
1082 }
1083
1084
1085 /* EOF */