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