Fixed bug 167.
[reactos.git] / reactos / subsys / 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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: keyboard.c,v 1.22 2004/02/19 03:45:44 arty Exp $
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Messages
24 * FILE: subsys/win32k/ntuser/keyboard.c
25 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
26 * REVISION HISTORY:
27 * 06-06-2001 CSH Created
28 */
29
30 /* INCLUDES ******************************************************************/
31
32 #include <ddk/ntddk.h>
33 #include <win32k/win32k.h>
34 #include <internal/safe.h>
35 #include <internal/kbd.h>
36 #include <include/guicheck.h>
37 #include <include/msgqueue.h>
38 #include <include/window.h>
39 #include <include/class.h>
40 #include <include/error.h>
41 #include <include/object.h>
42 #include <include/winsta.h>
43 #include <rosrtl/string.h>
44
45 #define NDEBUG
46 #include <debug.h>
47
48 /* Directory to load key layouts from */
49 #define SYSTEMROOT_DIR L"\\SystemRoot\\System32\\"
50 /* Lock modifiers */
51 #define CAPITAL_BIT 0x80000000
52 #define NUMLOCK_BIT 0x40000000
53 #define MOD_BITS_MASK 0x3fffffff
54 #define MOD_KCTRL 0x02
55 /* Key States */
56 #define KS_DOWN_MASK 0xc0
57 #define KS_DOWN_BIT 0x80
58 #define KS_EXT_BIT 0x40
59 #define KS_LOCK_BIT 0x01
60 /* lParam bits */
61 #define LP_EXT_BIT (1<<24)
62 /* From kbdxx.c -- Key changes with numlock */
63 #define KNUMP 0x400
64
65 /* Lock the keyboard state to prevent unusual concurrent access */
66 FAST_MUTEX QueueStateLock;
67
68 BYTE QueueKeyStateTable[256];
69
70 /* FUNCTIONS *****************************************************************/
71
72 /* Initialization -- Right now, just zero the key state and init the lock */
73 NTSTATUS FASTCALL InitKeyboardImpl(VOID) {
74 ExInitializeFastMutex(&QueueStateLock);
75 RtlZeroMemory(&QueueKeyStateTable,0x100);
76 return STATUS_SUCCESS;
77 }
78
79 /*** Statics used by TranslateMessage ***/
80
81 /*** Shift state code needs to be cleaned up here. Sorry, I let it get out
82 * of hand. */
83
84 static UINT DontDistinguishShifts( UINT ret ) {
85 if( ret == VK_LSHIFT || ret == VK_RSHIFT ) ret = VK_SHIFT;
86 if( ret == VK_LCONTROL || ret == VK_RCONTROL ) ret = VK_CONTROL;
87 if( ret == VK_LMENU || ret == VK_RMENU ) ret = VK_MENU;
88 return ret;
89 }
90
91 static VOID STDCALL SetKeyState(DWORD key, DWORD vk, DWORD ext, BOOL down) {
92 /* Special handling for toggles like numpad and caps lock */
93 if (vk == VK_CAPITAL || vk == VK_NUMLOCK)
94 {
95 if (down) QueueKeyStateTable[key] ^= 1;
96 }
97
98 if (down)
99 QueueKeyStateTable[key] |= KS_DOWN_BIT | (ext ? KS_EXT_BIT : 0);
100 else
101 QueueKeyStateTable[key] &= ~KS_DOWN_MASK;
102 }
103
104 VOID DumpKeyState( PBYTE KeyState ) {
105 int i;
106
107 DbgPrint( "KeyState { " );
108 for( i = 0; i < 0x100; i++ ) {
109 if( KeyState[i] ) DbgPrint( "%02x(%02x) ", i, KeyState[i] );
110 }
111 DbgPrint( "};\n" );
112 }
113
114 static BYTE KeysSet( PKBDTABLES pkKT, PBYTE KeyState,
115 int Mod, int FakeModLeft, int FakeModRight ) {
116 int i;
117 UINT Vk = 0;
118
119 if( !KeyState || !pkKT ) return 0;
120
121 for( i = 0; i < pkKT->bMaxVSCtoVK; i++ ) {
122 Vk = pkKT->pusVSCtoVK[i] & 0xff;
123 if( KeyState[i] &&
124 ((Vk == Mod) ||
125 (FakeModLeft && Vk == FakeModLeft) ||
126 (FakeModRight && Vk == FakeModRight)) ) {
127 return KeyState[i];
128 }
129 }
130
131 return 0;
132 }
133
134 static DWORD ModBits( PKBDTABLES pkKT, PBYTE KeyState ) {
135 int i;
136 DWORD ModBits = 0;
137 BYTE Mask;
138
139 if( !KeyState ) return 0;
140
141 /* DumpKeyState( KeyState ); */
142
143 for( i = 0; pkKT->pCharModifiers->pVkToBit[i].Vk; i++ ) {
144 int Vk = pkKT->pCharModifiers->pVkToBit[i].Vk;
145 switch(Vk)
146 {
147 case VK_SHIFT:
148 Mask = KeysSet( pkKT, KeyState, Vk, VK_LSHIFT, VK_RSHIFT );
149 if (Mask & KS_DOWN_MASK)
150 ModBits |= pkKT->pCharModifiers->pVkToBit[i].ModBits;
151 break;
152 case VK_CONTROL:
153 Mask = KeysSet( pkKT, KeyState, Vk, VK_LCONTROL, VK_RCONTROL );
154 if (Mask & KS_DOWN_MASK)
155 ModBits |= pkKT->pCharModifiers->pVkToBit[i].ModBits;
156 break;
157 case VK_MENU:
158 Mask = KeysSet( pkKT, KeyState, Vk, VK_LMENU, VK_RMENU );
159 if (Mask & KS_DOWN_MASK)
160 ModBits |= pkKT->pCharModifiers->pVkToBit[i].ModBits;
161 if (Mask & KS_EXT_BIT)
162 ModBits |= MOD_KCTRL;
163 break;
164 default:
165 Mask = KeysSet( pkKT, KeyState, Vk, 0, 0 );
166 if (Mask & KS_DOWN_BIT)
167 ModBits |= pkKT->pCharModifiers->pVkToBit[i].ModBits;
168 break;
169 }
170 }
171
172 /* Deal with VK_CAPITAL */
173 if (KeysSet( pkKT, KeyState, VK_CAPITAL, 0, 0 ) & KS_LOCK_BIT)
174 {
175 ModBits |= CAPITAL_BIT;
176 }
177
178 /* Deal with VK_NUMLOCK */
179 if (KeysSet( pkKT, KeyState, VK_NUMLOCK, 0, 0 ) & KS_LOCK_BIT)
180 {
181 ModBits |= NUMLOCK_BIT;
182 }
183
184 DPRINT( "Current Mod Bits: %x\n", ModBits );
185
186 return ModBits;
187 }
188
189 static BOOL TryToTranslateChar(WORD wVirtKey,
190 DWORD ModBits,
191 PBOOL pbDead,
192 PBOOL pbLigature,
193 PWCHAR pwcTranslatedChar,
194 PKBDTABLES keyLayout )
195 {
196 PVK_TO_WCHAR_TABLE vtwTbl;
197 PVK_TO_WCHARS10 vkPtr;
198 size_t size_this_entry;
199 int nMod, shift;
200 DWORD CapsMod = 0, CapsState = 0;
201
202 CapsState = ModBits & ~MOD_BITS_MASK;
203 ModBits = ModBits & MOD_BITS_MASK;
204
205 DPRINT ( "TryToTranslate: %04x %x\n", wVirtKey, ModBits );
206
207 if (ModBits > keyLayout->pCharModifiers->wMaxModBits)
208 {
209 return FALSE;
210 }
211 shift = keyLayout->pCharModifiers->ModNumber[ModBits];
212
213 for (nMod = 0; keyLayout->pVkToWcharTable[nMod].nModifications; nMod++)
214 {
215 if (shift > keyLayout->pVkToWcharTable[nMod].nModifications)
216 {
217 continue;
218 }
219 vtwTbl = &keyLayout->pVkToWcharTable[nMod];
220 size_this_entry = vtwTbl->cbSize;
221 vkPtr = (PVK_TO_WCHARS10)((BYTE *)vtwTbl->pVkToWchars);
222 while(vkPtr->VirtualKey)
223 {
224 if( wVirtKey == (vkPtr->VirtualKey & 0xff) )
225 {
226 CapsMod =
227 shift | ((CapsState & CAPITAL_BIT) ? vkPtr->Attributes : 0);
228
229 *pbDead = vkPtr->wch[CapsMod] == WCH_DEAD;
230 *pbLigature = vkPtr->wch[CapsMod] == WCH_LGTR;
231 *pwcTranslatedChar = vkPtr->wch[CapsMod];
232
233 DPRINT("CapsMod %08x CapsState %08x shift %08x Char %04x\n",
234 CapsMod, CapsState, shift, *pwcTranslatedChar);
235
236 if( *pbDead )
237 {
238 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
239 if( vkPtr->VirtualKey != 0xff )
240 {
241 DPRINT( "Found dead key with no trailer in the table.\n" );
242 DPRINT( "VK: %04x, ADDR: %08x\n", wVirtKey, (int)vkPtr );
243 return FALSE;
244 }
245 *pwcTranslatedChar = vkPtr->wch[shift];
246 }
247 return TRUE;
248 }
249 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
250 }
251 }
252 return FALSE;
253 }
254
255 static
256 int STDCALL
257 ToUnicodeInner(UINT wVirtKey,
258 UINT wScanCode,
259 PBYTE lpKeyState,
260 LPWSTR pwszBuff,
261 int cchBuff,
262 UINT wFlags,
263 PKBDTABLES pkKT)
264 {
265 WCHAR wcTranslatedChar;
266 BOOL bDead;
267 BOOL bLigature;
268
269 if( !pkKT ) return 0;
270
271 if( TryToTranslateChar( wVirtKey,
272 ModBits( pkKT, lpKeyState ),
273 &bDead,
274 &bLigature,
275 &wcTranslatedChar,
276 pkKT ) )
277 {
278 if( bLigature )
279 {
280 DPRINT("Not handling ligature (yet)\n" );
281 return 0;
282 }
283
284 if( cchBuff > 0 ) pwszBuff[0] = wcTranslatedChar;
285
286 return bDead ? -1 : 1;
287 }
288
289 return 0;
290 }
291
292 DWORD
293 STDCALL
294 NtUserGetKeyState(
295 DWORD key)
296 {
297 DWORD ret = 0;
298
299 ExAcquireFastMutex(&QueueStateLock);
300 if( key < 0x100 ) {
301 ret = ((DWORD)(QueueKeyStateTable[key] & KS_DOWN_BIT) << 8 ) |
302 (QueueKeyStateTable[key] & KS_EXT_BIT) |
303 (QueueKeyStateTable[key] & KS_LOCK_BIT);
304 }
305 ExReleaseFastMutex(&QueueStateLock);
306 return ret;
307 }
308
309 int STDCALL ToUnicodeEx( UINT wVirtKey,
310 UINT wScanCode,
311 PBYTE lpKeyState,
312 LPWSTR pwszBuff,
313 int cchBuff,
314 UINT wFlags,
315 HKL dwhkl ) {
316 int ToUnicodeResult = 0;
317
318 ExAcquireFastMutex(&QueueStateLock);
319 ToUnicodeResult = ToUnicodeInner( wVirtKey,
320 wScanCode,
321 lpKeyState,
322 pwszBuff,
323 cchBuff,
324 wFlags,
325 PsGetWin32Thread() ?
326 PsGetWin32Thread()->KeyboardLayout : 0 );
327 ExReleaseFastMutex(&QueueStateLock);
328
329 return ToUnicodeResult;
330 }
331
332 int STDCALL ToUnicode( UINT wVirtKey,
333 UINT wScanCode,
334 PBYTE lpKeyState,
335 LPWSTR pwszBuff,
336 int cchBuff,
337 UINT wFlags ) {
338 return ToUnicodeEx( wVirtKey,
339 wScanCode,
340 QueueKeyStateTable,
341 pwszBuff,
342 cchBuff,
343 wFlags,
344 0 );
345 }
346
347 /*
348 * Utility to copy and append two unicode strings.
349 *
350 * IN OUT PUNICODE_STRING ResultFirst -> First string and result
351 * IN PUNICODE_STRING Second -> Second string to append
352 * IN BOOL Deallocate -> TRUE: Deallocate First string before
353 * overwriting.
354 *
355 * Returns NTSTATUS.
356 */
357
358 NTSTATUS NTAPI AppendUnicodeString(PUNICODE_STRING ResultFirst,
359 PUNICODE_STRING Second,
360 BOOL Deallocate) {
361 NTSTATUS Status;
362 PWSTR new_string =
363 ExAllocatePool(PagedPool,
364 (ResultFirst->Length + Second->Length + sizeof(WCHAR)));
365 if( !new_string ) {
366 return STATUS_NO_MEMORY;
367 }
368 memcpy( new_string, ResultFirst->Buffer,
369 ResultFirst->Length );
370 memcpy( new_string + ResultFirst->Length / sizeof(WCHAR),
371 Second->Buffer,
372 Second->Length );
373 if( Deallocate ) RtlFreeUnicodeString(ResultFirst);
374 ResultFirst->Length += Second->Length;
375 ResultFirst->MaximumLength = ResultFirst->Length;
376 new_string[ResultFirst->Length / sizeof(WCHAR)] = 0;
377 Status = RtlCreateUnicodeString(ResultFirst,new_string) ?
378 STATUS_SUCCESS : STATUS_NO_MEMORY;
379 ExFreePool(new_string);
380 return Status;
381 }
382
383 /*
384 * Utility function to read a value from the registry more easily.
385 *
386 * IN PUNICODE_STRING KeyName -> Name of key to open
387 * IN PUNICODE_STRING ValueName -> Name of value to open
388 * OUT PUNICODE_STRING ReturnedValue -> String contained in registry
389 *
390 * Returns NTSTATUS
391 */
392
393 static NTSTATUS NTAPI ReadRegistryValue( PUNICODE_STRING KeyName,
394 PUNICODE_STRING ValueName,
395 PUNICODE_STRING ReturnedValue ) {
396 NTSTATUS Status;
397 HANDLE KeyHandle;
398 OBJECT_ATTRIBUTES KeyAttributes;
399 PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;
400 ULONG Length = 0;
401 ULONG ResLength = 0;
402 UNICODE_STRING Temp;
403
404 InitializeObjectAttributes(&KeyAttributes, KeyName, OBJ_CASE_INSENSITIVE,
405 NULL, NULL);
406 Status = ZwOpenKey(&KeyHandle, KEY_ALL_ACCESS, &KeyAttributes);
407 if( !NT_SUCCESS(Status) ) {
408 return Status;
409 }
410
411 Status = ZwQueryValueKey(KeyHandle, ValueName, KeyValuePartialInformation,
412 0,
413 0,
414 &ResLength);
415
416 if( Status != STATUS_BUFFER_TOO_SMALL ) {
417 NtClose(KeyHandle);
418 return Status;
419 }
420
421 ResLength += sizeof( *KeyValuePartialInfo );
422 KeyValuePartialInfo =
423 ExAllocatePool(PagedPool, ResLength);
424 Length = ResLength;
425
426 if( !KeyValuePartialInfo ) {
427 NtClose(KeyHandle);
428 return STATUS_NO_MEMORY;
429 }
430
431 Status = ZwQueryValueKey(KeyHandle, ValueName, KeyValuePartialInformation,
432 (PVOID)KeyValuePartialInfo,
433 Length,
434 &ResLength);
435
436 if( !NT_SUCCESS(Status) ) {
437 NtClose(KeyHandle);
438 ExFreePool(KeyValuePartialInfo);
439 return Status;
440 }
441
442 Temp.Length = Temp.MaximumLength = KeyValuePartialInfo->DataLength;
443 Temp.Buffer = (PWCHAR)KeyValuePartialInfo->Data;
444
445 /* At this point, KeyValuePartialInfo->Data contains the key data */
446 RtlInitUnicodeString(ReturnedValue,L"");
447 AppendUnicodeString(ReturnedValue,&Temp,FALSE);
448
449 ExFreePool(KeyValuePartialInfo);
450 NtClose(KeyHandle);
451
452 return Status;
453 }
454
455 typedef PVOID (*KbdLayerDescriptor)(VOID);
456 NTSTATUS STDCALL LdrGetProcedureAddress(PVOID module,
457 PANSI_STRING import_name,
458 DWORD flags,
459 PVOID *func_addr);
460
461 void InitKbdLayout( PVOID *pkKeyboardLayout ) {
462 UNICODE_STRING KeyName;
463 UNICODE_STRING ValueName;
464 UNICODE_STRING LayoutKeyName;
465 UNICODE_STRING LayoutValueName;
466 UNICODE_STRING DefaultLocale;
467 UNICODE_STRING LayoutFile;
468 UNICODE_STRING FullLayoutPath;
469 PWCHAR KeyboardLayoutWSTR;
470 HMODULE kbModule = 0;
471 NTSTATUS Status;
472 ANSI_STRING kbdProcedureName;
473 KbdLayerDescriptor layerDescGetFn;
474
475 #define XX_STATUS(x) if (!NT_SUCCESS(Status = (x))) continue;
476
477 do {
478 RtlInitUnicodeString(&KeyName,
479 L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet"
480 L"\\Control\\Nls\\Locale");
481 RtlInitUnicodeString(&ValueName,
482 L"(Default)");
483
484 DPRINT("KeyName = %wZ, ValueName = %wZ\n", &KeyName, &ValueName);
485
486 Status = ReadRegistryValue(&KeyName,&ValueName,&DefaultLocale);
487
488 if( !NT_SUCCESS(Status) ) {
489 DPRINT1( "Could not get default locale (%08x).\n", Status );
490 } else {
491 DPRINT( "DefaultLocale = %wZ\n", &DefaultLocale );
492
493 RtlInitUnicodeString(&LayoutKeyName,
494 L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet"
495 L"\\Control\\KeyboardLayouts\\");
496
497 AppendUnicodeString(&LayoutKeyName,&DefaultLocale,FALSE);
498
499 RtlFreeUnicodeString(&DefaultLocale);
500 RtlInitUnicodeString(&LayoutValueName,L"Layout File");
501
502 Status = ReadRegistryValue(&LayoutKeyName,&LayoutValueName,&LayoutFile);
503 RtlInitUnicodeString(&FullLayoutPath,SYSTEMROOT_DIR);
504
505 if( !NT_SUCCESS(Status) ) {
506 DPRINT1("Got default locale but not layout file. (%08x)\n",
507 Status);
508 RtlFreeUnicodeString(&LayoutFile);
509 } else {
510 DPRINT("Read registry and got %wZ\n", &LayoutFile);
511
512 RtlFreeUnicodeString(&LayoutKeyName);
513
514 AppendUnicodeString(&FullLayoutPath,&LayoutFile,FALSE);
515
516 DPRINT("Loading Keyboard DLL %wZ\n", &FullLayoutPath);
517
518 RtlFreeUnicodeString(&LayoutFile);
519
520 KeyboardLayoutWSTR = ExAllocatePool(PagedPool,
521 (FullLayoutPath.Length + 1) *
522 sizeof(WCHAR));
523
524 if( !KeyboardLayoutWSTR ) {
525 DPRINT1("Couldn't allocate a string for the keyboard layout name.\n");
526 RtlFreeUnicodeString(&FullLayoutPath);
527 return;
528 }
529 memcpy(KeyboardLayoutWSTR,FullLayoutPath.Buffer,
530 (FullLayoutPath.Length + 1) * sizeof(WCHAR));
531 KeyboardLayoutWSTR[FullLayoutPath.Length] = 0;
532
533 kbModule = EngLoadImage(KeyboardLayoutWSTR);
534 DPRINT( "Load Keyboard Layout: %S\n", KeyboardLayoutWSTR );
535
536 if( !kbModule )
537 DPRINT1( "Load Keyboard Layout: No %wZ\n", &FullLayoutPath );
538 }
539
540 RtlFreeUnicodeString(&FullLayoutPath);
541 }
542
543 if( !kbModule )
544 {
545 DPRINT1("Trying to load US Keyboard Layout\n");
546 kbModule = EngLoadImage(L"\\SystemRoot\\system32\\kbdus.dll");
547
548 if (!kbModule)
549 {
550 DPRINT1("Failed to load any Keyboard Layout\n");
551 return;
552 }
553 }
554
555 RtlInitAnsiString( &kbdProcedureName, "KbdLayerDescriptor" );
556
557 LdrGetProcedureAddress((PVOID)kbModule,
558 &kbdProcedureName,
559 0,
560 (PVOID*)&layerDescGetFn);
561
562 if( layerDescGetFn ) {
563 *pkKeyboardLayout = layerDescGetFn();
564 }
565 } while (FALSE);
566
567 if( !*pkKeyboardLayout ) {
568 DPRINT1("Failed to load the keyboard layout.\n");
569 }
570
571 #undef XX_STATUS
572 }
573
574 PKBDTABLES W32kGetDefaultKeyLayout() {
575 PKBDTABLES pkKeyboardLayout = 0;
576 InitKbdLayout( (PVOID) &pkKeyboardLayout );
577 return pkKeyboardLayout;
578 }
579
580 BOOL FASTCALL
581 IntTranslateKbdMessage(LPMSG lpMsg,
582 HKL dwhkl)
583 {
584 static INT dead_char = 0;
585 LONG UState = 0;
586 WCHAR wp[2] = { 0 };
587 MSG NewMsg = { 0 };
588 PUSER_MESSAGE UMsg;
589 PKBDTABLES keyLayout;
590 BOOL Result = FALSE;
591 DWORD ScanCode = 0;
592
593
594 keyLayout = PsGetWin32Thread()->KeyboardLayout;
595 if( !keyLayout )
596 return FALSE;
597
598 if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN)
599 return FALSE;
600
601 ScanCode = (lpMsg->lParam >> 16) & 0xff;
602
603 ExAcquireFastMutex(&QueueStateLock);
604
605 UState = ToUnicodeInner(lpMsg->wParam, HIWORD(lpMsg->lParam) & 0xff,
606 QueueKeyStateTable, wp, 2, 0,
607 keyLayout );
608
609 if (UState == 1)
610 {
611 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
612 if (dead_char)
613 {
614 ULONG i;
615 WCHAR first, second;
616 DPRINT("PREVIOUS DEAD CHAR: %c\n", dead_char);
617
618 for( i = 0; keyLayout->pDeadKey[i].dwBoth; i++ )
619 {
620 first = keyLayout->pDeadKey[i].dwBoth >> 16;
621 second = keyLayout->pDeadKey[i].dwBoth;
622 if (first == dead_char && second == wp[0])
623 {
624 wp[0] = keyLayout->pDeadKey[i].wchComposed;
625 dead_char = 0;
626 break;
627 }
628 }
629
630 DPRINT("FINAL CHAR: %c\n", wp[0]);
631 }
632 if (dead_char)
633 {
634 NewMsg.hwnd = lpMsg->hwnd;
635 NewMsg.wParam = dead_char;
636 NewMsg.lParam = lpMsg->lParam;
637 UMsg = MsqCreateMessage(&NewMsg);
638 dead_char = 0;
639 if (UMsg)
640 MsqPostMessage(PsGetWin32Thread()->MessageQueue, UMsg);
641 }
642
643 NewMsg.hwnd = lpMsg->hwnd;
644 NewMsg.wParam = wp[0];
645 NewMsg.lParam = lpMsg->lParam;
646 UMsg = MsqCreateMessage(&NewMsg);
647 DPRINT( "CHAR='%c' %04x %08x\n", wp[0], wp[0], lpMsg->lParam );
648 if (UMsg)
649 MsqPostMessage(PsGetWin32Thread()->MessageQueue, UMsg);
650 Result = TRUE;
651 }
652 else if (UState == -1)
653 {
654 NewMsg.message =
655 (lpMsg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
656 NewMsg.hwnd = lpMsg->hwnd;
657 NewMsg.wParam = wp[0];
658 NewMsg.lParam = lpMsg->lParam;
659 dead_char = wp[0];
660 UMsg = MsqCreateMessage(&NewMsg);
661 if (UMsg)
662 MsqPostMessage(PsGetWin32Thread()->MessageQueue, UMsg);
663 Result = TRUE;
664 }
665
666 ExReleaseFastMutex(&QueueStateLock);
667 return Result;
668 }
669
670 DWORD
671 STDCALL
672 NtUserGetKeyboardState(
673 LPBYTE lpKeyState)
674 {
675 BOOL Result = TRUE;
676
677 ExAcquireFastMutex(&QueueStateLock);
678 if (lpKeyState) {
679 if(!NT_SUCCESS(MmCopyToCaller(lpKeyState, QueueKeyStateTable, 256)))
680 Result = FALSE;
681 }
682 ExReleaseFastMutex(&QueueStateLock);
683 return Result;
684 }
685
686 DWORD
687 STDCALL
688 NtUserSetKeyboardState(
689 LPBYTE lpKeyState)
690 {
691 BOOL Result = TRUE;
692
693 ExAcquireFastMutex(&QueueStateLock);
694 if (lpKeyState) {
695 if(! NT_SUCCESS(MmCopyFromCaller(QueueKeyStateTable, lpKeyState, 256)))
696 Result = FALSE;
697 }
698 ExReleaseFastMutex(&QueueStateLock);
699
700 return Result;
701 }
702
703 static UINT VkToScan( UINT Code, BOOL ExtCode, PKBDTABLES pkKT ) {
704 int i;
705
706 for( i = 0; i < pkKT->bMaxVSCtoVK; i++ ) {
707 if( pkKT->pusVSCtoVK[i] == Code ) { return i; }
708 }
709
710 return 0;
711 }
712
713 UINT ScanToVk( UINT Code, BOOL ExtKey, PKBDTABLES pkKT ) {
714 if( !pkKT ) {
715 DPRINT("ScanToVk: No layout\n");
716 return 0;
717 }
718
719 if( ExtKey ) {
720 int i;
721
722 for( i = 0; pkKT->pVSCtoVK_E0[i].Vsc; i++ ) {
723 if( pkKT->pVSCtoVK_E0[i].Vsc == Code )
724 return pkKT->pVSCtoVK_E0[i].Vk & 0xff;
725 }
726 for( i = 0; pkKT->pVSCtoVK_E1[i].Vsc; i++ ) {
727 if( pkKT->pVSCtoVK_E1[i].Vsc == Code )
728 return pkKT->pVSCtoVK_E1[i].Vk & 0xff;
729 }
730
731 return 0;
732 } else {
733 if( Code >= pkKT->bMaxVSCtoVK ) { return 0; }
734 return pkKT->pusVSCtoVK[Code] & 0xff;
735 }
736 }
737
738 /*
739 * Map a virtual key code, or virtual scan code, to a scan code, key code,
740 * or unshifted unicode character.
741 *
742 * Code: See Below
743 * Type:
744 * 0 -- Code is a virtual key code that is converted into a virtual scan code
745 * that does not distinguish between left and right shift keys.
746 * 1 -- Code is a virtual scan code that is converted into a virtual key code
747 * that does not distinguish between left and right shift keys.
748 * 2 -- Code is a virtual key code that is converted into an unshifted unicode
749 * character.
750 * 3 -- Code is a virtual scan code that is converted into a virtual key code
751 * that distinguishes left and right shift keys.
752 * KeyLayout: Keyboard layout handle (currently, unused)
753 *
754 * @implemented
755 */
756
757 static UINT IntMapVirtualKeyEx( UINT Code, UINT Type, PKBDTABLES keyLayout ) {
758 UINT ret = 0;
759
760 switch( Type ) {
761 case 0:
762 if( Code == VK_RSHIFT ) Code = VK_LSHIFT;
763 if( Code == VK_RMENU ) Code = VK_LMENU;
764 if( Code == VK_RCONTROL ) Code = VK_LCONTROL;
765 ret = VkToScan( Code, FALSE, keyLayout );
766 break;
767
768 case 1:
769 ret =
770 DontDistinguishShifts
771 (IntMapVirtualKeyEx( Code, 3, keyLayout ) );
772 break;
773
774 case 2: {
775 WCHAR wp[2];
776
777 ret = VkToScan( Code, FALSE, keyLayout );
778 ToUnicodeInner( Code, ret, 0, wp, 2, 0, keyLayout );
779 ret = wp[0];
780 } break;
781
782 case 3:
783 ret = ScanToVk( Code, FALSE, keyLayout );
784 break;
785 }
786
787 return ret;
788 }
789
790 UINT
791 STDCALL
792 NtUserMapVirtualKeyEx( UINT Code, UINT Type, DWORD keyboardId, HKL dwhkl ) {
793 PKBDTABLES keyLayout = PsGetWin32Thread() ?
794 PsGetWin32Thread()->KeyboardLayout : 0;
795
796 if( !keyLayout ) return 0;
797
798 return IntMapVirtualKeyEx( Code, Type, keyLayout );
799 }
800
801
802 int
803 STDCALL
804 NtUserToUnicodeEx(
805 UINT wVirtKey,
806 UINT wScanCode,
807 PBYTE lpKeyState,
808 LPWSTR pwszBuff,
809 int cchBuff,
810 UINT wFlags,
811 HKL dwhkl ) {
812 BYTE KeyStateBuf[0x100];
813 PWCHAR OutPwszBuff = 0;
814 int ret = 0;
815
816
817 if( !NT_SUCCESS(MmCopyFromCaller(KeyStateBuf,
818 lpKeyState,
819 sizeof(KeyStateBuf))) ) {
820 DPRINT1( "Couldn't copy key state from caller.\n" );
821 return 0;
822 }
823 OutPwszBuff = ExAllocatePool(NonPagedPool,sizeof(WCHAR) * cchBuff);
824 if( !OutPwszBuff ) {
825 DPRINT1( "ExAllocatePool(%d) failed\n", sizeof(WCHAR) * cchBuff);
826 return 0;
827 }
828 RtlZeroMemory( OutPwszBuff, sizeof( WCHAR ) * cchBuff );
829
830 ret = ToUnicodeEx( wVirtKey,
831 wScanCode,
832 KeyStateBuf,
833 OutPwszBuff,
834 cchBuff,
835 wFlags,
836 dwhkl );
837
838 MmCopyToCaller(pwszBuff,OutPwszBuff,sizeof(WCHAR)*cchBuff);
839 ExFreePool(OutPwszBuff);
840
841 return ret;
842 }
843
844 static int W32kSimpleToupper( int ch ) {
845 if( ch >= 'a' && ch <= 'z' ) ch = ch - 'a' + 'A';
846 return ch;
847 }
848
849 DWORD
850 STDCALL
851 NtUserGetKeyNameText( LONG lParam, LPWSTR lpString, int nSize ) {
852 int i;
853 DWORD ret = 0;
854 UINT CareVk = 0;
855 UINT VkCode = 0;
856 UINT ScanCode = (lParam >> 16) & 0xff;
857 BOOL ExtKey = lParam & (1<<24) ? TRUE : FALSE;
858 PKBDTABLES keyLayout =
859 PsGetWin32Thread() ?
860 PsGetWin32Thread()->KeyboardLayout : 0;
861
862 if( !keyLayout || nSize < 1 ) return 0;
863
864 if( lParam & (1<<25) ) {
865 CareVk = VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
866 if( VkCode == VK_LSHIFT || VkCode == VK_RSHIFT )
867 VkCode = VK_LSHIFT;
868 if( VkCode == VK_LCONTROL || VkCode == VK_RCONTROL )
869 VkCode = VK_LCONTROL;
870 if( VkCode == VK_LMENU || VkCode == VK_RMENU )
871 VkCode = VK_LMENU;
872 } else {
873 VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
874 }
875
876 VSC_LPWSTR *KeyNames = 0;
877
878 if( CareVk != VkCode )
879 ScanCode = VkToScan( VkCode, ExtKey, keyLayout );
880
881 if( ExtKey )
882 KeyNames = keyLayout->pKeyNamesExt;
883 else
884 KeyNames = keyLayout->pKeyNames;
885
886 for( i = 0; KeyNames[i].pwsz; i++ ) {
887 if( KeyNames[i].vsc == ScanCode ) {
888 UINT StrLen = wcslen(KeyNames[i].pwsz);
889 UINT StrMax = StrLen > (nSize - 1) ? (nSize - 1) : StrLen;
890 WCHAR null_wc = 0;
891 if( NT_SUCCESS( MmCopyToCaller( lpString,
892 KeyNames[i].pwsz,
893 StrMax * sizeof(WCHAR) ) ) &&
894 NT_SUCCESS( MmCopyToCaller( lpString + StrMax,
895 &null_wc,
896 sizeof( WCHAR ) ) ) ) {
897 ret = StrMax;
898 break;
899 }
900 }
901 }
902
903 if( ret == 0 ) {
904 WCHAR UCName[2];
905
906 UCName[0] = W32kSimpleToupper(IntMapVirtualKeyEx( VkCode, 2, keyLayout ));
907 UCName[1] = 0;
908 ret = 1;
909
910 if( !NT_SUCCESS(MmCopyToCaller( lpString, UCName, 2 * sizeof(WCHAR) )) )
911 return 0;
912 }
913
914 return ret;
915 }
916
917 /* Search the keyboard layout modifiers table for the shift bit. I don't
918 * want to count on the shift bit not moving, because it can be specified
919 * in the layout */
920
921 static DWORD FASTCALL GetShiftBit( PKBDTABLES pkKT ) {
922 int i;
923
924 for( i = 0; pkKT->pCharModifiers->pVkToBit[i].Vk; i++ )
925 if( pkKT->pCharModifiers->pVkToBit[i].Vk == VK_SHIFT )
926 return pkKT->pCharModifiers->pVkToBit[i].ModBits;
927
928 return 0;
929 }
930
931 /*
932 * Filter this message according to the current key layout, setting wParam
933 * appropriately.
934 */
935
936 VOID FASTCALL W32kKeyProcessMessage(LPMSG Msg, PKBDTABLES KeyboardLayout) {
937 DWORD ScanCode = 0, ModifierBits = 0;
938 DWORD i = 0;
939 DWORD BaseMapping = 0;
940 DWORD RawVk = 0;
941 static WORD NumpadConversion[][2] =
942 { { VK_DELETE, VK_DECIMAL },
943 { VK_INSERT, VK_NUMPAD0 },
944 { VK_END, VK_NUMPAD1 },
945 { VK_DOWN, VK_NUMPAD2 },
946 { VK_NEXT, VK_NUMPAD3 },
947 { VK_LEFT, VK_NUMPAD4 },
948 { VK_CLEAR, VK_NUMPAD5 },
949 { VK_RIGHT, VK_NUMPAD6 },
950 { VK_HOME, VK_NUMPAD7 },
951 { VK_UP, VK_NUMPAD8 },
952 { VK_PRIOR, VK_NUMPAD9 },
953 { 0,0 } };
954
955 if( !KeyboardLayout || !Msg ||
956 (Msg->message != WM_KEYDOWN && Msg->message != WM_SYSKEYDOWN &&
957 Msg->message != WM_KEYUP && Msg->message != WM_SYSKEYUP) )
958 {
959 return;
960 }
961
962 ExAcquireFastMutex(&QueueStateLock);
963
964 /* arty -- handle numpad -- On real windows, the actual key produced
965 * by the messaging layer is different based on the state of numlock. */
966 ModifierBits = ModBits(KeyboardLayout,QueueKeyStateTable);
967
968 /* Get the raw scan code, so we can look up whether the key is a numpad
969 * key
970 *
971 * Shift and the LP_EXT_BIT cancel. */
972 ScanCode = (Msg->lParam >> 16) & 0xff;
973 BaseMapping = Msg->wParam =
974 IntMapVirtualKeyEx( ScanCode, 1, KeyboardLayout );
975 RawVk = KeyboardLayout->pusVSCtoVK[ScanCode];
976
977 if ((ModifierBits & NUMLOCK_BIT) &&
978 !(ModifierBits & GetShiftBit(KeyboardLayout)) &&
979 (RawVk & KNUMP) &&
980 !(Msg->lParam & LP_EXT_BIT))
981 {
982 /* The key in question is a numpad key. Search for a translation. */
983 for (i = 0; NumpadConversion[i][0]; i++)
984 {
985 if ((BaseMapping & 0xff) == NumpadConversion[i][0]) /* RawVk? */
986 {
987 Msg->wParam = NumpadConversion[i][1];
988 break;
989 }
990 }
991 }
992
993 DPRINT("Key: [%04x -> %04x]\n", BaseMapping, Msg->wParam);
994
995 /* Now that we have the VK, we can set the keymap appropriately
996 * This is a better place for this code, as it's guaranteed to be
997 * run, unlike translate message. */
998 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN)
999 {
1000 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
1001 TRUE ); /* Strike key */
1002 }
1003 else if (Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
1004 {
1005 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
1006 FALSE ); /* Release key */
1007 }
1008
1009 ExReleaseFastMutex(&QueueStateLock);
1010 }
1011 /* EOF */