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