0c7bd22be74a48bfe804002f449292ecd4e07429
[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
319 DWORD FASTCALL UserGetKeyState(DWORD key)
320 {
321 DWORD ret = 0;
322
323 IntLockQueueState;
324 if( key < 0x100 ) {
325 ret = ((DWORD)(QueueKeyStateTable[key] & KS_DOWN_BIT) << 8 ) |
326 (QueueKeyStateTable[key] & KS_LOCK_BIT);
327 }
328 IntUnLockQueueState;
329 return ret;
330 }
331
332
333 DWORD
334 STDCALL
335 NtUserGetKeyState(
336 DWORD key)
337 {
338 DECLARE_RETURN(DWORD);
339
340 DPRINT("Enter NtUserGetKeyState\n");
341 UserEnterExclusive();
342
343 RETURN(UserGetKeyState(key));
344
345 CLEANUP:
346 DPRINT("Leave NtUserGetKeyState, ret=%i\n",_ret_);
347 UserLeave();
348 END_CLEANUP;
349 }
350
351
352
353 DWORD FASTCALL UserGetAsyncKeyState(DWORD key)
354 {
355 DWORD ret = 0;
356
357 IntLockQueueState;
358 if( key < 0x100 ) {
359 ret = ((DWORD)(QueueKeyStateTable[key] & KS_DOWN_BIT) << 8 ) |
360 (QueueKeyStateTable[key] & KS_LOCK_BIT);
361 }
362 IntUnLockQueueState;
363 return ret;
364 }
365
366
367
368 DWORD
369 STDCALL
370 NtUserGetAsyncKeyState(
371 DWORD key)
372 {
373 DECLARE_RETURN(DWORD);
374
375 DPRINT("Enter NtUserGetAsyncKeyState\n");
376 UserEnterExclusive();
377
378 RETURN(UserGetAsyncKeyState(key));
379
380 CLEANUP:
381 DPRINT("Leave NtUserGetAsyncKeyState, ret=%i\n",_ret_);
382 UserLeave();
383 END_CLEANUP;
384 }
385
386
387
388 int STDCALL ToUnicodeEx( UINT wVirtKey,
389 UINT wScanCode,
390 PBYTE lpKeyState,
391 LPWSTR pwszBuff,
392 int cchBuff,
393 UINT wFlags,
394 HKL dwhkl ) {
395 int ToUnicodeResult = 0;
396
397 if (0 == (lpKeyState[wVirtKey] & KS_DOWN_BIT))
398 {
399 ToUnicodeResult = 0;
400 }
401 else
402 {
403 IntLockQueueState;
404 ToUnicodeResult = ToUnicodeInner( wVirtKey,
405 wScanCode,
406 lpKeyState,
407 pwszBuff,
408 cchBuff,
409 wFlags,
410 PsGetWin32Thread() ?
411 PsGetWin32Thread()->KeyboardLayout : 0 );
412 IntUnLockQueueState;
413 }
414
415 return ToUnicodeResult;
416 }
417
418 int STDCALL ToUnicode( UINT wVirtKey,
419 UINT wScanCode,
420 PBYTE lpKeyState,
421 LPWSTR pwszBuff,
422 int cchBuff,
423 UINT wFlags ) {
424 return ToUnicodeEx( wVirtKey,
425 wScanCode,
426 QueueKeyStateTable,
427 pwszBuff,
428 cchBuff,
429 wFlags,
430 0 );
431 }
432
433 /*
434 * Utility to copy and append two unicode strings.
435 *
436 * IN OUT PUNICODE_STRING ResultFirst -> First string and result
437 * IN PUNICODE_STRING Second -> Second string to append
438 * IN BOOL Deallocate -> TRUE: Deallocate First string before
439 * overwriting.
440 *
441 * Returns NTSTATUS.
442 */
443
444 NTSTATUS NTAPI AppendUnicodeString(PUNICODE_STRING ResultFirst,
445 PUNICODE_STRING Second,
446 BOOL Deallocate) {
447 NTSTATUS Status;
448 PWSTR new_string =
449 ExAllocatePoolWithTag(PagedPool,
450 (ResultFirst->Length + Second->Length + sizeof(WCHAR)),
451 TAG_STRING);
452 if( !new_string ) {
453 return STATUS_NO_MEMORY;
454 }
455 memcpy( new_string, ResultFirst->Buffer,
456 ResultFirst->Length );
457 memcpy( new_string + ResultFirst->Length / sizeof(WCHAR),
458 Second->Buffer,
459 Second->Length );
460 if( Deallocate ) RtlFreeUnicodeString(ResultFirst);
461 ResultFirst->Length += Second->Length;
462 ResultFirst->MaximumLength = ResultFirst->Length;
463 new_string[ResultFirst->Length / sizeof(WCHAR)] = 0;
464 Status = RtlCreateUnicodeString(ResultFirst,new_string) ?
465 STATUS_SUCCESS : STATUS_NO_MEMORY;
466 ExFreePool(new_string);
467 return Status;
468 }
469
470 /*
471 * Utility function to read a value from the registry more easily.
472 *
473 * IN PUNICODE_STRING KeyName -> Name of key to open
474 * IN PUNICODE_STRING ValueName -> Name of value to open
475 * OUT PUNICODE_STRING ReturnedValue -> String contained in registry
476 *
477 * Returns NTSTATUS
478 */
479
480 static NTSTATUS NTAPI ReadRegistryValue( PUNICODE_STRING KeyName,
481 PUNICODE_STRING ValueName,
482 PUNICODE_STRING ReturnedValue ) {
483 NTSTATUS Status;
484 HANDLE KeyHandle;
485 OBJECT_ATTRIBUTES KeyAttributes;
486 PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;
487 ULONG Length = 0;
488 ULONG ResLength = 0;
489 UNICODE_STRING Temp;
490
491 InitializeObjectAttributes(&KeyAttributes, KeyName, OBJ_CASE_INSENSITIVE,
492 NULL, NULL);
493 Status = ZwOpenKey(&KeyHandle, KEY_ALL_ACCESS, &KeyAttributes);
494 if( !NT_SUCCESS(Status) ) {
495 return Status;
496 }
497
498 Status = ZwQueryValueKey(KeyHandle, ValueName, KeyValuePartialInformation,
499 0,
500 0,
501 &ResLength);
502
503 if( Status != STATUS_BUFFER_TOO_SMALL ) {
504 NtClose(KeyHandle);
505 return Status;
506 }
507
508 ResLength += sizeof( *KeyValuePartialInfo );
509 KeyValuePartialInfo =
510 ExAllocatePoolWithTag(PagedPool, ResLength, TAG_STRING);
511 Length = ResLength;
512
513 if( !KeyValuePartialInfo ) {
514 NtClose(KeyHandle);
515 return STATUS_NO_MEMORY;
516 }
517
518 Status = ZwQueryValueKey(KeyHandle, ValueName, KeyValuePartialInformation,
519 (PVOID)KeyValuePartialInfo,
520 Length,
521 &ResLength);
522
523 if( !NT_SUCCESS(Status) ) {
524 NtClose(KeyHandle);
525 ExFreePool(KeyValuePartialInfo);
526 return Status;
527 }
528
529 Temp.Length = Temp.MaximumLength = KeyValuePartialInfo->DataLength;
530 Temp.Buffer = (PWCHAR)KeyValuePartialInfo->Data;
531
532 /* At this point, KeyValuePartialInfo->Data contains the key data */
533 RtlInitUnicodeString(ReturnedValue,L"");
534 AppendUnicodeString(ReturnedValue,&Temp,FALSE);
535
536 ExFreePool(KeyValuePartialInfo);
537 NtClose(KeyHandle);
538
539 return Status;
540 }
541
542 typedef PVOID (*KbdLayerDescriptor)(VOID);
543 NTSTATUS STDCALL LdrGetProcedureAddress(PVOID module,
544 PANSI_STRING import_name,
545 DWORD flags,
546 PVOID *func_addr);
547
548 void InitKbdLayout( PVOID *pkKeyboardLayout )
549 {
550 WCHAR LocaleBuffer[16];
551 UNICODE_STRING LayoutKeyName;
552 UNICODE_STRING LayoutValueName;
553 UNICODE_STRING DefaultLocale;
554 UNICODE_STRING LayoutFile;
555 UNICODE_STRING FullLayoutPath;
556 LCID LocaleId;
557 PWCHAR KeyboardLayoutWSTR;
558 HMODULE kbModule = 0;
559 NTSTATUS Status;
560 ANSI_STRING kbdProcedureName;
561 KbdLayerDescriptor layerDescGetFn;
562
563 #define XX_STATUS(x) if (!NT_SUCCESS(Status = (x))) continue;
564
565 do {
566 Status = ZwQueryDefaultLocale(FALSE, &LocaleId);
567 if (!NT_SUCCESS(Status))
568 {
569 DPRINT1("Could not get default locale (%08lx).\n", Status);
570 }
571 else
572 {
573 DPRINT("DefaultLocale = %lx\n", LocaleId);
574 swprintf(LocaleBuffer, L"%08lx", LocaleId);
575 DPRINT("DefaultLocale = %S\n", LocaleBuffer);
576 RtlInitUnicodeString(&DefaultLocale, LocaleBuffer);
577
578 RtlInitUnicodeString(&LayoutKeyName,
579 L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet"
580 L"\\Control\\KeyboardLayouts\\");
581
582 AppendUnicodeString(&LayoutKeyName,&DefaultLocale,FALSE);
583
584 RtlInitUnicodeString(&LayoutValueName,L"Layout File");
585
586 Status = ReadRegistryValue(&LayoutKeyName,&LayoutValueName,&LayoutFile);
587 RtlInitUnicodeString(&FullLayoutPath,SYSTEMROOT_DIR);
588
589 if( !NT_SUCCESS(Status) ) {
590 DPRINT1("Got default locale but not layout file. (%08lx)\n",
591 Status);
592 } else {
593 DPRINT("Read registry and got %wZ\n", &LayoutFile);
594
595 RtlFreeUnicodeString(&LayoutKeyName);
596
597 AppendUnicodeString(&FullLayoutPath,&LayoutFile,FALSE);
598
599 DPRINT("Loading Keyboard DLL %wZ\n", &FullLayoutPath);
600
601 RtlFreeUnicodeString(&LayoutFile);
602
603 KeyboardLayoutWSTR =
604 ExAllocatePoolWithTag(PagedPool,
605 FullLayoutPath.Length + sizeof(WCHAR),
606 TAG_STRING);
607
608 if( !KeyboardLayoutWSTR ) {
609 DPRINT1("Couldn't allocate a string for the keyboard layout name.\n");
610 RtlFreeUnicodeString(&FullLayoutPath);
611 return;
612 }
613 memcpy(KeyboardLayoutWSTR,FullLayoutPath.Buffer,
614 FullLayoutPath.Length + sizeof(WCHAR));
615 KeyboardLayoutWSTR[FullLayoutPath.Length / sizeof(WCHAR)] = 0;
616
617 kbModule = EngLoadImage(KeyboardLayoutWSTR);
618 DPRINT( "Load Keyboard Layout: %S\n", KeyboardLayoutWSTR );
619
620 if( !kbModule )
621 DPRINT1( "Load Keyboard Layout: No %wZ\n", &FullLayoutPath );
622
623 RtlFreeUnicodeString(&FullLayoutPath);
624 }
625 }
626
627 if( !kbModule )
628 {
629 DPRINT1("Trying to load US Keyboard Layout\n");
630 kbModule = EngLoadImage(L"\\SystemRoot\\system32\\kbdus.dll");
631
632 if (!kbModule)
633 {
634 DPRINT1("Failed to load any Keyboard Layout\n");
635 return;
636 }
637 }
638
639 RtlInitAnsiString( &kbdProcedureName, "KbdLayerDescriptor" );
640
641 LdrGetProcedureAddress((PVOID)kbModule,
642 &kbdProcedureName,
643 0,
644 (PVOID*)&layerDescGetFn);
645
646 if( layerDescGetFn ) {
647 *pkKeyboardLayout = layerDescGetFn();
648 }
649 } while (FALSE);
650
651 if( !*pkKeyboardLayout ) {
652 DPRINT1("Failed to load the keyboard layout.\n");
653 }
654
655 #undef XX_STATUS
656 }
657
658 PKBDTABLES W32kGetDefaultKeyLayout() {
659 PKBDTABLES pkKeyboardLayout = 0;
660 InitKbdLayout( (PVOID) &pkKeyboardLayout );
661 return pkKeyboardLayout;
662 }
663
664 BOOL FASTCALL
665 IntTranslateKbdMessage(LPMSG lpMsg,
666 HKL dwhkl)
667 {
668 static INT dead_char = 0;
669 LONG UState = 0;
670 WCHAR wp[2] = { 0 };
671 MSG NewMsg = { 0 };
672 PKBDTABLES keyLayout;
673 BOOL Result = FALSE;
674 DWORD ScanCode = 0;
675
676
677 keyLayout = PsGetWin32Thread()->KeyboardLayout;
678 if( !keyLayout )
679 return FALSE;
680
681 if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN)
682 return FALSE;
683
684 ScanCode = (lpMsg->lParam >> 16) & 0xff;
685
686 IntLockQueueState;
687
688 /* All messages have to contain the cursor point. */
689 IntGetCursorLocation(PsGetWin32Thread()->Desktop->WindowStation,
690 &NewMsg.pt);
691
692 UState = ToUnicodeInner(lpMsg->wParam, HIWORD(lpMsg->lParam) & 0xff,
693 QueueKeyStateTable, wp, 2, 0,
694 keyLayout );
695
696 if (UState == 1)
697 {
698 NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
699 if (dead_char)
700 {
701 ULONG i;
702 WCHAR first, second;
703 DPRINT("PREVIOUS DEAD CHAR: %c\n", dead_char);
704
705 for( i = 0; keyLayout->pDeadKey[i].dwBoth; i++ )
706 {
707 first = keyLayout->pDeadKey[i].dwBoth >> 16;
708 second = keyLayout->pDeadKey[i].dwBoth;
709 if (first == dead_char && second == wp[0])
710 {
711 wp[0] = keyLayout->pDeadKey[i].wchComposed;
712 dead_char = 0;
713 break;
714 }
715 }
716
717 DPRINT("FINAL CHAR: %c\n", wp[0]);
718 }
719
720 if (dead_char)
721 {
722 NewMsg.hwnd = lpMsg->hwnd;
723 NewMsg.wParam = dead_char;
724 NewMsg.lParam = lpMsg->lParam;
725 dead_char = 0;
726 MsqPostMessage(PsGetWin32Thread()->MessageQueue, &NewMsg, FALSE, QS_KEY);
727 }
728
729 NewMsg.hwnd = lpMsg->hwnd;
730 NewMsg.wParam = wp[0];
731 NewMsg.lParam = lpMsg->lParam;
732 DPRINT( "CHAR='%c' %04x %08x\n", wp[0], wp[0], lpMsg->lParam );
733 MsqPostMessage(PsGetWin32Thread()->MessageQueue, &NewMsg, FALSE, QS_KEY);
734 Result = TRUE;
735 }
736 else if (UState == -1)
737 {
738 NewMsg.message =
739 (lpMsg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
740 NewMsg.hwnd = lpMsg->hwnd;
741 NewMsg.wParam = wp[0];
742 NewMsg.lParam = lpMsg->lParam;
743 dead_char = wp[0];
744 MsqPostMessage(PsGetWin32Thread()->MessageQueue, &NewMsg, FALSE, QS_KEY);
745 Result = TRUE;
746 }
747
748 IntUnLockQueueState;
749 return Result;
750 }
751
752 DWORD
753 STDCALL
754 NtUserGetKeyboardState(
755 LPBYTE lpKeyState)
756 {
757 BOOL Result = TRUE;
758
759 IntLockQueueState;
760 if (lpKeyState) {
761 if(!NT_SUCCESS(MmCopyToCaller(lpKeyState, QueueKeyStateTable, 256)))
762 Result = FALSE;
763 }
764 IntUnLockQueueState;
765 return Result;
766 }
767
768 DWORD
769 STDCALL
770 NtUserSetKeyboardState(
771 LPBYTE lpKeyState)
772 {
773 BOOL Result = TRUE;
774
775 IntLockQueueState;
776 if (lpKeyState) {
777 if(! NT_SUCCESS(MmCopyFromCaller(QueueKeyStateTable, lpKeyState, 256)))
778 Result = FALSE;
779 }
780 IntUnLockQueueState;
781
782 return Result;
783 }
784
785 static UINT VkToScan( UINT Code, BOOL ExtCode, PKBDTABLES pkKT ) {
786 int i;
787
788 for( i = 0; i < pkKT->bMaxVSCtoVK; i++ ) {
789 if( pkKT->pusVSCtoVK[i] == Code ) { return i; }
790 }
791
792 return 0;
793 }
794
795 UINT ScanToVk( UINT Code, BOOL ExtKey, PKBDTABLES pkKT ) {
796 if( !pkKT ) {
797 DPRINT("ScanToVk: No layout\n");
798 return 0;
799 }
800
801 if( ExtKey ) {
802 int i;
803
804 for( i = 0; pkKT->pVSCtoVK_E0[i].Vsc; i++ ) {
805 if( pkKT->pVSCtoVK_E0[i].Vsc == Code )
806 return pkKT->pVSCtoVK_E0[i].Vk & 0xff;
807 }
808 for( i = 0; pkKT->pVSCtoVK_E1[i].Vsc; i++ ) {
809 if( pkKT->pVSCtoVK_E1[i].Vsc == Code )
810 return pkKT->pVSCtoVK_E1[i].Vk & 0xff;
811 }
812
813 return 0;
814 } else {
815 if( Code >= pkKT->bMaxVSCtoVK ) { return 0; }
816 return pkKT->pusVSCtoVK[Code] & 0xff;
817 }
818 }
819
820 /*
821 * Map a virtual key code, or virtual scan code, to a scan code, key code,
822 * or unshifted unicode character.
823 *
824 * Code: See Below
825 * Type:
826 * 0 -- Code is a virtual key code that is converted into a virtual scan code
827 * that does not distinguish between left and right shift keys.
828 * 1 -- Code is a virtual scan code that is converted into a virtual key code
829 * that does not distinguish between left and right shift keys.
830 * 2 -- Code is a virtual key code that is converted into an unshifted unicode
831 * character.
832 * 3 -- Code is a virtual scan code that is converted into a virtual key code
833 * that distinguishes left and right shift keys.
834 * KeyLayout: Keyboard layout handle (currently, unused)
835 *
836 * @implemented
837 */
838
839 static UINT IntMapVirtualKeyEx( UINT Code, UINT Type, PKBDTABLES keyLayout ) {
840 UINT ret = 0;
841
842 switch( Type ) {
843 case 0:
844 if( Code == VK_RSHIFT ) Code = VK_LSHIFT;
845 if( Code == VK_RMENU ) Code = VK_LMENU;
846 if( Code == VK_RCONTROL ) Code = VK_LCONTROL;
847 ret = VkToScan( Code, FALSE, keyLayout );
848 break;
849
850 case 1:
851 ret =
852 DontDistinguishShifts
853 (IntMapVirtualKeyEx( Code, 3, keyLayout ) );
854 break;
855
856 case 2: {
857 WCHAR wp[2];
858
859 ret = VkToScan( Code, FALSE, keyLayout );
860 ToUnicodeInner( Code, ret, 0, wp, 2, 0, keyLayout );
861 ret = wp[0];
862 } break;
863
864 case 3:
865
866 ret = ScanToVk( Code, FALSE, keyLayout );
867 break;
868 }
869
870 return ret;
871 }
872
873 UINT
874 STDCALL
875 NtUserMapVirtualKeyEx( UINT Code, UINT Type, DWORD keyboardId, HKL dwhkl ) {
876 PKBDTABLES keyLayout = PsGetWin32Thread() ?
877 PsGetWin32Thread()->KeyboardLayout : 0;
878
879 if( !keyLayout ) return 0;
880
881 return IntMapVirtualKeyEx( Code, Type, keyLayout );
882 }
883
884
885 int
886 STDCALL
887 NtUserToUnicodeEx(
888 UINT wVirtKey,
889 UINT wScanCode,
890 PBYTE lpKeyState,
891 LPWSTR pwszBuff,
892 int cchBuff,
893 UINT wFlags,
894 HKL dwhkl ) {
895 BYTE KeyStateBuf[0x100];
896 PWCHAR OutPwszBuff = 0;
897 int ret = 0;
898
899
900 if( !NT_SUCCESS(MmCopyFromCaller(KeyStateBuf,
901 lpKeyState,
902 sizeof(KeyStateBuf))) ) {
903 DPRINT1( "Couldn't copy key state from caller.\n" );
904 return 0;
905 }
906 OutPwszBuff = ExAllocatePoolWithTag(NonPagedPool,sizeof(WCHAR) * cchBuff, TAG_STRING);
907 if( !OutPwszBuff ) {
908 DPRINT1( "ExAllocatePool(%d) failed\n", sizeof(WCHAR) * cchBuff);
909 return 0;
910 }
911 RtlZeroMemory( OutPwszBuff, sizeof( WCHAR ) * cchBuff );
912
913 ret = ToUnicodeEx( wVirtKey,
914 wScanCode,
915 KeyStateBuf,
916 OutPwszBuff,
917 cchBuff,
918 wFlags,
919 dwhkl );
920
921 MmCopyToCaller(pwszBuff,OutPwszBuff,sizeof(WCHAR)*cchBuff);
922 ExFreePool(OutPwszBuff);
923
924 return ret;
925 }
926
927 static int W32kSimpleToupper( int ch ) {
928 if( ch >= 'a' && ch <= 'z' ) ch = ch - 'a' + 'A';
929 return ch;
930 }
931
932 DWORD
933 STDCALL
934 NtUserGetKeyNameText( LONG lParam, LPWSTR lpString, int nSize ) {
935 int i;
936 DWORD ret = 0;
937 UINT CareVk = 0;
938 UINT VkCode = 0;
939 UINT ScanCode = (lParam >> 16) & 0xff;
940 BOOL ExtKey = lParam & (1<<24) ? TRUE : FALSE;
941 PKBDTABLES keyLayout =
942 PsGetWin32Thread() ?
943 PsGetWin32Thread()->KeyboardLayout : 0;
944
945 if( !keyLayout || nSize < 1 ) return 0;
946
947 if( lParam & (1<<25) ) {
948 CareVk = VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
949 if( VkCode == VK_LSHIFT || VkCode == VK_RSHIFT )
950 VkCode = VK_LSHIFT;
951 if( VkCode == VK_LCONTROL || VkCode == VK_RCONTROL )
952 VkCode = VK_LCONTROL;
953 if( VkCode == VK_LMENU || VkCode == VK_RMENU )
954 VkCode = VK_LMENU;
955 } else {
956 VkCode = ScanToVk( ScanCode, ExtKey, keyLayout );
957 }
958
959 VSC_LPWSTR *KeyNames = 0;
960
961 if( CareVk != VkCode )
962 ScanCode = VkToScan( VkCode, ExtKey, keyLayout );
963
964 if( ExtKey )
965 KeyNames = keyLayout->pKeyNamesExt;
966 else
967 KeyNames = keyLayout->pKeyNames;
968
969 for( i = 0; KeyNames[i].pwsz; i++ ) {
970 if( KeyNames[i].vsc == ScanCode ) {
971 UINT StrLen = wcslen(KeyNames[i].pwsz);
972 UINT StrMax = StrLen > (nSize - 1) ? (nSize - 1) : StrLen;
973 WCHAR null_wc = 0;
974 if( NT_SUCCESS( MmCopyToCaller( lpString,
975 KeyNames[i].pwsz,
976 StrMax * sizeof(WCHAR) ) ) &&
977 NT_SUCCESS( MmCopyToCaller( lpString + StrMax,
978 &null_wc,
979 sizeof( WCHAR ) ) ) ) {
980 ret = StrMax;
981 break;
982 }
983 }
984 }
985
986 if( ret == 0 ) {
987 WCHAR UCName[2];
988
989 UCName[0] = W32kSimpleToupper(IntMapVirtualKeyEx( VkCode, 2, keyLayout ));
990 UCName[1] = 0;
991 ret = 1;
992
993 if( !NT_SUCCESS(MmCopyToCaller( lpString, UCName, 2 * sizeof(WCHAR) )) )
994 return 0;
995 }
996
997 return ret;
998 }
999
1000 /*
1001 * Filter this message according to the current key layout, setting wParam
1002 * appropriately.
1003 */
1004
1005 VOID FASTCALL
1006 W32kKeyProcessMessage(LPMSG Msg,
1007 PKBDTABLES KeyboardLayout,
1008 BYTE Prefix)
1009 {
1010 DWORD ScanCode = 0, ModifierBits = 0;
1011 DWORD i = 0;
1012 DWORD BaseMapping = 0;
1013 DWORD RawVk = 0;
1014 static WORD NumpadConversion[][2] =
1015 { { VK_DELETE, VK_DECIMAL },
1016 { VK_INSERT, VK_NUMPAD0 },
1017 { VK_END, VK_NUMPAD1 },
1018 { VK_DOWN, VK_NUMPAD2 },
1019 { VK_NEXT, VK_NUMPAD3 },
1020 { VK_LEFT, VK_NUMPAD4 },
1021 { VK_CLEAR, VK_NUMPAD5 },
1022 { VK_RIGHT, VK_NUMPAD6 },
1023 { VK_HOME, VK_NUMPAD7 },
1024 { VK_UP, VK_NUMPAD8 },
1025 { VK_PRIOR, VK_NUMPAD9 },
1026 { 0,0 } };
1027 PVSC_VK VscVkTable;
1028
1029 if( !KeyboardLayout || !Msg ||
1030 (Msg->message != WM_KEYDOWN && Msg->message != WM_SYSKEYDOWN &&
1031 Msg->message != WM_KEYUP && Msg->message != WM_SYSKEYUP) )
1032 {
1033 return;
1034 }
1035
1036 IntLockQueueState;
1037
1038 /* arty -- handle numpad -- On real windows, the actual key produced
1039 * by the messaging layer is different based on the state of numlock. */
1040 ModifierBits = ModBits(KeyboardLayout,QueueKeyStateTable);
1041
1042 /* Get the raw scan code, so we can look up whether the key is a numpad
1043 * key
1044 *
1045 * Shift and the LP_EXT_BIT cancel. */
1046 ScanCode = (Msg->lParam >> 16) & 0xff;
1047 BaseMapping = Msg->wParam =
1048 IntMapVirtualKeyEx( ScanCode, 1, KeyboardLayout );
1049 if( Prefix == 0 )
1050 {
1051 if( ScanCode >= KeyboardLayout->bMaxVSCtoVK )
1052 RawVk = 0xff;
1053 else
1054 RawVk = KeyboardLayout->pusVSCtoVK[ScanCode];
1055 }
1056 else
1057 {
1058 if( Prefix == 0xE0 )
1059 {
1060 /* ignore shift codes */
1061 if( ScanCode == 0x2A || ScanCode == 0x36 )
1062 {
1063 IntUnLockQueueState;
1064 return;
1065 }
1066 VscVkTable = KeyboardLayout->pVSCtoVK_E0;
1067 }
1068 else if( Prefix == 0xE1 )
1069 {
1070 VscVkTable = KeyboardLayout->pVSCtoVK_E1;
1071 }
1072
1073 RawVk = 0xff;
1074 while (VscVkTable->Vsc)
1075 {
1076 if( VscVkTable->Vsc == ScanCode )
1077 {
1078 RawVk = VscVkTable->Vk;
1079 }
1080 VscVkTable++;
1081 }
1082 }
1083
1084 if ((ModifierBits & NUMLOCK_BIT) &&
1085 !(ModifierBits & GetShiftBit(KeyboardLayout, VK_SHIFT)) &&
1086 (RawVk & KNUMP) &&
1087 !(Msg->lParam & LP_EXT_BIT))
1088 {
1089 /* The key in question is a numpad key. Search for a translation. */
1090 for (i = 0; NumpadConversion[i][0]; i++)
1091 {
1092 if ((BaseMapping & 0xff) == NumpadConversion[i][0]) /* RawVk? */
1093 {
1094 Msg->wParam = NumpadConversion[i][1];
1095 break;
1096 }
1097 }
1098 }
1099
1100 DPRINT("Key: [%04x -> %04x]\n", BaseMapping, Msg->wParam);
1101
1102 /* Now that we have the VK, we can set the keymap appropriately
1103 * This is a better place for this code, as it's guaranteed to be
1104 * run, unlike translate message. */
1105 if (Msg->message == WM_KEYDOWN || Msg->message == WM_SYSKEYDOWN)
1106 {
1107 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
1108 TRUE ); /* Strike key */
1109 }
1110 else if (Msg->message == WM_KEYUP || Msg->message == WM_SYSKEYUP)
1111 {
1112 SetKeyState( ScanCode, Msg->wParam, Msg->lParam & LP_EXT_BIT,
1113 FALSE ); /* Release key */
1114 }
1115
1116 /* We need to unset SYSKEYDOWN if the ALT key is an ALT+Gr */
1117 if( QueueKeyStateTable[VK_RMENU] & KS_DOWN_BIT ) {
1118 if( Msg->message == WM_SYSKEYDOWN ) Msg->message = WM_KEYDOWN;
1119 else Msg->message = WM_KEYUP;
1120 }
1121
1122 IntUnLockQueueState;
1123 }
1124
1125 DWORD
1126 STDCALL
1127 NtUserGetKeyboardLayoutList(
1128 DWORD Items,
1129 DWORD pHklBuff)
1130 {
1131 UNIMPLEMENTED
1132
1133 return 0;
1134 }
1135
1136 DWORD
1137 STDCALL
1138 NtUserGetKeyboardLayoutName(
1139 DWORD lpszName)
1140 {
1141 UNIMPLEMENTED
1142
1143 return 0;
1144 }
1145
1146
1147
1148 HKL FASTCALL
1149 UserGetKeyboardLayout(
1150 DWORD dwThreadId)
1151 {
1152 NTSTATUS Status;
1153 PETHREAD Thread;
1154 PW32THREAD W32Thread;
1155 PKBDTABLES layout;
1156
1157 if (!dwThreadId)
1158 W32Thread = PsGetWin32Thread();
1159 else
1160 {
1161 Status = PsLookupThreadByThreadId((HANDLE)dwThreadId, &Thread);//fixme: deref thread
1162 if(!NT_SUCCESS(Status))
1163 {
1164 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1165 return 0;
1166 }
1167 W32Thread = Thread->Tcb.Win32Thread;
1168 }
1169 layout = W32Thread->KeyboardLayout;
1170 if(!layout) return 0;
1171 return (HKL)layout;
1172 }
1173
1174
1175 HKL
1176 STDCALL
1177 NtUserGetKeyboardLayout(
1178 DWORD dwThreadId)
1179 {
1180 DECLARE_RETURN(HKL);
1181
1182 UserEnterShared();
1183 DPRINT("Enter NtUserGetKeyboardLayout\n");
1184
1185 RETURN( UserGetKeyboardLayout(dwThreadId));
1186
1187 CLEANUP:
1188 DPRINT("Leave NtUserGetKeyboardLayout, ret=%i\n",_ret_);
1189 UserLeave();
1190 END_CLEANUP;
1191 }
1192
1193
1194 DWORD FASTCALL
1195 UserGetKeyboardType(
1196 DWORD TypeFlag)
1197 {
1198 switch(TypeFlag)
1199 {
1200 case 0: /* Keyboard type */
1201 return 4; /* AT-101 */
1202 case 1: /* Keyboard Subtype */
1203 return 0; /* There are no defined subtypes */
1204 case 2: /* Number of F-keys */
1205 return 12; /* We're doing an 101 for now, so return 12 F-keys */
1206 default:
1207 DPRINT1("Unknown type!\n");
1208 return 0; /* The book says 0 here, so 0 */
1209 }
1210 }
1211
1212 DWORD
1213 STDCALL
1214 NtUserGetKeyboardType(
1215 DWORD TypeFlag)
1216 {
1217 return UserGetKeyboardType(TypeFlag);
1218 }
1219
1220
1221 /*
1222 Based on TryToTranslateChar, instead of processing VirtualKey match,
1223 look for wChar match.
1224 */
1225 DWORD
1226 STDCALL
1227 NtUserVkKeyScanEx(
1228 DWORD wChar,
1229 DWORD KeyboardLayout,
1230 DWORD Unknown2)
1231 {
1232 PKBDTABLES KeyLayout;
1233 PVK_TO_WCHAR_TABLE vtwTbl;
1234 PVK_TO_WCHARS10 vkPtr;
1235 size_t size_this_entry;
1236 int nMod;
1237 DWORD CapsMod = 0, CapsState = 0;
1238
1239 if(!KeyboardLayout) return -1;
1240 KeyLayout = (PKBDTABLES) KeyboardLayout;
1241
1242 for (nMod = 0; KeyLayout->pVkToWcharTable[nMod].nModifications; nMod++)
1243 {
1244 vtwTbl = &KeyLayout->pVkToWcharTable[nMod];
1245 size_this_entry = vtwTbl->cbSize;
1246 vkPtr = (PVK_TO_WCHARS10)((BYTE *)vtwTbl->pVkToWchars);
1247
1248 while(vkPtr->VirtualKey)
1249 {
1250 /*
1251 0x01 Shift key
1252 0x02 Ctrl key
1253 0x04 Alt key
1254 Should have only 8 valid possibilities. Including zero.
1255 */
1256 for(CapsState = 0; CapsState < vtwTbl->nModifications; CapsState++)
1257 {
1258 if(vkPtr->wch[CapsState] == wChar)
1259 {
1260 CapsMod = KeyLayout->pCharModifiers->ModNumber[CapsState];
1261 DPRINT("nMod %d wC %04x: CapsMod %08x CapsState %08x MaxModBits %08x\n",
1262 nMod, wChar, CapsMod, CapsState, KeyLayout->pCharModifiers->wMaxModBits);
1263 return ((CapsMod << 8)|(vkPtr->VirtualKey & 0xff));
1264 }
1265 }
1266 vkPtr = (PVK_TO_WCHARS10)(((BYTE *)vkPtr) + size_this_entry);
1267 }
1268 }
1269 return -1;
1270 }
1271
1272
1273 /* EOF */