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