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