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