Branch setupapi
[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 if (0 == (lpKeyState[wVirtKey] & KS_DOWN_BIT))
346 {
347 ToUnicodeResult = 0;
348 }
349 else
350 {
351 IntLockQueueState;
352 ToUnicodeResult = ToUnicodeInner( wVirtKey,
353 wScanCode,
354 lpKeyState,
355 pwszBuff,
356 cchBuff,
357 wFlags,
358 PsGetWin32Thread() ?
359 PsGetWin32Thread()->KeyboardLayout : 0 );
360 IntUnLockQueueState;
361 }
362
363 return ToUnicodeResult;
364 }
365
366 int STDCALL ToUnicode( UINT wVirtKey,
367 UINT wScanCode,
368 PBYTE lpKeyState,
369 LPWSTR pwszBuff,
370 int cchBuff,
371 UINT wFlags ) {
372 return ToUnicodeEx( wVirtKey,
373 wScanCode,
374 QueueKeyStateTable,
375 pwszBuff,
376 cchBuff,
377 wFlags,
378 0 );
379 }
380
381 /*
382 * Utility to copy and append two unicode strings.
383 *
384 * IN OUT PUNICODE_STRING ResultFirst -> First string and result
385 * IN PUNICODE_STRING Second -> Second string to append
386 * IN BOOL Deallocate -> TRUE: Deallocate First string before
387 * overwriting.
388 *
389 * Returns NTSTATUS.
390 */
391
392 NTSTATUS NTAPI AppendUnicodeString(PUNICODE_STRING ResultFirst,
393 PUNICODE_STRING Second,
394 BOOL Deallocate) {
395 NTSTATUS Status;
396 PWSTR new_string =
397 ExAllocatePoolWithTag(PagedPool,
398 (ResultFirst->Length + Second->Length + sizeof(WCHAR)),
399 TAG_STRING);
400 if( !new_string ) {
401 return STATUS_NO_MEMORY;
402 }
403 memcpy( new_string, ResultFirst->Buffer,
404 ResultFirst->Length );
405 memcpy( new_string + ResultFirst->Length / sizeof(WCHAR),
406 Second->Buffer,
407 Second->Length );
408 if( Deallocate ) RtlFreeUnicodeString(ResultFirst);
409 ResultFirst->Length += Second->Length;
410 ResultFirst->MaximumLength = ResultFirst->Length;
411 new_string[ResultFirst->Length / sizeof(WCHAR)] = 0;
412 Status = RtlCreateUnicodeString(ResultFirst,new_string) ?
413 STATUS_SUCCESS : STATUS_NO_MEMORY;
414 ExFreePool(new_string);
415 return Status;
416 }
417
418 /*
419 * Utility function to read a value from the registry more easily.
420 *
421 * IN PUNICODE_STRING KeyName -> Name of key to open
422 * IN PUNICODE_STRING ValueName -> Name of value to open
423 * OUT PUNICODE_STRING ReturnedValue -> String contained in registry
424 *
425 * Returns NTSTATUS
426 */
427
428 static NTSTATUS NTAPI ReadRegistryValue( PUNICODE_STRING KeyName,
429 PUNICODE_STRING ValueName,
430 PUNICODE_STRING ReturnedValue ) {
431 NTSTATUS Status;
432 HANDLE KeyHandle;
433 OBJECT_ATTRIBUTES KeyAttributes;
434 PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;
435 ULONG Length = 0;
436 ULONG ResLength = 0;
437 UNICODE_STRING Temp;
438
439 InitializeObjectAttributes(&KeyAttributes, KeyName, OBJ_CASE_INSENSITIVE,
440 NULL, NULL);
441 Status = ZwOpenKey(&KeyHandle, KEY_ALL_ACCESS, &KeyAttributes);
442 if( !NT_SUCCESS(Status) ) {
443 return Status;
444 }
445
446 Status = ZwQueryValueKey(KeyHandle, ValueName, KeyValuePartialInformation,
447 0,
448 0,
449 &ResLength);
450
451 if( Status != STATUS_BUFFER_TOO_SMALL ) {
452 NtClose(KeyHandle);
453 return Status;
454 }
455
456 ResLength += sizeof( *KeyValuePartialInfo );
457 KeyValuePartialInfo =
458 ExAllocatePoolWithTag(PagedPool, ResLength, TAG_STRING);
459 Length = ResLength;
460
461 if( !KeyValuePartialInfo ) {
462 NtClose(KeyHandle);
463 return STATUS_NO_MEMORY;
464 }
465
466 Status = ZwQueryValueKey(KeyHandle, ValueName, KeyValuePartialInformation,
467 (PVOID)KeyValuePartialInfo,
468 Length,
469 &ResLength);
470
471 if( !NT_SUCCESS(Status) ) {
472 NtClose(KeyHandle);
473 ExFreePool(KeyValuePartialInfo);
474 return Status;
475 }
476
477 Temp.Length = Temp.MaximumLength = KeyValuePartialInfo->DataLength;
478 Temp.Buffer = (PWCHAR)KeyValuePartialInfo->Data;
479
480 /* At this point, KeyValuePartialInfo->Data contains the key data */
481 RtlInitUnicodeString(ReturnedValue,L"");
482 AppendUnicodeString(ReturnedValue,&Temp,FALSE);
483
484 ExFreePool(KeyValuePartialInfo);
485 NtClose(KeyHandle);
486
487 return Status;
488 }
489
490 typedef PVOID (*KbdLayerDescriptor)(VOID);
491 NTSTATUS STDCALL LdrGetProcedureAddress(PVOID module,
492 PANSI_STRING import_name,
493 DWORD flags,
494 PVOID *func_addr);
495
496 void InitKbdLayout( PVOID *pkKeyboardLayout )
497 {
498 WCHAR LocaleBuffer[16];
499 UNICODE_STRING LayoutKeyName;
500 UNICODE_STRING LayoutValueName;
501 UNICODE_STRING DefaultLocale;
502 UNICODE_STRING LayoutFile;
503 UNICODE_STRING FullLayoutPath;
504 LCID LocaleId;
505 PWCHAR KeyboardLayoutWSTR;
506 HMODULE kbModule = 0;
507 NTSTATUS Status;
508 ANSI_STRING kbdProcedureName;
509 KbdLayerDescriptor layerDescGetFn;
510
511 #define XX_STATUS(x) if (!NT_SUCCESS(Status = (x))) continue;
512
513 do {
514 Status = ZwQueryDefaultLocale(FALSE, &LocaleId);
515 if (!NT_SUCCESS(Status))
516 {
517 DPRINT1("Could not get default locale (%08lx).\n", Status);
518 }
519 else
520 {
521 DPRINT("DefaultLocale = %lx\n", LocaleId);
522 swprintf(LocaleBuffer, L"%08lx", LocaleId);
523 DPRINT("DefaultLocale = %S\n", LocaleBuffer);
524 RtlInitUnicodeString(&DefaultLocale, LocaleBuffer);
525
526 RtlInitUnicodeString(&LayoutKeyName,
527 L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet"
528 L"\\Control\\KeyboardLayouts\\");
529
530 AppendUnicodeString(&LayoutKeyName,&DefaultLocale,FALSE);
531
532 RtlInitUnicodeString(&LayoutValueName,L"Layout File");
533
534 Status = ReadRegistryValue(&LayoutKeyName,&LayoutValueName,&LayoutFile);
535 RtlInitUnicodeString(&FullLayoutPath,SYSTEMROOT_DIR);
536
537 if( !NT_SUCCESS(Status) ) {
538 DPRINT1("Got default locale but not layout file. (%08x)\n",
539 Status);
540 RtlFreeUnicodeString(&LayoutFile);
541 } else {
542 DPRINT("Read registry and got %wZ\n", &LayoutFile);
543
544 RtlFreeUnicodeString(&LayoutKeyName);
545
546 AppendUnicodeString(&FullLayoutPath,&LayoutFile,FALSE);
547
548 DPRINT("Loading Keyboard DLL %wZ\n", &FullLayoutPath);
549
550 RtlFreeUnicodeString(&LayoutFile);
551
552 KeyboardLayoutWSTR =
553 ExAllocatePoolWithTag(PagedPool,
554 FullLayoutPath.Length + sizeof(WCHAR),
555 TAG_STRING);
556
557 if( !KeyboardLayoutWSTR ) {
558 DPRINT1("Couldn't allocate a string for the keyboard layout name.\n");
559 RtlFreeUnicodeString(&FullLayoutPath);
560 return;
561 }
562 memcpy(KeyboardLayoutWSTR,FullLayoutPath.Buffer,
563 FullLayoutPath.Length + sizeof(WCHAR));
564 KeyboardLayoutWSTR[FullLayoutPath.Length / sizeof(WCHAR)] = 0;
565
566 kbModule = EngLoadImage(KeyboardLayoutWSTR);
567 DPRINT( "Load Keyboard Layout: %S\n", KeyboardLayoutWSTR );
568
569 if( !kbModule )
570 DPRINT1( "Load Keyboard Layout: No %wZ\n", &FullLayoutPath );
571 }
572
573 RtlFreeUnicodeString(&FullLayoutPath);
574 }
575
576 if( !kbModule )
577 {
578 DPRINT1("Trying to load US Keyboard Layout\n");
579 kbModule = EngLoadImage(L"\\SystemRoot\\system32\\kbdus.dll");
580
581 if (!kbModule)
582 {
583 DPRINT1("Failed to load any Keyboard Layout\n");
584 return;
585 }
586 }
587
588 RtlInitAnsiString( &kbdProcedureName, "KbdLayerDescriptor" );
589
590 LdrGetProcedureAddress((PVOID)kbModule,
591 &kbdProcedureName,
592 0,
593 (PVOID*)&layerDescGetFn);
594
595 if( layerDescGetFn ) {
596 *pkKeyboardLayout = layerDescGetFn();
597 }
598 } while (FALSE);
599
600 if( !*pkKeyboardLayout ) {
601 DPRINT1("Failed to load the keyboard layout.\n");
602 }
603
604 #undef XX_STATUS
605 }
606
607 PKBDTABLES W32kGetDefaultKeyLayout() {
608 PKBDTABLES pkKeyboardLayout = 0;
609 InitKbdLayout( (PVOID) &pkKeyboardLayout );
610 return pkKeyboardLayout;
611 }
612
613 BOOL FASTCALL
614 IntTranslateKbdMessage(LPMSG lpMsg,
615 HKL dwhkl)
616 {
617 static INT dead_char = 0;
618 LONG UState = 0;
619 WCHAR wp[2] = { 0 };
620 MSG NewMsg = { 0 };
621 PKBDTABLES keyLayout;
622 BOOL Result = FALSE;
623 DWORD ScanCode = 0;
624
625
626 keyLayout = PsGetWin32Thread()->KeyboardLayout;
627 if( !keyLayout )
628 return FALSE;
629
630 if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN)
631 return FALSE;
632
633 ScanCode = (lpMsg->lParam >> 16) & 0xff;
634
635 IntLockQueueState;
636
637 UState = ToUnicodeInner(lpMsg->wParam, HIWORD(lpMsg->lParam) & 0xff,
638 QueueKeyStateTable, wp, 2, 0,
639 keyLayout );
640
641 if (UState == 1)
642 {
643 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
644 if (dead_char)
645 {
646 ULONG i;
647 WCHAR first, second;
648 DPRINT("PREVIOUS DEAD CHAR: %c\n", dead_char);
649
650 for( i = 0; keyLayout->pDeadKey[i].dwBoth; i++ )
651 {
652 first = keyLayout->pDeadKey[i].dwBoth >> 16;
653 second = keyLayout->pDeadKey[i].dwBoth;
654 if (first == dead_char && second == wp[0])
655 {
656 wp[0] = keyLayout->pDeadKey[i].wchComposed;
657 dead_char = 0;
658 break;
659 }
660 }
661
662 DPRINT("FINAL CHAR: %c\n", wp[0]);
663 }
664 if (dead_char)
665 {
666 NewMsg.hwnd = lpMsg->hwnd;
667 NewMsg.wParam = dead_char;
668 NewMsg.lParam = lpMsg->lParam;
669 dead_char = 0;
670 MsqPostMessage(PsGetWin32Thread()->MessageQueue, &NewMsg, FALSE, QS_KEY);
671 }
672
673 NewMsg.hwnd = lpMsg->hwnd;
674 NewMsg.wParam = wp[0];
675 NewMsg.lParam = lpMsg->lParam;
676 DPRINT( "CHAR='%c' %04x %08x\n", wp[0], wp[0], lpMsg->lParam );
677 MsqPostMessage(PsGetWin32Thread()->MessageQueue, &NewMsg, FALSE, QS_KEY);
678 Result = TRUE;
679 }
680 else if (UState == -1)
681 {
682 NewMsg.message =
683 (lpMsg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
684 NewMsg.hwnd = lpMsg->hwnd;
685 NewMsg.wParam = wp[0];
686 NewMsg.lParam = lpMsg->lParam;
687 dead_char = wp[0];
688 MsqPostMessage(PsGetWin32Thread()->MessageQueue, &NewMsg, FALSE, QS_KEY);
689 Result = TRUE;
690 }
691
692 IntUnLockQueueState;
693 return Result;
694 }
695
696 DWORD
697 STDCALL
698 NtUserGetKeyboardState(
699 LPBYTE lpKeyState)
700 {
701 BOOL Result = TRUE;
702
703 IntLockQueueState;
704 if (lpKeyState) {
705 if(!NT_SUCCESS(MmCopyToCaller(lpKeyState, QueueKeyStateTable, 256)))
706 Result = FALSE;
707 }
708 IntUnLockQueueState;
709 return Result;
710 }
711
712 DWORD
713 STDCALL
714 NtUserSetKeyboardState(
715 LPBYTE lpKeyState)
716 {
717 BOOL Result = TRUE;
718
719 IntLockQueueState;
720 if (lpKeyState) {
721 if(! NT_SUCCESS(MmCopyFromCaller(QueueKeyStateTable, lpKeyState, 256)))
722 Result = FALSE;
723 }
724 IntUnLockQueueState;
725
726 return Result;
727 }
728
729 static UINT VkToScan( UINT Code, BOOL ExtCode, PKBDTABLES pkKT ) {
730 int i;
731
732 for( i = 0; i < pkKT->bMaxVSCtoVK; i++ ) {
733 if( pkKT->pusVSCtoVK[i] == Code ) { return i; }
734 }
735
736 return 0;
737 }
738
739 UINT ScanToVk( UINT Code, BOOL ExtKey, PKBDTABLES pkKT ) {
740 if( !pkKT ) {
741 DPRINT("ScanToVk: No layout\n");
742 return 0;
743 }
744
745 if( ExtKey ) {
746 int i;
747
748 for( i = 0; pkKT->pVSCtoVK_E0[i].Vsc; i++ ) {
749 if( pkKT->pVSCtoVK_E0[i].Vsc == Code )
750 return pkKT->pVSCtoVK_E0[i].Vk & 0xff;
751 }
752 for( i = 0; pkKT->pVSCtoVK_E1[i].Vsc; i++ ) {
753 if( pkKT->pVSCtoVK_E1[i].Vsc == Code )
754 return pkKT->pVSCtoVK_E1[i].Vk & 0xff;
755 }
756
757 return 0;
758 } else {
759 if( Code >= pkKT->bMaxVSCtoVK ) { return 0; }
760 return pkKT->pusVSCtoVK[Code] & 0xff;
761 }
762 }
763
764 /*
765 * Map a virtual key code, or virtual scan code, to a scan code, key code,
766 * or unshifted unicode character.
767 *
768 * Code: See Below
769 * Type:
770 * 0 -- Code is a virtual key code that is converted into a virtual scan code
771 * that does not distinguish between left and right shift keys.
772 * 1 -- Code is a virtual scan code that is converted into a virtual key code
773 * that does not distinguish between left and right shift keys.
774 * 2 -- Code is a virtual key code that is converted into an unshifted unicode
775 * character.
776 * 3 -- Code is a virtual scan code that is converted into a virtual key code
777 * that distinguishes left and right shift keys.
778 * KeyLayout: Keyboard layout handle (currently, unused)
779 *
780 * @implemented
781 */
782
783 static UINT IntMapVirtualKeyEx( UINT Code, UINT Type, PKBDTABLES keyLayout ) {
784 UINT ret = 0;
785
786 switch( Type ) {
787 case 0:
788 if( Code == VK_RSHIFT ) Code = VK_LSHIFT;
789 if( Code == VK_RMENU ) Code = VK_LMENU;
790 if( Code == VK_RCONTROL ) Code = VK_LCONTROL;
791 ret = VkToScan( Code, FALSE, keyLayout );
792 break;
793
794 case 1:
795 ret =
796 DontDistinguishShifts
797 (IntMapVirtualKeyEx( Code, 3, keyLayout ) );
798 break;
799
800 case 2: {
801 WCHAR wp[2];
802
803 ret = VkToScan( Code, FALSE, keyLayout );
804 ToUnicodeInner( Code, ret, 0, wp, 2, 0, keyLayout );
805 ret = wp[0];
806 } break;
807
808 case 3:
809
810 ret = ScanToVk( Code, FALSE, keyLayout );
811 break;
812 }
813
814 return ret;
815 }
816
817 UINT
818 STDCALL
819 NtUserMapVirtualKeyEx( UINT Code, UINT Type, DWORD keyboardId, HKL dwhkl ) {
820 PKBDTABLES keyLayout = PsGetWin32Thread() ?
821 PsGetWin32Thread()->KeyboardLayout : 0;
822
823 if( !keyLayout ) return 0;
824
825 return IntMapVirtualKeyEx( Code, Type, keyLayout );
826 }
827
828
829 int
830 STDCALL
831 NtUserToUnicodeEx(
832 UINT wVirtKey,
833 UINT wScanCode,
834 PBYTE lpKeyState,
835 LPWSTR pwszBuff,
836 int cchBuff,
837 UINT wFlags,
838 HKL dwhkl ) {
839 BYTE KeyStateBuf[0x100];
840 PWCHAR OutPwszBuff = 0;
841 int ret = 0;
842
843
844 if( !NT_SUCCESS(MmCopyFromCaller(KeyStateBuf,
845 lpKeyState,
846 sizeof(KeyStateBuf))) ) {
847 DPRINT1( "Couldn't copy key state from caller.\n" );
848 return 0;
849 }
850 OutPwszBuff = ExAllocatePoolWithTag(NonPagedPool,sizeof(WCHAR) * cchBuff, TAG_STRING);
851 if( !OutPwszBuff ) {
852 DPRINT1( "ExAllocatePool(%d) failed\n", sizeof(WCHAR) * cchBuff);
853 return 0;
854 }
855 RtlZeroMemory( OutPwszBuff, sizeof( WCHAR ) * cchBuff );
856
857 ret = ToUnicodeEx( wVirtKey,
858 wScanCode,
859 KeyStateBuf,
860 OutPwszBuff,
861 cchBuff,
862 wFlags,
863 dwhkl );
864
865 MmCopyToCaller(pwszBuff,OutPwszBuff,sizeof(WCHAR)*cchBuff);
866 ExFreePool(OutPwszBuff);
867
868 return ret;
869 }
870
871 static int W32kSimpleToupper( int ch ) {
872 if( ch >= 'a' && ch <= 'z' ) ch = ch - 'a' + 'A';
873 return ch;
874 }
875
876 DWORD
877 STDCALL
878 NtUserGetKeyNameText( LONG lParam, LPWSTR lpString, int nSize ) {
879 int i;
880 DWORD ret = 0;
881 UINT CareVk = 0;
882 UINT VkCode = 0;
883 UINT ScanCode = (lParam >> 16) & 0xff;
884 BOOL ExtKey = lParam & (1<<24) ? TRUE : FALSE;
885 PKBDTABLES keyLayout =
886 PsGetWin32Thread() ?
887 PsGetWin32Thread()->KeyboardLayout : 0;
888
889 if( !keyLayout || nSize < 1 ) return 0;
890
891 if( lParam & (1<<25) ) {
892 CareVk = VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
893 if( VkCode == VK_LSHIFT || VkCode == VK_RSHIFT )
894 VkCode = VK_LSHIFT;
895 if( VkCode == VK_LCONTROL || VkCode == VK_RCONTROL )
896 VkCode = VK_LCONTROL;
897 if( VkCode == VK_LMENU || VkCode == VK_RMENU )
898 VkCode = VK_LMENU;
899 } else {
900 VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
901 }
902
903 VSC_LPWSTR *KeyNames = 0;
904
905 if( CareVk != VkCode )
906 ScanCode = VkToScan( VkCode, ExtKey, keyLayout );
907
908 if( ExtKey )
909 KeyNames = keyLayout->pKeyNamesExt;
910 else
911 KeyNames = keyLayout->pKeyNames;
912
913 for( i = 0; KeyNames[i].pwsz; i++ ) {
914 if( KeyNames[i].vsc == ScanCode ) {
915 UINT StrLen = wcslen(KeyNames[i].pwsz);
916 UINT StrMax = StrLen > (nSize - 1) ? (nSize - 1) : StrLen;
917 WCHAR null_wc = 0;
918 if( NT_SUCCESS( MmCopyToCaller( lpString,
919 KeyNames[i].pwsz,
920 StrMax * sizeof(WCHAR) ) ) &&
921 NT_SUCCESS( MmCopyToCaller( lpString + StrMax,
922 &null_wc,
923 sizeof( WCHAR ) ) ) ) {
924 ret = StrMax;
925 break;
926 }
927 }
928 }
929
930 if( ret == 0 ) {
931 WCHAR UCName[2];
932
933 UCName[0] = W32kSimpleToupper(IntMapVirtualKeyEx( VkCode, 2, keyLayout ));
934 UCName[1] = 0;
935 ret = 1;
936
937 if( !NT_SUCCESS(MmCopyToCaller( lpString, UCName, 2 * sizeof(WCHAR) )) )
938 return 0;
939 }
940
941 return ret;
942 }
943
944 /*
945 * Filter this message according to the current key layout, setting wParam
946 * appropriately.
947 */
948
949 VOID FASTCALL W32kKeyProcessMessage(LPMSG Msg, PKBDTABLES KeyboardLayout) {
950 DWORD ScanCode = 0, ModifierBits = 0;
951 DWORD i = 0;
952 DWORD BaseMapping = 0;
953 DWORD RawVk = 0;
954 static WORD NumpadConversion[][2] =
955 { { VK_DELETE, VK_DECIMAL },
956 { VK_INSERT, VK_NUMPAD0 },
957 { VK_END, VK_NUMPAD1 },
958 { VK_DOWN, VK_NUMPAD2 },
959 { VK_NEXT, VK_NUMPAD3 },
960 { VK_LEFT, VK_NUMPAD4 },
961 { VK_CLEAR, VK_NUMPAD5 },
962 { VK_RIGHT, VK_NUMPAD6 },
963 { VK_HOME, VK_NUMPAD7 },
964 { VK_UP, VK_NUMPAD8 },
965 { VK_PRIOR, VK_NUMPAD9 },
966 { 0,0 } };
967
968 if( !KeyboardLayout || !Msg ||
969 (Msg->message != WM_KEYDOWN && Msg->message != WM_SYSKEYDOWN &&
970 Msg->message != WM_KEYUP && Msg->message != WM_SYSKEYUP) )
971 {
972 return;
973 }
974
975 IntLockQueueState;
976
977 /* arty -- handle numpad -- On real windows, the actual key produced
978 * by the messaging layer is different based on the state of numlock. */
979 ModifierBits = ModBits(KeyboardLayout,QueueKeyStateTable);
980
981 /* Get the raw scan code, so we can look up whether the key is a numpad
982 * key
983 *
984 * Shift and the LP_EXT_BIT cancel. */
985 ScanCode = (Msg->lParam >> 16) & 0xff;
986 BaseMapping = Msg->wParam =
987 IntMapVirtualKeyEx( ScanCode, 1, KeyboardLayout );
988 if( ScanCode >= KeyboardLayout->bMaxVSCtoVK )
989 RawVk = 0;
990 else
991 RawVk = KeyboardLayout->pusVSCtoVK[ScanCode];
992
993 if ((ModifierBits & NUMLOCK_BIT) &&
994 !(ModifierBits & GetShiftBit(KeyboardLayout, VK_SHIFT)) &&
995 (RawVk & KNUMP) &&
996 !(Msg->lParam & LP_EXT_BIT))
997 {
998 /* The key in question is a numpad key. Search for a translation. */
999 for (i = 0; NumpadConversion[i][0]; i++)
1000 {
1001 if ((BaseMapping & 0xff) == NumpadConversion[i][0]) /* RawVk? */
1002 {
1003 Msg->wParam = NumpadConversion[i][1];
1004 break;
1005 }
1006 }
1007 }
1008
1009 DPRINT("Key: [%04x -> %04x]\n", BaseMapping, Msg->wParam);
1010
1011 /* Now that we have the VK, we can set the keymap appropriately
1012 * This is a better place for this code, as it's guaranteed to be
1013 * run, unlike translate message. */
1014 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN)
1015 {
1016 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
1017 TRUE ); /* Strike key */
1018 }
1019 else if (Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
1020 {
1021 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
1022 FALSE ); /* Release key */
1023 }
1024
1025 /* We need to unset SYSKEYDOWN if the ALT key is an ALT+Gr */
1026 if( QueueKeyStateTable[VK_RMENU] & KS_DOWN_BIT ) {
1027 if( Msg->message == WM_SYSKEYDOWN ) Msg->message = WM_KEYDOWN;
1028 else Msg->message = WM_KEYUP;
1029 }
1030
1031 IntUnLockQueueState;
1032 }
1033 /* EOF */