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