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