[CMAKE]
[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 <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 INIT_FUNCTION
62 NTSTATUS
63 NTAPI
64 InitKeyboardImpl(VOID)
65 {
66 RtlZeroMemory(&gQueueKeyStateTable,0x100);
67 return STATUS_SUCCESS;
68 }
69
70 /*** Statics used by TranslateMessage ***/
71
72 /*** Shift state code was out of hand, sorry. --- arty */
73
74 static UINT DontDistinguishShifts( UINT ret )
75 {
76 if( ret == VK_LSHIFT || ret == VK_RSHIFT )
77 ret = VK_SHIFT;
78 if( ret == VK_LCONTROL || ret == VK_RCONTROL )
79 ret = VK_CONTROL;
80 if( ret == VK_LMENU || ret == VK_RMENU )
81 ret = VK_MENU;
82 return ret;
83 }
84
85 static VOID APIENTRY SetKeyState(DWORD key, DWORD vk, DWORD ext, BOOL down)
86 {
87 ASSERT(vk <= 0xff);
88
89 /* Special handling for toggles like numpad and caps lock */
90 if (vk == VK_CAPITAL || vk == VK_NUMLOCK)
91 {
92 if (down)
93 gQueueKeyStateTable[vk] ^= KS_LOCK_BIT;
94 }
95
96 if (vk == VK_SHIFT)
97 vk = ext ? VK_RSHIFT : VK_LSHIFT;
98 if (vk == VK_CONTROL)
99 vk = ext ? VK_RCONTROL : VK_LCONTROL;
100 if (vk == VK_MENU)
101 vk = ext ? VK_RMENU : VK_LMENU;
102
103 if (down)
104 gQueueKeyStateTable[vk] |= KS_DOWN_BIT;
105 else
106 gQueueKeyStateTable[vk] &= ~KS_DOWN_MASK;
107
108 if (vk == VK_LSHIFT || vk == VK_RSHIFT)
109 {
110 if ((gQueueKeyStateTable[VK_LSHIFT] & KS_DOWN_BIT) ||
111 (gQueueKeyStateTable[VK_RSHIFT] & KS_DOWN_BIT))
112 {
113 gQueueKeyStateTable[VK_SHIFT] |= KS_DOWN_BIT;
114 }
115 else
116 {
117 gQueueKeyStateTable[VK_SHIFT] &= ~KS_DOWN_MASK;
118 }
119 }
120
121 if (vk == VK_LCONTROL || vk == VK_RCONTROL)
122 {
123 if ((gQueueKeyStateTable[VK_LCONTROL] & KS_DOWN_BIT) ||
124 (gQueueKeyStateTable[VK_RCONTROL] & KS_DOWN_BIT))
125 {
126 gQueueKeyStateTable[VK_CONTROL] |= KS_DOWN_BIT;
127 }
128 else
129 {
130 gQueueKeyStateTable[VK_CONTROL] &= ~KS_DOWN_MASK;
131 }
132 }
133
134 if (vk == VK_LMENU || vk == VK_RMENU)
135 {
136 if ((gQueueKeyStateTable[VK_LMENU] & KS_DOWN_BIT) ||
137 (gQueueKeyStateTable[VK_RMENU] & KS_DOWN_BIT))
138 {
139 gQueueKeyStateTable[VK_MENU] |= KS_DOWN_BIT;
140 }
141 else
142 {
143 gQueueKeyStateTable[VK_MENU] &= ~KS_DOWN_MASK;
144 }
145 }
146 }
147
148 VOID DumpKeyState( PBYTE KeyState )
149 {
150 int i;
151
152 DbgPrint( "KeyState { " );
153 for( i = 0; i < 0x100; i++ )
154 {
155 if( KeyState[i] )
156 DbgPrint( "%02x(%02x) ", i, KeyState[i] );
157 }
158 DbgPrint( "};\n" );
159 }
160
161 static BYTE KeysSet( PKBDTABLES pkKT, PBYTE KeyState,
162 int FakeModLeft, int FakeModRight )
163 {
164 if( !KeyState || !pkKT )
165 return 0;
166
167 /* Search special codes first */
168 if( FakeModLeft && KeyState[FakeModLeft] )
169 return KeyState[FakeModLeft];
170 else if( FakeModRight && KeyState[FakeModRight] )
171 return KeyState[FakeModRight];
172
173 return 0;
174 }
175
176 /* Search the keyboard layout modifiers table for the shift bit. I don't
177 * want to count on the shift bit not moving, because it can be specified
178 * in the layout */
179
180 static DWORD FASTCALL GetShiftBit( PKBDTABLES pkKT, DWORD Vk )
181 {
182 int i;
183
184 for( i = 0; pkKT->pCharModifiers->pVkToBit[i].Vk; i++ )
185 if( pkKT->pCharModifiers->pVkToBit[i].Vk == Vk )
186 return pkKT->pCharModifiers->pVkToBit[i].ModBits;
187
188 return 0;
189 }
190
191 static DWORD ModBits( PKBDTABLES pkKT, PBYTE KeyState )
192 {
193 DWORD ModBits = 0;
194
195 if( !KeyState )
196 return 0;
197
198 /* DumpKeyState( KeyState ); */
199
200 if (KeysSet( pkKT, KeyState, VK_LSHIFT, VK_RSHIFT ) &
201 KS_DOWN_BIT)
202 ModBits |= GetShiftBit( pkKT, VK_SHIFT );
203
204 if (KeysSet( pkKT, KeyState, VK_SHIFT, 0 ) &
205 KS_DOWN_BIT)
206 ModBits |= GetShiftBit( pkKT, VK_SHIFT );
207
208 if (KeysSet( pkKT, KeyState, VK_LCONTROL, VK_RCONTROL ) &
209 KS_DOWN_BIT )
210 ModBits |= GetShiftBit( pkKT, VK_CONTROL );
211
212 if (KeysSet( pkKT, KeyState, VK_CONTROL, 0 ) &
213 KS_DOWN_BIT )
214 ModBits |= GetShiftBit( pkKT, VK_CONTROL );
215
216 if (KeysSet( pkKT, KeyState, VK_LMENU, VK_RMENU ) &
217 KS_DOWN_BIT )
218 ModBits |= GetShiftBit( pkKT, VK_MENU );
219
220 /* Handle Alt+Gr */
221 if (pkKT->fLocalFlags & 0x1)
222 if (KeysSet( pkKT, KeyState, VK_RMENU, 0 ) &
223 KS_DOWN_BIT)
224 ModBits |= GetShiftBit( pkKT, VK_CONTROL );
225
226 /* Deal with VK_CAPITAL */
227 if (KeysSet( pkKT, KeyState, VK_CAPITAL, 0 ) & KS_LOCK_BIT)
228 {
229 ModBits |= CAPITAL_BIT;
230 }
231
232 /* Deal with VK_NUMLOCK */
233 if (KeysSet( pkKT, KeyState, VK_NUMLOCK, 0 ) & KS_LOCK_BIT)
234 {
235 ModBits |= NUMLOCK_BIT;
236 }
237
238 DPRINT( "Current Mod Bits: %x\n", ModBits );
239
240 return ModBits;
241 }
242
243 static BOOL TryToTranslateChar(WORD wVirtKey,
244 DWORD ModBits,
245 PBOOL pbDead,
246 PBOOL pbLigature,
247 PWCHAR pwcTranslatedChar,
248 PKBDTABLES keyLayout )
249 {
250 PVK_TO_WCHAR_TABLE vtwTbl;
251 PVK_TO_WCHARS10 vkPtr;
252 size_t size_this_entry;
253 int nMod;
254 DWORD CapsMod = 0, CapsState = 0;
255
256 CapsState = ModBits & ~MOD_BITS_MASK;
257 ModBits = ModBits & MOD_BITS_MASK;
258
259 DPRINT ( "TryToTranslate: %04x %x\n", wVirtKey, ModBits );
260
261 if (ModBits > keyLayout->pCharModifiers->wMaxModBits)
262 {
263 return FALSE;
264 }
265
266 for (nMod = 0; keyLayout->pVkToWcharTable[nMod].nModifications; nMod++)
267 {
268 vtwTbl = &keyLayout->pVkToWcharTable[nMod];
269 size_this_entry = vtwTbl->cbSize;
270 vkPtr = (PVK_TO_WCHARS10)((BYTE *)vtwTbl->pVkToWchars);
271 while(vkPtr->VirtualKey)
272 {
273 if( wVirtKey == (vkPtr->VirtualKey & 0xff) )
274 {
275 CapsMod = keyLayout->pCharModifiers->ModNumber
276 [ModBits ^
277 ((CapsState & CAPITAL_BIT) ? vkPtr->Attributes : 0)];
278
279 if( CapsMod >= keyLayout->pVkToWcharTable[nMod].nModifications )
280 {
281 return FALSE;
282 }
283
284 if( vkPtr->wch[CapsMod] == WCH_NONE )
285 {
286 return FALSE;
287 }
288
289 *pbDead = vkPtr->wch[CapsMod] == WCH_DEAD;
290 *pbLigature = vkPtr->wch[CapsMod] == WCH_LGTR;
291 *pwcTranslatedChar = vkPtr->wch[CapsMod];
292
293 DPRINT("%d %04x: CapsMod %08x CapsState %08x Char %04x\n",
294 nMod, wVirtKey,
295 CapsMod, CapsState, *pwcTranslatedChar);
296
297 if( *pbDead )
298 {
299 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
300 if( vkPtr->VirtualKey != 0xff )
301 {
302 DPRINT( "Found dead key with no trailer in the table.\n" );
303 DPRINT( "VK: %04x, ADDR: %p\n", wVirtKey, vkPtr );
304 return FALSE;
305 }
306 *pwcTranslatedChar = vkPtr->wch[CapsMod];
307 }
308 return TRUE;
309 }
310 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
311 }
312 }
313 return FALSE;
314 }
315
316 static
317 int APIENTRY
318 ToUnicodeInner(UINT wVirtKey,
319 UINT wScanCode,
320 PBYTE lpKeyState,
321 LPWSTR pwszBuff,
322 int cchBuff,
323 UINT wFlags,
324 PKBDTABLES pkKT)
325 {
326 WCHAR wcTranslatedChar;
327 BOOL bDead;
328 BOOL bLigature;
329
330 if( !pkKT )
331 return 0;
332
333 if( TryToTranslateChar( wVirtKey,
334 ModBits( pkKT, lpKeyState ),
335 &bDead,
336 &bLigature,
337 &wcTranslatedChar,
338 pkKT ) )
339 {
340 if( bLigature )
341 {
342 DPRINT("Not handling ligature (yet)\n" );
343 return 0;
344 }
345
346 if( cchBuff > 0 )
347 pwszBuff[0] = wcTranslatedChar;
348
349 return bDead ? -1 : 1;
350 }
351
352 return 0;
353 }
354
355
356 DWORD FASTCALL UserGetKeyState(DWORD key)
357 {
358 DWORD ret = 0;
359
360 if( key < 0x100 )
361 {
362 ret = ((DWORD)(gQueueKeyStateTable[key] & KS_DOWN_BIT) << 8 ) |
363 (gQueueKeyStateTable[key] & KS_LOCK_BIT);
364 }
365
366 return ret;
367 }
368
369
370 SHORT
371 APIENTRY
372 NtUserGetKeyState(
373 INT key)
374 {
375 DECLARE_RETURN(DWORD);
376
377 DPRINT("Enter NtUserGetKeyState\n");
378 UserEnterExclusive();
379
380 RETURN(UserGetKeyState(key));
381
382 CLEANUP:
383 DPRINT("Leave NtUserGetKeyState, ret=%i\n",_ret_);
384 UserLeave();
385 END_CLEANUP;
386 }
387
388
389
390 DWORD FASTCALL UserGetAsyncKeyState(DWORD key)
391 {
392 DWORD ret = 0;
393
394 if( key < 0x100 )
395 {
396 ret = ((DWORD)(gQueueKeyStateTable[key] & KS_DOWN_BIT) << 8 ) |
397 (gQueueKeyStateTable[key] & KS_LOCK_BIT);
398 }
399
400 return ret;
401 }
402
403
404
405 SHORT
406 APIENTRY
407 NtUserGetAsyncKeyState(
408 INT key)
409 {
410 DECLARE_RETURN(SHORT);
411
412 DPRINT("Enter NtUserGetAsyncKeyState\n");
413 UserEnterExclusive();
414
415 RETURN((SHORT)UserGetAsyncKeyState(key));
416
417 CLEANUP:
418 DPRINT("Leave NtUserGetAsyncKeyState, ret=%i\n",_ret_);
419 UserLeave();
420 END_CLEANUP;
421 }
422
423
424
425 BOOL FASTCALL
426 IntTranslateKbdMessage(LPMSG lpMsg,
427 UINT flags)
428 {
429 PTHREADINFO pti;
430 static INT dead_char = 0;
431 LONG UState = 0;
432 WCHAR wp[2] = { 0 };
433 MSG NewMsg = { 0 };
434 PKBDTABLES keyLayout;
435 BOOL Result = FALSE;
436
437 pti = PsGetCurrentThreadWin32Thread();
438 keyLayout = pti->KeyboardLayout->KBTables;
439 if( !keyLayout )
440 return FALSE;
441
442 if (lpMsg->message < WM_KEYFIRST || lpMsg->message > WM_KEYLAST)
443 return FALSE;
444 if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN)
445 return FALSE;
446
447 /* All messages have to contain the cursor point. */
448 NewMsg.pt = gpsi->ptCursor;
449
450 switch (lpMsg->wParam)
451 {
452 case VK_PACKET:
453 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
454 NewMsg.hwnd = lpMsg->hwnd;
455 NewMsg.wParam = HIWORD(lpMsg->lParam);
456 NewMsg.lParam = LOWORD(lpMsg->lParam);
457 MsqPostMessage(pti->MessageQueue, &NewMsg, FALSE, QS_KEY);
458 return TRUE;
459 }
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( "ExAllocatePoolWithTag(%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 if (!VscVkTable)
948 {
949 DPRINT1("somethings wrong, Prefix=0x%x", Prefix);
950 return;
951 }
952
953 RawVk = 0xff;
954 while (VscVkTable->Vsc)
955 {
956 if( VscVkTable->Vsc == ScanCode )
957 {
958 RawVk = VscVkTable->Vk;
959 }
960 VscVkTable++;
961 }
962 }
963
964 if ((ModifierBits & NUMLOCK_BIT) &&
965 !(ModifierBits & GetShiftBit(KeyboardLayout, VK_SHIFT)) &&
966 (RawVk & KNUMP) &&
967 !(Msg->lParam & LP_EXT_BIT))
968 {
969 /* The key in question is a numpad key. Search for a translation. */
970 for (i = 0; NumpadConversion[i][0]; i++)
971 {
972 if ((BaseMapping & 0xff) == NumpadConversion[i][0]) /* RawVk? */
973 {
974 Msg->wParam = NumpadConversion[i][1];
975 break;
976 }
977 }
978 }
979
980 DPRINT("Key: [%04x -> %04x]\n", BaseMapping, Msg->wParam);
981
982 /* Now that we have the VK, we can set the keymap appropriately
983 * This is a better place for this code, as it's guaranteed to be
984 * run, unlike translate message. */
985 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN)
986 {
987 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
988 TRUE ); /* Strike key */
989 }
990 else if (Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
991 {
992 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
993 FALSE ); /* Release key */
994 }
995
996 /* We need to unset SYSKEYDOWN if the ALT key is an ALT+Gr */
997 if( gQueueKeyStateTable[VK_RMENU] & KS_DOWN_BIT )
998 {
999 if( Msg->message == WM_SYSKEYDOWN )
1000 Msg->message = WM_KEYDOWN;
1001 else
1002 Msg->message = WM_KEYUP;
1003 }
1004
1005 }
1006
1007
1008
1009 DWORD FASTCALL
1010 UserGetKeyboardType(
1011 DWORD TypeFlag)
1012 {
1013 switch(TypeFlag)
1014 {
1015 case 0: /* Keyboard type */
1016 return 4; /* AT-101 */
1017 case 1: /* Keyboard Subtype */
1018 return 0; /* There are no defined subtypes */
1019 case 2: /* Number of F-keys */
1020 return 12; /* We're doing an 101 for now, so return 12 F-keys */
1021 default:
1022 DPRINT1("Unknown type!\n");
1023 return 0; /* The book says 0 here, so 0 */
1024 }
1025 }
1026
1027
1028 /*
1029 Based on TryToTranslateChar, instead of processing VirtualKey match,
1030 look for wChar match.
1031 */
1032 DWORD
1033 APIENTRY
1034 NtUserVkKeyScanEx(
1035 WCHAR wChar,
1036 HKL hKeyboardLayout,
1037 BOOL UsehKL ) // TRUE from KeyboardLayout, FALSE from pkbl = (THREADINFO)->KeyboardLayout
1038 {
1039 PKBDTABLES KeyLayout;
1040 PVK_TO_WCHAR_TABLE vtwTbl;
1041 PVK_TO_WCHARS10 vkPtr;
1042 size_t size_this_entry;
1043 int nMod;
1044 PKBL pkbl = NULL;
1045 DWORD CapsMod = 0, CapsState = 0, Ret = -1;
1046
1047 DPRINT("NtUserVkKeyScanEx() wChar %d, KbdLayout 0x%p\n", wChar, hKeyboardLayout);
1048 UserEnterShared();
1049
1050 if (UsehKL)
1051 {
1052 if ( !hKeyboardLayout || !(pkbl = UserHklToKbl(hKeyboardLayout)))
1053 goto Exit;
1054 }
1055 else // From VkKeyScanAW it is FALSE so KeyboardLayout is white noise.
1056 {
1057 pkbl = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->KeyboardLayout;
1058 }
1059
1060 KeyLayout = pkbl->KBTables;
1061
1062 for (nMod = 0; KeyLayout->pVkToWcharTable[nMod].nModifications; nMod++)
1063 {
1064 vtwTbl = &KeyLayout->pVkToWcharTable[nMod];
1065 size_this_entry = vtwTbl->cbSize;
1066 vkPtr = (PVK_TO_WCHARS10)((BYTE *)vtwTbl->pVkToWchars);
1067
1068 while(vkPtr->VirtualKey)
1069 {
1070 /*
1071 0x01 Shift key
1072 0x02 Ctrl key
1073 0x04 Alt key
1074 Should have only 8 valid possibilities. Including zero.
1075 */
1076 for(CapsState = 0; CapsState < vtwTbl->nModifications; CapsState++)
1077 {
1078 if(vkPtr->wch[CapsState] == wChar)
1079 {
1080 CapsMod = KeyLayout->pCharModifiers->ModNumber[CapsState];
1081 DPRINT("nMod %d wC %04x: CapsMod %08x CapsState %08x MaxModBits %08x\n",
1082 nMod, wChar, CapsMod, CapsState, KeyLayout->pCharModifiers->wMaxModBits);
1083 Ret = ((CapsMod << 8)|(vkPtr->VirtualKey & 0xff));
1084 goto Exit;
1085 }
1086 }
1087 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
1088 }
1089 }
1090 Exit:
1091 UserLeave();
1092 return Ret;
1093 }
1094
1095
1096 /* EOF */