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