2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
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.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
23 * FILE: subsys/win32k/ntuser/keyboard.c
24 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
26 * 06-06-2001 CSH Created
29 /* INCLUDES ******************************************************************/
36 /* Directory to load key layouts from */
37 #define SYSTEMROOT_DIR L"\\SystemRoot\\System32\\"
39 #define CAPITAL_BIT 0x80000000
40 #define NUMLOCK_BIT 0x40000000
41 #define MOD_BITS_MASK 0x3fffffff
42 #define MOD_KCTRL 0x02
44 #define KS_DOWN_MASK 0xc0
45 #define KS_DOWN_BIT 0x80
46 #define KS_LOCK_BIT 0x01
48 #define LP_EXT_BIT (1<<24)
49 /* From kbdxx.c -- Key changes with numlock */
53 BYTE gQueueKeyStateTable
[256];
56 PKBDRVFILE KBLList
= NULL
; // Keyboard layout list.
59 /* FUNCTIONS *****************************************************************/
61 /* Initialization -- Right now, just zero the key state and init the lock */
62 NTSTATUS FASTCALL
InitKeyboardImpl(VOID
)
64 RtlZeroMemory(&gQueueKeyStateTable
,0x100);
65 return STATUS_SUCCESS
;
68 /*** Statics used by TranslateMessage ***/
70 /*** Shift state code was out of hand, sorry. --- arty */
72 static UINT
DontDistinguishShifts( UINT ret
)
74 if( ret
== VK_LSHIFT
|| ret
== VK_RSHIFT
)
76 if( ret
== VK_LCONTROL
|| ret
== VK_RCONTROL
)
78 if( ret
== VK_LMENU
|| ret
== VK_RMENU
)
83 static VOID STDCALL
SetKeyState(DWORD key
, DWORD vk
, DWORD ext
, BOOL down
)
87 /* Special handling for toggles like numpad and caps lock */
88 if (vk
== VK_CAPITAL
|| vk
== VK_NUMLOCK
)
91 gQueueKeyStateTable
[vk
] ^= KS_LOCK_BIT
;
94 if (ext
&& vk
== VK_LSHIFT
)
96 if (ext
&& vk
== VK_LCONTROL
)
98 if (ext
&& vk
== VK_LMENU
)
102 gQueueKeyStateTable
[vk
] |= KS_DOWN_BIT
;
104 gQueueKeyStateTable
[vk
] &= ~KS_DOWN_MASK
;
106 if (vk
== VK_LSHIFT
|| vk
== VK_RSHIFT
)
108 if ((gQueueKeyStateTable
[VK_LSHIFT
] & KS_DOWN_BIT
) ||
109 (gQueueKeyStateTable
[VK_RSHIFT
] & KS_DOWN_BIT
))
111 gQueueKeyStateTable
[VK_SHIFT
] |= KS_DOWN_BIT
;
115 gQueueKeyStateTable
[VK_SHIFT
] &= ~KS_DOWN_MASK
;
119 if (vk
== VK_LCONTROL
|| vk
== VK_RCONTROL
)
121 if ((gQueueKeyStateTable
[VK_LCONTROL
] & KS_DOWN_BIT
) ||
122 (gQueueKeyStateTable
[VK_RCONTROL
] & KS_DOWN_BIT
))
124 gQueueKeyStateTable
[VK_CONTROL
] |= KS_DOWN_BIT
;
128 gQueueKeyStateTable
[VK_CONTROL
] &= ~KS_DOWN_MASK
;
132 if (vk
== VK_LMENU
|| vk
== VK_RMENU
)
134 if ((gQueueKeyStateTable
[VK_LMENU
] & KS_DOWN_BIT
) ||
135 (gQueueKeyStateTable
[VK_RMENU
] & KS_DOWN_BIT
))
137 gQueueKeyStateTable
[VK_MENU
] |= KS_DOWN_BIT
;
141 gQueueKeyStateTable
[VK_MENU
] &= ~KS_DOWN_MASK
;
146 VOID
DumpKeyState( PBYTE KeyState
)
150 DbgPrint( "KeyState { " );
151 for( i
= 0; i
< 0x100; i
++ )
154 DbgPrint( "%02x(%02x) ", i
, KeyState
[i
] );
159 static BYTE
KeysSet( PKBDTABLES pkKT
, PBYTE KeyState
,
160 int FakeModLeft
, int FakeModRight
)
162 if( !KeyState
|| !pkKT
)
165 /* Search special codes first */
166 if( FakeModLeft
&& KeyState
[FakeModLeft
] )
167 return KeyState
[FakeModLeft
];
168 else if( FakeModRight
&& KeyState
[FakeModRight
] )
169 return KeyState
[FakeModRight
];
174 /* Search the keyboard layout modifiers table for the shift bit. I don't
175 * want to count on the shift bit not moving, because it can be specified
178 static DWORD FASTCALL
GetShiftBit( PKBDTABLES pkKT
, DWORD Vk
)
182 for( i
= 0; pkKT
->pCharModifiers
->pVkToBit
[i
].Vk
; i
++ )
183 if( pkKT
->pCharModifiers
->pVkToBit
[i
].Vk
== Vk
)
184 return pkKT
->pCharModifiers
->pVkToBit
[i
].ModBits
;
189 static DWORD
ModBits( PKBDTABLES pkKT
, PBYTE KeyState
)
196 /* DumpKeyState( KeyState ); */
198 if (KeysSet( pkKT
, KeyState
, VK_LSHIFT
, VK_RSHIFT
) &
200 ModBits
|= GetShiftBit( pkKT
, VK_SHIFT
);
202 if (KeysSet( pkKT
, KeyState
, VK_LCONTROL
, VK_RCONTROL
) &
204 ModBits
|= GetShiftBit( pkKT
, VK_CONTROL
);
206 if (KeysSet( pkKT
, KeyState
, VK_LMENU
, VK_RMENU
) &
208 ModBits
|= GetShiftBit( pkKT
, VK_MENU
);
211 if (KeysSet( pkKT
, KeyState
, VK_RMENU
, 0 ) &
213 ModBits
|= GetShiftBit( pkKT
, VK_CONTROL
);
215 /* Deal with VK_CAPITAL */
216 if (KeysSet( pkKT
, KeyState
, VK_CAPITAL
, 0 ) & KS_LOCK_BIT
)
218 ModBits
|= CAPITAL_BIT
;
221 /* Deal with VK_NUMLOCK */
222 if (KeysSet( pkKT
, KeyState
, VK_NUMLOCK
, 0 ) & KS_LOCK_BIT
)
224 ModBits
|= NUMLOCK_BIT
;
227 DPRINT( "Current Mod Bits: %x\n", ModBits
);
232 static BOOL
TryToTranslateChar(WORD wVirtKey
,
236 PWCHAR pwcTranslatedChar
,
237 PKBDTABLES keyLayout
)
239 PVK_TO_WCHAR_TABLE vtwTbl
;
240 PVK_TO_WCHARS10 vkPtr
;
241 size_t size_this_entry
;
243 DWORD CapsMod
= 0, CapsState
= 0;
245 CapsState
= ModBits
& ~MOD_BITS_MASK
;
246 ModBits
= ModBits
& MOD_BITS_MASK
;
248 DPRINT ( "TryToTranslate: %04x %x\n", wVirtKey
, ModBits
);
250 if (ModBits
> keyLayout
->pCharModifiers
->wMaxModBits
)
254 for (nMod
= 0; keyLayout
->pVkToWcharTable
[nMod
].nModifications
; nMod
++)
256 vtwTbl
= &keyLayout
->pVkToWcharTable
[nMod
];
257 size_this_entry
= vtwTbl
->cbSize
;
258 vkPtr
= (PVK_TO_WCHARS10
)((BYTE
*)vtwTbl
->pVkToWchars
);
259 while(vkPtr
->VirtualKey
)
261 if( wVirtKey
== (vkPtr
->VirtualKey
& 0xff) )
263 CapsMod
= keyLayout
->pCharModifiers
->ModNumber
265 ((CapsState
& CAPITAL_BIT
) ? vkPtr
->Attributes
: 0)];
267 if( CapsMod
>= keyLayout
->pVkToWcharTable
[nMod
].nModifications
)
271 keyLayout
->pVkToWcharTable
[nMod
].nModifications
)
274 CapsMod
&= MaxBit
- 1; /* Guarantee that CapsMod lies
278 *pbDead
= vkPtr
->wch
[CapsMod
] == WCH_DEAD
;
279 *pbLigature
= vkPtr
->wch
[CapsMod
] == WCH_LGTR
;
280 *pwcTranslatedChar
= vkPtr
->wch
[CapsMod
];
282 DPRINT("%d %04x: CapsMod %08x CapsState %08x Char %04x\n",
284 CapsMod
, CapsState
, *pwcTranslatedChar
);
288 vkPtr
= (PVK_TO_WCHARS10
)(((BYTE
*)vkPtr
) + size_this_entry
);
289 if( vkPtr
->VirtualKey
!= 0xff )
291 DPRINT( "Found dead key with no trailer in the table.\n" );
292 DPRINT( "VK: %04x, ADDR: %08x\n", wVirtKey
, (int)vkPtr
);
295 *pwcTranslatedChar
= vkPtr
->wch
[CapsMod
];
299 vkPtr
= (PVK_TO_WCHARS10
)(((BYTE
*)vkPtr
) + size_this_entry
);
307 ToUnicodeInner(UINT wVirtKey
,
315 WCHAR wcTranslatedChar
;
322 if( TryToTranslateChar( wVirtKey
,
323 ModBits( pkKT
, lpKeyState
),
331 DPRINT("Not handling ligature (yet)\n" );
336 pwszBuff
[0] = wcTranslatedChar
;
338 return bDead
? -1 : 1;
345 DWORD FASTCALL
UserGetKeyState(DWORD key
)
351 ret
= ((DWORD
)(gQueueKeyStateTable
[key
] & KS_DOWN_BIT
) << 8 ) |
352 (gQueueKeyStateTable
[key
] & KS_LOCK_BIT
);
364 DECLARE_RETURN(DWORD
);
366 DPRINT("Enter NtUserGetKeyState\n");
367 UserEnterExclusive();
369 RETURN(UserGetKeyState(key
));
372 DPRINT("Leave NtUserGetKeyState, ret=%i\n",_ret_
);
379 DWORD FASTCALL
UserGetAsyncKeyState(DWORD key
)
385 ret
= ((DWORD
)(gQueueKeyStateTable
[key
] & KS_DOWN_BIT
) << 8 ) |
386 (gQueueKeyStateTable
[key
] & KS_LOCK_BIT
);
396 NtUserGetAsyncKeyState(
399 DECLARE_RETURN(DWORD
);
401 DPRINT("Enter NtUserGetAsyncKeyState\n");
402 UserEnterExclusive();
404 RETURN(UserGetAsyncKeyState(key
));
407 DPRINT("Leave NtUserGetAsyncKeyState, ret=%i\n",_ret_
);
414 int STDCALL
ToUnicodeEx( UINT wVirtKey
,
422 int ToUnicodeResult
= 0;
424 if (0 == (lpKeyState
[wVirtKey
] & KS_DOWN_BIT
))
430 ToUnicodeResult
= ToUnicodeInner( wVirtKey
,
436 PsGetCurrentThreadWin32Thread() ?
437 PsGetCurrentThreadWin32Thread()->KeyboardLayout
: 0 );
440 return ToUnicodeResult
;
443 int STDCALL
ToUnicode( UINT wVirtKey
,
450 return ToUnicodeEx( wVirtKey
,
460 * Utility to copy and append two unicode strings.
462 * IN OUT PUNICODE_STRING ResultFirst -> First string and result
463 * IN PUNICODE_STRING Second -> Second string to append
464 * IN BOOL Deallocate -> TRUE: Deallocate First string before
470 NTSTATUS NTAPI
AppendUnicodeString(PUNICODE_STRING ResultFirst
,
471 PUNICODE_STRING Second
,
476 ExAllocatePoolWithTag(PagedPool
,
477 (ResultFirst
->Length
+ Second
->Length
+ sizeof(WCHAR
)),
481 return STATUS_NO_MEMORY
;
483 memcpy( new_string
, ResultFirst
->Buffer
,
484 ResultFirst
->Length
);
485 memcpy( new_string
+ ResultFirst
->Length
/ sizeof(WCHAR
),
489 RtlFreeUnicodeString(ResultFirst
);
490 ResultFirst
->Length
+= Second
->Length
;
491 ResultFirst
->MaximumLength
= ResultFirst
->Length
;
492 new_string
[ResultFirst
->Length
/ sizeof(WCHAR
)] = 0;
493 Status
= RtlCreateUnicodeString(ResultFirst
,new_string
) ?
494 STATUS_SUCCESS
: STATUS_NO_MEMORY
;
495 ExFreePool(new_string
);
500 * Utility function to read a value from the registry more easily.
502 * IN PUNICODE_STRING KeyName -> Name of key to open
503 * IN PUNICODE_STRING ValueName -> Name of value to open
504 * OUT PUNICODE_STRING ReturnedValue -> String contained in registry
509 static NTSTATUS NTAPI
ReadRegistryValue( PUNICODE_STRING KeyName
,
510 PUNICODE_STRING ValueName
,
511 PUNICODE_STRING ReturnedValue
)
515 OBJECT_ATTRIBUTES KeyAttributes
;
516 PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo
;
521 InitializeObjectAttributes(&KeyAttributes
, KeyName
, OBJ_CASE_INSENSITIVE
,
523 Status
= ZwOpenKey(&KeyHandle
, KEY_ALL_ACCESS
, &KeyAttributes
);
524 if( !NT_SUCCESS(Status
) )
529 Status
= ZwQueryValueKey(KeyHandle
, ValueName
, KeyValuePartialInformation
,
534 if( Status
!= STATUS_BUFFER_TOO_SMALL
)
540 ResLength
+= sizeof( *KeyValuePartialInfo
);
541 KeyValuePartialInfo
=
542 ExAllocatePoolWithTag(PagedPool
, ResLength
, TAG_STRING
);
545 if( !KeyValuePartialInfo
)
548 return STATUS_NO_MEMORY
;
551 Status
= ZwQueryValueKey(KeyHandle
, ValueName
, KeyValuePartialInformation
,
552 (PVOID
)KeyValuePartialInfo
,
556 if( !NT_SUCCESS(Status
) )
559 ExFreePool(KeyValuePartialInfo
);
563 Temp
.Length
= Temp
.MaximumLength
= KeyValuePartialInfo
->DataLength
;
564 Temp
.Buffer
= (PWCHAR
)KeyValuePartialInfo
->Data
;
566 /* At this point, KeyValuePartialInfo->Data contains the key data */
567 RtlInitUnicodeString(ReturnedValue
,L
"");
568 AppendUnicodeString(ReturnedValue
,&Temp
,FALSE
);
570 ExFreePool(KeyValuePartialInfo
);
576 typedef PVOID (*KbdLayerDescriptor
)(VOID
);
577 NTSTATUS STDCALL
LdrGetProcedureAddress(PVOID module
,
578 PANSI_STRING import_name
,
582 void InitKbdLayout( PVOID
*pkKeyboardLayout
)
584 WCHAR LocaleBuffer
[16];
585 UNICODE_STRING LayoutKeyName
;
586 UNICODE_STRING LayoutValueName
;
587 UNICODE_STRING DefaultLocale
;
588 UNICODE_STRING LayoutFile
;
589 UNICODE_STRING FullLayoutPath
;
591 PWCHAR KeyboardLayoutWSTR
;
592 HMODULE kbModule
= 0;
594 ANSI_STRING kbdProcedureName
;
595 KbdLayerDescriptor layerDescGetFn
;
597 #define XX_STATUS(x) if (!NT_SUCCESS(Status = (x))) continue;
601 Status
= ZwQueryDefaultLocale(FALSE
, &LocaleId
);
602 if (!NT_SUCCESS(Status
))
604 DPRINT1("Could not get default locale (%08lx).\n", Status
);
608 DPRINT("DefaultLocale = %lx\n", LocaleId
);
609 swprintf(LocaleBuffer
, L
"%08lx", LocaleId
);
610 DPRINT("DefaultLocale = %S\n", LocaleBuffer
);
611 RtlInitUnicodeString(&DefaultLocale
, LocaleBuffer
);
613 RtlInitUnicodeString(&LayoutKeyName
,
614 L
"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet"
615 L
"\\Control\\KeyboardLayouts\\");
617 AppendUnicodeString(&LayoutKeyName
,&DefaultLocale
,FALSE
);
619 RtlInitUnicodeString(&LayoutValueName
,L
"Layout File");
621 Status
= ReadRegistryValue(&LayoutKeyName
,&LayoutValueName
,&LayoutFile
);
623 RtlFreeUnicodeString(&LayoutKeyName
);
625 if( !NT_SUCCESS(Status
) )
627 DPRINT1("Got default locale but not layout file. (%08lx)\n",
632 DPRINT("Read registry and got %wZ\n", &LayoutFile
);
635 RtlInitUnicodeString(&FullLayoutPath
,SYSTEMROOT_DIR
);
636 AppendUnicodeString(&FullLayoutPath
,&LayoutFile
,FALSE
);
638 DPRINT("Loading Keyboard DLL %wZ\n", &FullLayoutPath
);
640 RtlFreeUnicodeString(&LayoutFile
);
643 ExAllocatePoolWithTag(PagedPool
,
644 FullLayoutPath
.Length
+ sizeof(WCHAR
),
647 if( !KeyboardLayoutWSTR
)
649 DPRINT1("Couldn't allocate a string for the keyboard layout name.\n");
650 RtlFreeUnicodeString(&FullLayoutPath
);
653 memcpy(KeyboardLayoutWSTR
,FullLayoutPath
.Buffer
,
654 FullLayoutPath
.Length
);
655 KeyboardLayoutWSTR
[FullLayoutPath
.Length
/ sizeof(WCHAR
)] = 0;
657 kbModule
= EngLoadImage(KeyboardLayoutWSTR
);
658 DPRINT( "Load Keyboard Layout: %S\n", KeyboardLayoutWSTR
);
661 DPRINT1( "Load Keyboard Layout: No %wZ\n", &FullLayoutPath
);
663 ExFreePool(KeyboardLayoutWSTR
);
664 RtlFreeUnicodeString(&FullLayoutPath
);
670 DPRINT1("Trying to load US Keyboard Layout\n");
671 kbModule
= EngLoadImage(L
"\\SystemRoot\\system32\\kbdus.dll");
675 DPRINT1("Failed to load any Keyboard Layout\n");
680 RtlInitAnsiString( &kbdProcedureName
, "KbdLayerDescriptor" );
682 LdrGetProcedureAddress((PVOID
)kbModule
,
685 (PVOID
*)&layerDescGetFn
);
689 *pkKeyboardLayout
= layerDescGetFn();
694 if( !*pkKeyboardLayout
)
696 DPRINT1("Failed to load the keyboard layout.\n");
702 PKBDTABLES
W32kGetDefaultKeyLayout(VOID
)
704 PKBDTABLES pkKeyboardLayout
= 0;
705 InitKbdLayout( (PVOID
) &pkKeyboardLayout
);
706 return pkKeyboardLayout
;
710 IntTranslateKbdMessage(LPMSG lpMsg
,
713 static INT dead_char
= 0;
717 PKBDTABLES keyLayout
;
722 keyLayout
= PsGetCurrentThreadWin32Thread()->KeyboardLayout
;
726 if (lpMsg
->message
!= WM_KEYDOWN
&& lpMsg
->message
!= WM_SYSKEYDOWN
)
729 ScanCode
= (lpMsg
->lParam
>> 16) & 0xff;
731 /* All messages have to contain the cursor point. */
732 IntGetCursorLocation(PsGetCurrentThreadWin32Thread()->Desktop
->WindowStation
,
735 UState
= ToUnicodeInner(lpMsg
->wParam
, HIWORD(lpMsg
->lParam
) & 0xff,
736 gQueueKeyStateTable
, wp
, 2, 0,
741 NewMsg
.message
= (lpMsg
->message
== WM_KEYDOWN
) ? WM_CHAR
: WM_SYSCHAR
;
746 DPRINT("PREVIOUS DEAD CHAR: %c\n", dead_char
);
748 for( i
= 0; keyLayout
->pDeadKey
[i
].dwBoth
; i
++ )
750 first
= keyLayout
->pDeadKey
[i
].dwBoth
>> 16;
751 second
= keyLayout
->pDeadKey
[i
].dwBoth
;
752 if (first
== dead_char
&& second
== wp
[0])
754 wp
[0] = keyLayout
->pDeadKey
[i
].wchComposed
;
760 DPRINT("FINAL CHAR: %c\n", wp
[0]);
765 NewMsg
.hwnd
= lpMsg
->hwnd
;
766 NewMsg
.wParam
= dead_char
;
767 NewMsg
.lParam
= lpMsg
->lParam
;
769 MsqPostMessage(PsGetCurrentThreadWin32Thread()->MessageQueue
, &NewMsg
, FALSE
, QS_KEY
);
772 NewMsg
.hwnd
= lpMsg
->hwnd
;
773 NewMsg
.wParam
= wp
[0];
774 NewMsg
.lParam
= lpMsg
->lParam
;
775 DPRINT( "CHAR='%c' %04x %08x\n", wp
[0], wp
[0], lpMsg
->lParam
);
776 MsqPostMessage(PsGetCurrentThreadWin32Thread()->MessageQueue
, &NewMsg
, FALSE
, QS_KEY
);
779 else if (UState
== -1)
782 (lpMsg
->message
== WM_KEYDOWN
) ? WM_DEADCHAR
: WM_SYSDEADCHAR
;
783 NewMsg
.hwnd
= lpMsg
->hwnd
;
784 NewMsg
.wParam
= wp
[0];
785 NewMsg
.lParam
= lpMsg
->lParam
;
787 MsqPostMessage(PsGetCurrentThreadWin32Thread()->MessageQueue
, &NewMsg
, FALSE
, QS_KEY
);
796 NtUserGetKeyboardState(
800 DECLARE_RETURN(DWORD
);
802 DPRINT("Enter NtUserGetKeyboardState\n");
807 if(!NT_SUCCESS(MmCopyToCaller(lpKeyState
, gQueueKeyStateTable
, 256)))
814 DPRINT("Leave NtUserGetKeyboardState, ret=%i\n",_ret_
);
821 NtUserSetKeyboardState(LPBYTE lpKeyState
)
824 DECLARE_RETURN(DWORD
);
826 DPRINT("Enter NtUserSetKeyboardState\n");
827 UserEnterExclusive();
831 if(! NT_SUCCESS(MmCopyFromCaller(gQueueKeyStateTable
, lpKeyState
, 256)))
838 DPRINT("Leave NtUserSetKeyboardState, ret=%i\n",_ret_
);
843 static UINT
VkToScan( UINT Code
, BOOL ExtCode
, PKBDTABLES pkKT
)
847 for( i
= 0; i
< pkKT
->bMaxVSCtoVK
; i
++ )
849 if( pkKT
->pusVSCtoVK
[i
] == Code
)
858 UINT
ScanToVk( UINT Code
, BOOL ExtKey
, PKBDTABLES pkKT
)
862 DPRINT("ScanToVk: No layout\n");
870 for( i
= 0; pkKT
->pVSCtoVK_E0
[i
].Vsc
; i
++ )
872 if( pkKT
->pVSCtoVK_E0
[i
].Vsc
== Code
)
873 return pkKT
->pVSCtoVK_E0
[i
].Vk
& 0xff;
875 for( i
= 0; pkKT
->pVSCtoVK_E1
[i
].Vsc
; i
++ )
877 if( pkKT
->pVSCtoVK_E1
[i
].Vsc
== Code
)
878 return pkKT
->pVSCtoVK_E1
[i
].Vk
& 0xff;
885 if( Code
>= pkKT
->bMaxVSCtoVK
)
889 return pkKT
->pusVSCtoVK
[Code
] & 0xff;
894 * Map a virtual key code, or virtual scan code, to a scan code, key code,
895 * or unshifted unicode character.
899 * 0 -- Code is a virtual key code that is converted into a virtual scan code
900 * that does not distinguish between left and right shift keys.
901 * 1 -- Code is a virtual scan code that is converted into a virtual key code
902 * that does not distinguish between left and right shift keys.
903 * 2 -- Code is a virtual key code that is converted into an unshifted unicode
905 * 3 -- Code is a virtual scan code that is converted into a virtual key code
906 * that distinguishes left and right shift keys.
907 * KeyLayout: Keyboard layout handle (currently, unused)
912 static UINT
IntMapVirtualKeyEx( UINT Code
, UINT Type
, PKBDTABLES keyLayout
)
919 if( Code
== VK_RSHIFT
)
921 if( Code
== VK_RMENU
)
923 if( Code
== VK_RCONTROL
)
925 ret
= VkToScan( Code
, FALSE
, keyLayout
);
930 DontDistinguishShifts
931 (IntMapVirtualKeyEx( Code
, 3, keyLayout
) );
938 ret
= VkToScan( Code
, FALSE
, keyLayout
);
939 ToUnicodeInner( Code
, ret
, 0, wp
, 2, 0, keyLayout
);
946 ret
= ScanToVk( Code
, FALSE
, keyLayout
);
955 NtUserMapVirtualKeyEx( UINT Code
, UINT Type
, DWORD keyboardId
, HKL dwhkl
)
957 PKBDTABLES keyLayout
;
958 DECLARE_RETURN(UINT
);
960 DPRINT("Enter NtUserMapVirtualKeyEx\n");
961 UserEnterExclusive();
963 keyLayout
= PsGetCurrentThreadWin32Thread() ? PsGetCurrentThreadWin32Thread()->KeyboardLayout
: 0;
968 RETURN(IntMapVirtualKeyEx( Code
, Type
, keyLayout
));
971 DPRINT("Leave NtUserMapVirtualKeyEx, ret=%i\n",_ret_
);
988 BYTE KeyStateBuf
[0x100];
989 PWCHAR OutPwszBuff
= 0;
993 DPRINT("Enter NtUserSetKeyboardState\n");
994 UserEnterShared();//faxme: this syscall doesnt seem to need any locking...
997 if( !NT_SUCCESS(MmCopyFromCaller(KeyStateBuf
,
999 sizeof(KeyStateBuf
))) )
1001 DPRINT1( "Couldn't copy key state from caller.\n" );
1004 OutPwszBuff
= ExAllocatePoolWithTag(NonPagedPool
,sizeof(WCHAR
) * cchBuff
, TAG_STRING
);
1007 DPRINT1( "ExAllocatePool(%d) failed\n", sizeof(WCHAR
) * cchBuff
);
1010 RtlZeroMemory( OutPwszBuff
, sizeof( WCHAR
) * cchBuff
);
1012 ret
= ToUnicodeEx( wVirtKey
,
1020 MmCopyToCaller(pwszBuff
,OutPwszBuff
,sizeof(WCHAR
)*cchBuff
);
1021 ExFreePool(OutPwszBuff
);
1026 DPRINT("Leave NtUserSetKeyboardState, ret=%i\n",_ret_
);
1031 static int W32kSimpleToupper( int ch
)
1033 if( ch
>= 'a' && ch
<= 'z' )
1034 ch
= ch
- 'a' + 'A';
1040 NtUserGetKeyNameText( LONG lParam
, LPWSTR lpString
, int nSize
)
1046 UINT ScanCode
= (lParam
>> 16) & 0xff;
1047 BOOL ExtKey
= lParam
& (1<<24) ? TRUE
: FALSE
;
1048 PKBDTABLES keyLayout
;
1049 VSC_LPWSTR
*KeyNames
;
1050 DECLARE_RETURN(DWORD
);
1052 DPRINT("Enter NtUserGetKeyNameText\n");
1055 keyLayout
= PsGetCurrentThreadWin32Thread() ?
1056 PsGetCurrentThreadWin32Thread()->KeyboardLayout
: 0;
1058 if( !keyLayout
|| nSize
< 1 )
1061 if( lParam
& (1<<25) )
1063 CareVk
= VkCode
= ScanToVk( ScanCode
, ExtKey
, keyLayout
);
1064 if( VkCode
== VK_LSHIFT
|| VkCode
== VK_RSHIFT
)
1066 if( VkCode
== VK_LCONTROL
|| VkCode
== VK_RCONTROL
)
1067 VkCode
= VK_LCONTROL
;
1068 if( VkCode
== VK_LMENU
|| VkCode
== VK_RMENU
)
1073 VkCode
= ScanToVk( ScanCode
, ExtKey
, keyLayout
);
1078 if( CareVk
!= VkCode
)
1079 ScanCode
= VkToScan( VkCode
, ExtKey
, keyLayout
);
1082 KeyNames
= keyLayout
->pKeyNamesExt
;
1084 KeyNames
= keyLayout
->pKeyNames
;
1086 for( i
= 0; KeyNames
[i
].pwsz
; i
++ )
1088 if( KeyNames
[i
].vsc
== ScanCode
)
1090 UINT StrLen
= wcslen(KeyNames
[i
].pwsz
);
1091 UINT StrMax
= StrLen
> (nSize
- 1) ? (nSize
- 1) : StrLen
;
1093 if( NT_SUCCESS( MmCopyToCaller( lpString
,
1095 StrMax
* sizeof(WCHAR
) ) ) &&
1096 NT_SUCCESS( MmCopyToCaller( lpString
+ StrMax
,
1098 sizeof( WCHAR
) ) ) )
1110 UCName
[0] = W32kSimpleToupper(IntMapVirtualKeyEx( VkCode
, 2, keyLayout
));
1114 if( !NT_SUCCESS(MmCopyToCaller( lpString
, UCName
, 2 * sizeof(WCHAR
) )) )
1121 DPRINT("Leave NtUserGetKeyNameText, ret=%i\n",_ret_
);
1127 * Filter this message according to the current key layout, setting wParam
1132 W32kKeyProcessMessage(LPMSG Msg
,
1133 PKBDTABLES KeyboardLayout
,
1136 DWORD ScanCode
= 0, ModifierBits
= 0;
1138 DWORD BaseMapping
= 0;
1140 static WORD NumpadConversion
[][2] =
1141 { { VK_DELETE
, VK_DECIMAL
},
1142 { VK_INSERT
, VK_NUMPAD0
},
1143 { VK_END
, VK_NUMPAD1
},
1144 { VK_DOWN
, VK_NUMPAD2
},
1145 { VK_NEXT
, VK_NUMPAD3
},
1146 { VK_LEFT
, VK_NUMPAD4
},
1147 { VK_CLEAR
, VK_NUMPAD5
},
1148 { VK_RIGHT
, VK_NUMPAD6
},
1149 { VK_HOME
, VK_NUMPAD7
},
1150 { VK_UP
, VK_NUMPAD8
},
1151 { VK_PRIOR
, VK_NUMPAD9
},
1153 PVSC_VK VscVkTable
= NULL
;
1155 if( !KeyboardLayout
|| !Msg
||
1156 (Msg
->message
!= WM_KEYDOWN
&& Msg
->message
!= WM_SYSKEYDOWN
&&
1157 Msg
->message
!= WM_KEYUP
&& Msg
->message
!= WM_SYSKEYUP
) )
1162 /* arty -- handle numpad -- On real windows, the actual key produced
1163 * by the messaging layer is different based on the state of numlock. */
1164 ModifierBits
= ModBits(KeyboardLayout
,gQueueKeyStateTable
);
1166 /* Get the raw scan code, so we can look up whether the key is a numpad
1169 * Shift and the LP_EXT_BIT cancel. */
1170 ScanCode
= (Msg
->lParam
>> 16) & 0xff;
1171 BaseMapping
= Msg
->wParam
=
1172 IntMapVirtualKeyEx( ScanCode
, 1, KeyboardLayout
);
1175 if( ScanCode
>= KeyboardLayout
->bMaxVSCtoVK
)
1178 RawVk
= KeyboardLayout
->pusVSCtoVK
[ScanCode
];
1182 if( Prefix
== 0xE0 )
1184 /* ignore shift codes */
1185 if( ScanCode
== 0x2A || ScanCode
== 0x36 )
1189 VscVkTable
= KeyboardLayout
->pVSCtoVK_E0
;
1191 else if( Prefix
== 0xE1 )
1193 VscVkTable
= KeyboardLayout
->pVSCtoVK_E1
;
1197 while (VscVkTable
->Vsc
)
1199 if( VscVkTable
->Vsc
== ScanCode
)
1201 RawVk
= VscVkTable
->Vk
;
1207 if ((ModifierBits
& NUMLOCK_BIT
) &&
1208 !(ModifierBits
& GetShiftBit(KeyboardLayout
, VK_SHIFT
)) &&
1210 !(Msg
->lParam
& LP_EXT_BIT
))
1212 /* The key in question is a numpad key. Search for a translation. */
1213 for (i
= 0; NumpadConversion
[i
][0]; i
++)
1215 if ((BaseMapping
& 0xff) == NumpadConversion
[i
][0]) /* RawVk? */
1217 Msg
->wParam
= NumpadConversion
[i
][1];
1223 DPRINT("Key: [%04x -> %04x]\n", BaseMapping
, Msg
->wParam
);
1225 /* Now that we have the VK, we can set the keymap appropriately
1226 * This is a better place for this code, as it's guaranteed to be
1227 * run, unlike translate message. */
1228 if (Msg
->message
== WM_KEYDOWN
|| Msg
->message
== WM_SYSKEYDOWN
)
1230 SetKeyState( ScanCode
, Msg
->wParam
, Msg
->lParam
& LP_EXT_BIT
,
1231 TRUE
); /* Strike key */
1233 else if (Msg
->message
== WM_KEYUP
|| Msg
->message
== WM_SYSKEYUP
)
1235 SetKeyState( ScanCode
, Msg
->wParam
, Msg
->lParam
& LP_EXT_BIT
,
1236 FALSE
); /* Release key */
1239 /* We need to unset SYSKEYDOWN if the ALT key is an ALT+Gr */
1240 if( gQueueKeyStateTable
[VK_RMENU
] & KS_DOWN_BIT
)
1242 if( Msg
->message
== WM_SYSKEYDOWN
)
1243 Msg
->message
= WM_KEYDOWN
;
1245 Msg
->message
= WM_KEYUP
;
1252 NtUserGetKeyboardLayoutList(
1263 NtUserGetKeyboardLayoutName(
1272 UserGetKeyboardLayout(
1277 PW32THREAD W32Thread
;
1281 W32Thread
= PsGetCurrentThreadWin32Thread();
1284 Status
= PsLookupThreadByThreadId((HANDLE
)dwThreadId
, &Thread
);//fixme: deref thread
1285 if(!NT_SUCCESS(Status
))
1287 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
1290 W32Thread
= Thread
->Tcb
.Win32Thread
; /* Wrong, but returning the pointer to
1293 layout
= W32Thread
->KeyboardLayout
;
1302 NtUserGetKeyboardLayout(
1305 DECLARE_RETURN(HKL
);
1308 DPRINT("Enter NtUserGetKeyboardLayout\n");
1310 RETURN( UserGetKeyboardLayout(dwThreadId
));
1313 DPRINT("Leave NtUserGetKeyboardLayout, ret=%i\n",_ret_
);
1320 UserGetKeyboardType(
1325 case 0: /* Keyboard type */
1326 return 4; /* AT-101 */
1327 case 1: /* Keyboard Subtype */
1328 return 0; /* There are no defined subtypes */
1329 case 2: /* Number of F-keys */
1330 return 12; /* We're doing an 101 for now, so return 12 F-keys */
1332 DPRINT1("Unknown type!\n");
1333 return 0; /* The book says 0 here, so 0 */
1340 NtUserLoadKeyboardLayoutEx(
1344 IN PUNICODE_STRING puszKLID
,
1345 IN UINT KLayoutLangID
,
1355 Based on TryToTranslateChar, instead of processing VirtualKey match,
1356 look for wChar match.
1362 DWORD KeyboardLayout
,
1365 /* FAXME: currently, this routine doesnt seem to need any locking */
1367 PKBDTABLES KeyLayout
;
1368 PVK_TO_WCHAR_TABLE vtwTbl
;
1369 PVK_TO_WCHARS10 vkPtr
;
1370 size_t size_this_entry
;
1372 DWORD CapsMod
= 0, CapsState
= 0;
1376 KeyLayout
= (PKBDTABLES
) KeyboardLayout
;
1378 for (nMod
= 0; KeyLayout
->pVkToWcharTable
[nMod
].nModifications
; nMod
++)
1380 vtwTbl
= &KeyLayout
->pVkToWcharTable
[nMod
];
1381 size_this_entry
= vtwTbl
->cbSize
;
1382 vkPtr
= (PVK_TO_WCHARS10
)((BYTE
*)vtwTbl
->pVkToWchars
);
1384 while(vkPtr
->VirtualKey
)
1390 Should have only 8 valid possibilities. Including zero.
1392 for(CapsState
= 0; CapsState
< vtwTbl
->nModifications
; CapsState
++)
1394 if(vkPtr
->wch
[CapsState
] == wChar
)
1396 CapsMod
= KeyLayout
->pCharModifiers
->ModNumber
[CapsState
];
1397 DPRINT("nMod %d wC %04x: CapsMod %08x CapsState %08x MaxModBits %08x\n",
1398 nMod
, wChar
, CapsMod
, CapsState
, KeyLayout
->pCharModifiers
->wMaxModBits
);
1399 return ((CapsMod
<< 8)|(vkPtr
->VirtualKey
& 0xff));
1402 vkPtr
= (PVK_TO_WCHARS10
)(((BYTE
*)vkPtr
) + size_this_entry
);