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