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