[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 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 }
345
346 return ret;
347 }
348
349 /***********************************************************************
350 * get_key_state
351 */
352 WORD FASTCALL get_key_state(void)
353 {
354 WORD ret = 0;
355
356 if (gpsi->aiSysMet[SM_SWAPBUTTON])
357 {
358 if (UserGetAsyncKeyState(VK_RBUTTON) & 0x80) ret |= MK_LBUTTON;
359 if (UserGetAsyncKeyState(VK_LBUTTON) & 0x80) ret |= MK_RBUTTON;
360 }
361 else
362 {
363 if (UserGetAsyncKeyState(VK_LBUTTON) & 0x80) ret |= MK_LBUTTON;
364 if (UserGetAsyncKeyState(VK_RBUTTON) & 0x80) ret |= MK_RBUTTON;
365 }
366 if (UserGetAsyncKeyState(VK_MBUTTON) & 0x80) ret |= MK_MBUTTON;
367 if (UserGetAsyncKeyState(VK_SHIFT) & 0x80) ret |= MK_SHIFT;
368 if (UserGetAsyncKeyState(VK_CONTROL) & 0x80) ret |= MK_CONTROL;
369 if (UserGetAsyncKeyState(VK_XBUTTON1) & 0x80) ret |= MK_XBUTTON1;
370 if (UserGetAsyncKeyState(VK_XBUTTON2) & 0x80) ret |= MK_XBUTTON2;
371 return ret;
372 }
373
374 SHORT
375 APIENTRY
376 NtUserGetAsyncKeyState(
377 INT key)
378 {
379 DECLARE_RETURN(SHORT);
380
381 DPRINT("Enter NtUserGetAsyncKeyState\n");
382 UserEnterExclusive();
383
384 RETURN((SHORT)UserGetAsyncKeyState(key));
385
386 CLEANUP:
387 DPRINT("Leave NtUserGetAsyncKeyState, ret=%i\n",_ret_);
388 UserLeave();
389 END_CLEANUP;
390 }
391
392
393
394 BOOL FASTCALL
395 IntTranslateKbdMessage(LPMSG lpMsg,
396 UINT flags)
397 {
398 PTHREADINFO pti;
399 static INT dead_char = 0;
400 LONG UState = 0;
401 WCHAR wp[2] = { 0 };
402 MSG NewMsg = { 0 };
403 PKBDTABLES keyLayout;
404 BOOL Result = FALSE;
405
406 pti = PsGetCurrentThreadWin32Thread();
407 keyLayout = pti->KeyboardLayout->KBTables;
408 if( !keyLayout )
409 return FALSE;
410
411 if (lpMsg->message < WM_KEYFIRST || lpMsg->message > WM_KEYLAST)
412 return FALSE;
413 if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN)
414 return FALSE;
415
416 /* All messages have to contain the cursor point. */
417 NewMsg.pt = gpsi->ptCursor;
418
419 switch (lpMsg->wParam)
420 {
421 case VK_PACKET:
422 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
423 NewMsg.hwnd = lpMsg->hwnd;
424 NewMsg.wParam = HIWORD(lpMsg->lParam);
425 NewMsg.lParam = LOWORD(lpMsg->lParam);
426 MsqPostMessage(pti->MessageQueue, &NewMsg, FALSE, QS_KEY);
427 return TRUE;
428 }
429
430 UState = ToUnicodeInner(lpMsg->wParam, HIWORD(lpMsg->lParam) & 0xff,
431 gQueueKeyStateTable, wp, 2, 0,
432 keyLayout );
433
434 if (UState == 1)
435 {
436 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
437 if (dead_char)
438 {
439 ULONG i;
440 WCHAR first, second;
441 DPRINT("PREVIOUS DEAD CHAR: %c\n", dead_char);
442
443 for( i = 0; keyLayout->pDeadKey[i].dwBoth; i++ )
444 {
445 first = keyLayout->pDeadKey[i].dwBoth >> 16;
446 second = keyLayout->pDeadKey[i].dwBoth;
447 if (first == dead_char && second == wp[0])
448 {
449 wp[0] = keyLayout->pDeadKey[i].wchComposed;
450 dead_char = 0;
451 break;
452 }
453 }
454
455 DPRINT("FINAL CHAR: %c\n", wp[0]);
456 }
457
458 if (dead_char)
459 {
460 NewMsg.hwnd = lpMsg->hwnd;
461 NewMsg.wParam = dead_char;
462 NewMsg.lParam = lpMsg->lParam;
463 dead_char = 0;
464 MsqPostMessage(pti->MessageQueue, &NewMsg, FALSE, QS_KEY);
465 }
466
467 NewMsg.hwnd = lpMsg->hwnd;
468 NewMsg.wParam = wp[0];
469 NewMsg.lParam = lpMsg->lParam;
470 DPRINT( "CHAR='%c' %04x %08x\n", wp[0], wp[0], lpMsg->lParam );
471 MsqPostMessage(pti->MessageQueue, &NewMsg, FALSE, QS_KEY);
472 Result = TRUE;
473 }
474 else if (UState == -1)
475 {
476 NewMsg.message =
477 (lpMsg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
478 NewMsg.hwnd = lpMsg->hwnd;
479 NewMsg.wParam = wp[0];
480 NewMsg.lParam = lpMsg->lParam;
481 dead_char = wp[0];
482 MsqPostMessage(pti->MessageQueue, &NewMsg, FALSE, QS_KEY);
483 Result = TRUE;
484 }
485
486 return Result;
487 }
488
489 static UINT VkToScan( UINT Code, BOOL ExtCode, PKBDTABLES pkKT )
490 {
491 int i;
492
493 for( i = 0; i < pkKT->bMaxVSCtoVK; i++ )
494 {
495 if( pkKT->pusVSCtoVK[i] == Code )
496 {
497 return i;
498 }
499 }
500
501 return 0;
502 }
503
504 UINT ScanToVk( UINT Code, BOOL ExtKey, PKBDTABLES pkKT )
505 {
506 if( !pkKT )
507 {
508 DPRINT("ScanToVk: No layout\n");
509 return 0;
510 }
511
512 if( ExtKey )
513 {
514 int i;
515
516 for( i = 0; pkKT->pVSCtoVK_E0[i].Vsc; i++ )
517 {
518 if( pkKT->pVSCtoVK_E0[i].Vsc == Code )
519 return pkKT->pVSCtoVK_E0[i].Vk & 0xff;
520 }
521 for( i = 0; pkKT->pVSCtoVK_E1[i].Vsc; i++ )
522 {
523 if( pkKT->pVSCtoVK_E1[i].Vsc == Code )
524 return pkKT->pVSCtoVK_E1[i].Vk & 0xff;
525 }
526
527 return 0;
528 }
529 else
530 {
531 if( Code >= pkKT->bMaxVSCtoVK )
532 {
533 return 0;
534 }
535 return pkKT->pusVSCtoVK[Code] & 0xff;
536 }
537 }
538
539 /*
540 * Map a virtual key code, or virtual scan code, to a scan code, key code,
541 * or unshifted unicode character.
542 *
543 * Code: See Below
544 * Type:
545 * 0 -- Code is a virtual key code that is converted into a virtual scan code
546 * that does not distinguish between left and right shift keys.
547 * 1 -- Code is a virtual scan code that is converted into a virtual key code
548 * that does not distinguish between left and right shift keys.
549 * 2 -- Code is a virtual key code that is converted into an unshifted unicode
550 * character.
551 * 3 -- Code is a virtual scan code that is converted into a virtual key code
552 * that distinguishes left and right shift keys.
553 * KeyLayout: Keyboard layout handle (currently, unused)
554 *
555 * @implemented
556 */
557
558 static UINT IntMapVirtualKeyEx( UINT Code, UINT Type, PKBDTABLES keyLayout )
559 {
560 UINT ret = 0;
561
562 switch( Type )
563 {
564 case 0:
565 if( Code == VK_SHIFT )
566 Code = VK_LSHIFT;
567 if( Code == VK_MENU )
568 Code = VK_LMENU;
569 if( Code == VK_CONTROL )
570 Code = VK_LCONTROL;
571 ret = VkToScan( Code, FALSE, keyLayout );
572 break;
573
574 case 1:
575 ret =
576 DontDistinguishShifts
577 (IntMapVirtualKeyEx( Code, 3, keyLayout ) );
578 break;
579
580 case 2:
581 {
582 WCHAR wp[2] = {0};
583
584 ret = VkToScan( Code, FALSE, keyLayout );
585 ToUnicodeInner( Code, ret, 0, wp, 2, 0, keyLayout );
586 ret = wp[0];
587 }
588 break;
589
590 case 3:
591
592 ret = ScanToVk( Code, FALSE, keyLayout );
593 break;
594 }
595
596 return ret;
597 }
598
599 UINT
600 APIENTRY
601 NtUserMapVirtualKeyEx( UINT Code, UINT Type, DWORD keyboardId, HKL dwhkl )
602 {
603 PTHREADINFO pti;
604 PKBDTABLES keyLayout;
605 DECLARE_RETURN(UINT);
606
607 DPRINT("Enter NtUserMapVirtualKeyEx\n");
608 UserEnterExclusive();
609
610 pti = PsGetCurrentThreadWin32Thread();
611 keyLayout = pti ? pti->KeyboardLayout->KBTables : 0;
612
613 if( !keyLayout )
614 RETURN(0);
615
616 RETURN(IntMapVirtualKeyEx( Code, Type, keyLayout ));
617
618 CLEANUP:
619 DPRINT("Leave NtUserMapVirtualKeyEx, ret=%i\n",_ret_);
620 UserLeave();
621 END_CLEANUP;
622 }
623
624
625 int
626 APIENTRY
627 NtUserToUnicodeEx(
628 UINT wVirtKey,
629 UINT wScanCode,
630 PBYTE lpKeyState,
631 LPWSTR pwszBuff,
632 int cchBuff,
633 UINT wFlags,
634 HKL dwhkl )
635 {
636 PTHREADINFO pti;
637 BYTE KeyStateBuf[0x100];
638 PWCHAR OutPwszBuff = 0;
639 int ret = 0;
640 DECLARE_RETURN(int);
641
642 DPRINT("Enter NtUserSetKeyboardState\n");
643 UserEnterShared();//fixme: this syscall doesnt seem to need any locking...
644
645 /* Key up? */
646 if (wScanCode & SC_KEY_UP)
647 {
648 RETURN(0);
649 }
650
651 if( !NT_SUCCESS(MmCopyFromCaller(KeyStateBuf,
652 lpKeyState,
653 sizeof(KeyStateBuf))) )
654 {
655 DPRINT1( "Couldn't copy key state from caller.\n" );
656 RETURN(0);
657 }
658
659 /* Virtual code is correct? */
660 if (wVirtKey < 0x100)
661 {
662 OutPwszBuff = ExAllocatePoolWithTag(NonPagedPool,sizeof(WCHAR) * cchBuff, TAG_STRING);
663 if( !OutPwszBuff )
664 {
665 DPRINT1( "ExAllocatePoolWithTag(%d) failed\n", sizeof(WCHAR) * cchBuff);
666 RETURN(0);
667 }
668 RtlZeroMemory( OutPwszBuff, sizeof( WCHAR ) * cchBuff );
669
670 pti = PsGetCurrentThreadWin32Thread();
671 ret = ToUnicodeInner( wVirtKey,
672 wScanCode,
673 KeyStateBuf,
674 OutPwszBuff,
675 cchBuff,
676 wFlags,
677 pti ? pti->KeyboardLayout->KBTables : 0 );
678
679 MmCopyToCaller(pwszBuff,OutPwszBuff,sizeof(WCHAR)*cchBuff);
680 ExFreePoolWithTag(OutPwszBuff, TAG_STRING);
681 }
682
683 RETURN(ret);
684
685 CLEANUP:
686 DPRINT("Leave NtUserSetKeyboardState, ret=%i\n",_ret_);
687 UserLeave();
688 END_CLEANUP;
689 }
690
691 static int W32kSimpleToupper( int ch )
692 {
693 if( ch >= 'a' && ch <= 'z' )
694 ch = ch - 'a' + 'A';
695 return ch;
696 }
697
698 DWORD
699 APIENTRY
700 NtUserGetKeyNameText( LONG lParam, LPWSTR lpString, int nSize )
701 {
702 PTHREADINFO pti;
703 int i;
704 DWORD ret = 0;
705 UINT CareVk = 0;
706 UINT VkCode = 0;
707 UINT ScanCode = (lParam >> 16) & 0xff;
708 BOOL ExtKey = lParam & (1<<24) ? TRUE : FALSE;
709 PKBDTABLES keyLayout;
710 VSC_LPWSTR *KeyNames;
711 DECLARE_RETURN(DWORD);
712
713 DPRINT("Enter NtUserGetKeyNameText\n");
714 UserEnterShared();
715
716 pti = PsGetCurrentThreadWin32Thread();
717 keyLayout = pti ? pti->KeyboardLayout->KBTables : 0;
718
719 if( !keyLayout || nSize < 1 )
720 RETURN(0);
721
722 if( lParam & (1<<25) )
723 {
724 CareVk = VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
725 switch (VkCode)
726 {
727 case VK_RSHIFT:
728 ScanCode |= 0x100;
729 case VK_LSHIFT:
730 VkCode = VK_SHIFT;
731 break;
732 case VK_LCONTROL:
733 case VK_RCONTROL:
734 VkCode = VK_CONTROL;
735 break;
736 case VK_LMENU:
737 case VK_RMENU:
738 VkCode = VK_MENU;
739 break;
740 }
741 }
742 else
743 {
744 VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
745 }
746
747 KeyNames = 0;
748
749 if( CareVk != VkCode )
750 ScanCode = VkToScan( VkCode, ExtKey, keyLayout );
751
752 if( ExtKey )
753 KeyNames = keyLayout->pKeyNamesExt;
754 else
755 KeyNames = keyLayout->pKeyNames;
756
757 for( i = 0; KeyNames[i].pwsz; i++ )
758 {
759 if( KeyNames[i].vsc == ScanCode )
760 {
761 UINT StrLen = wcslen(KeyNames[i].pwsz);
762 UINT StrMax = StrLen > (nSize - 1) ? (nSize - 1) : StrLen;
763 WCHAR null_wc = 0;
764 if( NT_SUCCESS( MmCopyToCaller( lpString,
765 KeyNames[i].pwsz,
766 StrMax * sizeof(WCHAR) ) ) &&
767 NT_SUCCESS( MmCopyToCaller( lpString + StrMax,
768 &null_wc,
769 sizeof( WCHAR ) ) ) )
770 {
771 ret = StrMax;
772 break;
773 }
774 }
775 }
776
777 if( ret == 0 )
778 {
779 WCHAR UCName[2];
780
781 UCName[0] = W32kSimpleToupper(IntMapVirtualKeyEx( VkCode, 2, keyLayout ));
782 UCName[1] = 0;
783 ret = 1;
784
785 if( !NT_SUCCESS(MmCopyToCaller( lpString, UCName, 2 * sizeof(WCHAR) )) )
786 RETURN(0);
787 }
788
789 RETURN(ret);
790
791 CLEANUP:
792 DPRINT("Leave NtUserGetKeyNameText, ret=%i\n",_ret_);
793 UserLeave();
794 END_CLEANUP;
795 }
796
797 /*
798 * Filter this message according to the current key layout, setting wParam
799 * appropriately.
800 */
801
802 VOID FASTCALL
803 W32kKeyProcessMessage(LPMSG Msg,
804 PKBDTABLES KeyboardLayout,
805 BYTE Prefix)
806 {
807 DWORD ScanCode = 0, ModifierBits = 0;
808 DWORD i = 0;
809 DWORD BaseMapping = 0;
810 DWORD RawVk = 0;
811 static WORD NumpadConversion[][2] =
812 { { VK_DELETE, VK_DECIMAL },
813 { VK_INSERT, VK_NUMPAD0 },
814 { VK_END, VK_NUMPAD1 },
815 { VK_DOWN, VK_NUMPAD2 },
816 { VK_NEXT, VK_NUMPAD3 },
817 { VK_LEFT, VK_NUMPAD4 },
818 { VK_CLEAR, VK_NUMPAD5 },
819 { VK_RIGHT, VK_NUMPAD6 },
820 { VK_HOME, VK_NUMPAD7 },
821 { VK_UP, VK_NUMPAD8 },
822 { VK_PRIOR, VK_NUMPAD9 },
823 { 0,0 } };
824 PVSC_VK VscVkTable = NULL;
825
826 if( !KeyboardLayout || !Msg ||
827 (Msg->message != WM_KEYDOWN && Msg->message != WM_SYSKEYDOWN &&
828 Msg->message != WM_KEYUP && Msg->message != WM_SYSKEYUP) )
829 {
830 return;
831 }
832
833 /* arty -- handle numpad -- On real windows, the actual key produced
834 * by the messaging layer is different based on the state of numlock. */
835 ModifierBits = ModBits(KeyboardLayout,gQueueKeyStateTable);
836
837 /* Get the raw scan code, so we can look up whether the key is a numpad
838 * key
839 *
840 * Shift and the LP_EXT_BIT cancel. */
841 ScanCode = (Msg->lParam >> 16) & 0xff;
842 BaseMapping = Msg->wParam =
843 IntMapVirtualKeyEx( ScanCode, 1, KeyboardLayout );
844 if( Prefix == 0 )
845 {
846 if( ScanCode >= KeyboardLayout->bMaxVSCtoVK )
847 RawVk = 0xff;
848 else
849 RawVk = KeyboardLayout->pusVSCtoVK[ScanCode];
850 }
851 else
852 {
853 if( Prefix == 0xE0 )
854 {
855 /* ignore shift codes */
856 if( ScanCode == 0x2A || ScanCode == 0x36 )
857 {
858 return;
859 }
860 VscVkTable = KeyboardLayout->pVSCtoVK_E0;
861 }
862 else if( Prefix == 0xE1 )
863 {
864 VscVkTable = KeyboardLayout->pVSCtoVK_E1;
865 }
866
867 if (!VscVkTable)
868 {
869 DPRINT1("somethings wrong, Prefix=0x%x", Prefix);
870 return;
871 }
872
873 RawVk = 0xff;
874 while (VscVkTable->Vsc)
875 {
876 if( VscVkTable->Vsc == ScanCode )
877 {
878 RawVk = VscVkTable->Vk;
879 }
880 VscVkTable++;
881 }
882 }
883
884 if ((ModifierBits & NUMLOCK_BIT) &&
885 !(ModifierBits & GetShiftBit(KeyboardLayout, VK_SHIFT)) &&
886 (RawVk & KNUMP) &&
887 !(Msg->lParam & LP_EXT_BIT))
888 {
889 /* The key in question is a numpad key. Search for a translation. */
890 for (i = 0; NumpadConversion[i][0]; i++)
891 {
892 if ((BaseMapping & 0xff) == NumpadConversion[i][0]) /* RawVk? */
893 {
894 Msg->wParam = NumpadConversion[i][1];
895 break;
896 }
897 }
898 }
899
900 DPRINT("Key: [%04x -> %04x]\n", BaseMapping, Msg->wParam);
901
902 /* Now that we have the VK, we can set the keymap appropriately
903 * This is a better place for this code, as it's guaranteed to be
904 * run, unlike translate message. */
905 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN)
906 {
907 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
908 TRUE ); /* Strike key */
909 }
910 else if (Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
911 {
912 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
913 FALSE ); /* Release key */
914 }
915
916 /* We need to unset SYSKEYDOWN if the ALT key is an ALT+Gr */
917 if( gQueueKeyStateTable[VK_RMENU] & KS_DOWN_BIT )
918 {
919 if( Msg->message == WM_SYSKEYDOWN )
920 Msg->message = WM_KEYDOWN;
921 else
922 Msg->message = WM_KEYUP;
923 }
924
925 }
926
927
928
929 DWORD FASTCALL
930 UserGetKeyboardType(
931 DWORD TypeFlag)
932 {
933 switch(TypeFlag)
934 {
935 case 0: /* Keyboard type */
936 return 4; /* AT-101 */
937 case 1: /* Keyboard Subtype */
938 return 0; /* There are no defined subtypes */
939 case 2: /* Number of F-keys */
940 return 12; /* We're doing an 101 for now, so return 12 F-keys */
941 default:
942 DPRINT1("Unknown type!\n");
943 return 0; /* The book says 0 here, so 0 */
944 }
945 }
946
947
948 /*
949 Based on TryToTranslateChar, instead of processing VirtualKey match,
950 look for wChar match.
951 */
952 DWORD
953 APIENTRY
954 NtUserVkKeyScanEx(
955 WCHAR wChar,
956 HKL hKeyboardLayout,
957 BOOL UsehKL ) // TRUE from KeyboardLayout, FALSE from pkbl = (THREADINFO)->KeyboardLayout
958 {
959 PKBDTABLES KeyLayout;
960 PVK_TO_WCHAR_TABLE vtwTbl;
961 PVK_TO_WCHARS10 vkPtr;
962 size_t size_this_entry;
963 int nMod;
964 PKBL pkbl = NULL;
965 DWORD CapsMod = 0, CapsState = 0, Ret = -1;
966
967 DPRINT("NtUserVkKeyScanEx() wChar %d, KbdLayout 0x%p\n", wChar, hKeyboardLayout);
968 UserEnterShared();
969
970 if (UsehKL)
971 {
972 if ( !hKeyboardLayout || !(pkbl = UserHklToKbl(hKeyboardLayout)))
973 goto Exit;
974 }
975 else // From VkKeyScanAW it is FALSE so KeyboardLayout is white noise.
976 {
977 pkbl = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->KeyboardLayout;
978 }
979
980 KeyLayout = pkbl->KBTables;
981
982 for (nMod = 0; KeyLayout->pVkToWcharTable[nMod].nModifications; nMod++)
983 {
984 vtwTbl = &KeyLayout->pVkToWcharTable[nMod];
985 size_this_entry = vtwTbl->cbSize;
986 vkPtr = (PVK_TO_WCHARS10)((BYTE *)vtwTbl->pVkToWchars);
987
988 while(vkPtr->VirtualKey)
989 {
990 /*
991 0x01 Shift key
992 0x02 Ctrl key
993 0x04 Alt key
994 Should have only 8 valid possibilities. Including zero.
995 */
996 for(CapsState = 0; CapsState < vtwTbl->nModifications; CapsState++)
997 {
998 if(vkPtr->wch[CapsState] == wChar)
999 {
1000 CapsMod = KeyLayout->pCharModifiers->ModNumber[CapsState];
1001 DPRINT("nMod %d wC %04x: CapsMod %08x CapsState %08x MaxModBits %08x\n",
1002 nMod, wChar, CapsMod, CapsState, KeyLayout->pCharModifiers->wMaxModBits);
1003 Ret = ((CapsMod << 8)|(vkPtr->VirtualKey & 0xff));
1004 goto Exit;
1005 }
1006 }
1007 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
1008 }
1009 }
1010 Exit:
1011 UserLeave();
1012 return Ret;
1013 }
1014
1015
1016 /* EOF */