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