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