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