xref: /reactos/win32ss/user/user32/windows/input.c (revision cdf90707)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 1998, 1999, 2000, 2001 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  * PROJECT:         ReactOS user32.dll
21  * FILE:            win32ss/user/user32/windows/input.c
22  * PURPOSE:         Input
23  * PROGRAMMER:      Casper S. Hornstrup (chorns@users.sourceforge.net)
24  * UPDATE HISTORY:
25  *      09-05-2001  CSH  Created
26  */
27 
28 #include <user32.h>
29 
30 #include <strsafe.h>
31 
32 WINE_DEFAULT_DEBUG_CHANNEL(user32);
33 
34 typedef struct tagIMEHOTKEYENTRY
35 {
36     DWORD  dwHotKeyId;
37     UINT   uVirtualKey;
38     UINT   uModifiers;
39     HKL    hKL;
40 } IMEHOTKEYENTRY, *PIMEHOTKEYENTRY;
41 
42 // Japanese
43 IMEHOTKEYENTRY DefaultHotKeyTableJ[] =
44 {
45     { IME_JHOTKEY_CLOSE_OPEN, VK_KANJI, MOD_IGNORE_ALL_MODIFIER, NULL },
46 };
47 
48 // Chinese Traditional
49 IMEHOTKEYENTRY DefaultHotKeyTableT[] =
50 {
51     { IME_THOTKEY_IME_NONIME_TOGGLE, VK_SPACE, MOD_LEFT | MOD_RIGHT | MOD_CONTROL, NULL },
52     { IME_THOTKEY_SHAPE_TOGGLE, VK_SPACE, MOD_LEFT | MOD_RIGHT | MOD_SHIFT, NULL },
53 };
54 
55 // Chinese Simplified
56 IMEHOTKEYENTRY DefaultHotKeyTableC[] =
57 {
58     { IME_CHOTKEY_IME_NONIME_TOGGLE, VK_SPACE, MOD_LEFT | MOD_RIGHT | MOD_CONTROL, NULL },
59     { IME_CHOTKEY_SHAPE_TOGGLE, VK_SPACE, MOD_LEFT | MOD_RIGHT | MOD_SHIFT, NULL },
60 };
61 
62 // The far-east flags
63 #define FE_JAPANESE             (1 << 0)
64 #define FE_CHINESE_TRADITIONAL  (1 << 1)
65 #define FE_CHINESE_SIMPLIFIED   (1 << 2)
66 #define FE_KOREAN               (1 << 3)
67 
68 // Sets the far-east flags
69 // Win: SetFeKeyboardFlags
70 VOID FASTCALL IntSetFeKeyboardFlags(LANGID LangID, PBYTE pbFlags)
71 {
72     switch (LangID)
73     {
74         case MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT):
75             *pbFlags |= FE_JAPANESE;
76             break;
77 
78         case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL):
79         case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG):
80             *pbFlags |= FE_CHINESE_TRADITIONAL;
81             break;
82 
83         case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED):
84         case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE):
85             *pbFlags |= FE_CHINESE_SIMPLIFIED;
86             break;
87 
88         case MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN):
89             *pbFlags |= FE_KOREAN;
90             break;
91 
92         default:
93             break;
94     }
95 }
96 
97 DWORD FASTCALL CliReadRegistryValue(HANDLE hKey, LPCWSTR pszName)
98 {
99     DWORD dwValue, cbValue;
100     LONG error;
101 
102     cbValue = sizeof(dwValue);
103     error = RegQueryValueExW(hKey, pszName, NULL, NULL, (LPBYTE)&dwValue, &cbValue);
104     if (error != ERROR_SUCCESS || cbValue < sizeof(DWORD))
105         return 0;
106 
107     return dwValue;
108 }
109 
110 BOOL APIENTRY
111 CliImmSetHotKeyWorker(DWORD dwHotKeyId, UINT uModifiers, UINT uVirtualKey, HKL hKL, DWORD dwAction)
112 {
113     if (dwAction == SETIMEHOTKEY_ADD)
114     {
115         if (IME_HOTKEY_DSWITCH_FIRST <= dwHotKeyId && dwHotKeyId <= IME_HOTKEY_DSWITCH_LAST)
116         {
117             if (!hKL)
118                 goto Failure;
119         }
120         else
121         {
122             if (hKL)
123                 goto Failure;
124 
125             if (IME_KHOTKEY_SHAPE_TOGGLE <= dwHotKeyId &&
126                 dwHotKeyId < IME_THOTKEY_IME_NONIME_TOGGLE)
127             {
128                 // The Korean cannot set the IME hotkeys
129                 goto Failure;
130             }
131         }
132 
133 #define MOD_ALL_MODS (MOD_ALT | MOD_CONTROL | MOD_SHIFT | MOD_WIN)
134         if ((uModifiers & MOD_ALL_MODS) && !(uModifiers & (MOD_LEFT | MOD_RIGHT)))
135             goto Failure;
136 #undef MOD_ALL_MODS
137     }
138 
139     return NtUserSetImeHotKey(dwHotKeyId, uModifiers, uVirtualKey, hKL, dwAction);
140 
141 Failure:
142     SetLastError(ERROR_INVALID_PARAMETER);
143     return FALSE;
144 }
145 
146 
147 /* Win: LoadPreloadKeyboardLayouts */
148 VOID IntLoadPreloadKeyboardLayouts(VOID)
149 {
150     UINT nNumber, uFlags;
151     DWORD cbValue, dwType;
152     WCHAR szNumber[32], szValue[KL_NAMELENGTH];
153     HKEY hPreloadKey;
154     BOOL bOK = FALSE;
155     HKL hKL, hDefaultKL = NULL;
156 
157     if (RegOpenKeyW(HKEY_CURRENT_USER,
158                     L"Keyboard Layout\\Preload",
159                     &hPreloadKey) != ERROR_SUCCESS)
160     {
161         return;
162     }
163 
164     for (nNumber = 1; nNumber <= 1000; ++nNumber)
165     {
166         _ultow(nNumber, szNumber, 10);
167 
168         cbValue = sizeof(szValue);
169         if (RegQueryValueExW(hPreloadKey,
170                              szNumber,
171                              NULL,
172                              &dwType,
173                              (LPBYTE)szValue,
174                              &cbValue) != ERROR_SUCCESS)
175         {
176             break;
177         }
178 
179         if (dwType != REG_SZ)
180             continue;
181 
182         if (nNumber == 1) /* The first entry is for default keyboard layout */
183             uFlags = KLF_SUBSTITUTE_OK | KLF_ACTIVATE | KLF_RESET;
184         else
185             uFlags = KLF_SUBSTITUTE_OK | KLF_NOTELLSHELL | KLF_REPLACELANG;
186 
187         hKL = LoadKeyboardLayoutW(szValue, uFlags);
188         if (hKL)
189         {
190             bOK = TRUE;
191             if (nNumber == 1) /* The first entry */
192                 hDefaultKL = hKL;
193         }
194     }
195 
196     RegCloseKey(hPreloadKey);
197 
198     if (hDefaultKL)
199         SystemParametersInfoW(SPI_SETDEFAULTINPUTLANG, 0, &hDefaultKL, 0);
200 
201     if (!bOK)
202     {
203         /* Fallback to English (US) */
204         LoadKeyboardLayoutW(L"00000409", KLF_SUBSTITUTE_OK | KLF_ACTIVATE | KLF_RESET);
205     }
206 }
207 
208 
209 BOOL APIENTRY
210 CliSaveImeHotKey(DWORD dwID, UINT uModifiers, UINT uVirtualKey, HKL hKL, BOOL bDelete)
211 {
212     WCHAR szName[MAX_PATH];
213     LONG error;
214     HKEY hControlPanel = NULL, hInputMethod = NULL, hHotKeys = NULL, hKey = NULL;
215     BOOL ret = FALSE, bRevertOnFailure = FALSE;
216 
217     if (bDelete)
218     {
219         StringCchPrintfW(szName, _countof(szName),
220                          L"Control Panel\\Input Method\\Hot Keys\\%08lX", dwID);
221         error = RegDeleteKeyW(HKEY_CURRENT_USER, szName);
222         return (error == ERROR_SUCCESS);
223     }
224 
225     // Open "Control Panel"
226     error = RegCreateKeyExW(HKEY_CURRENT_USER, L"Control Panel", 0, NULL, 0, KEY_ALL_ACCESS,
227                             NULL, &hControlPanel, NULL);
228     if (error == ERROR_SUCCESS)
229     {
230         // Open "Input Method"
231         error = RegCreateKeyExW(hControlPanel, L"Input Method", 0, NULL, 0, KEY_ALL_ACCESS,
232                                 NULL, &hInputMethod, NULL);
233         if (error == ERROR_SUCCESS)
234         {
235             // Open "Hot Keys"
236             error = RegCreateKeyExW(hInputMethod, L"Hot Keys", 0, NULL, 0, KEY_ALL_ACCESS,
237                                     NULL, &hHotKeys, NULL);
238             if (error == ERROR_SUCCESS)
239             {
240                 // Open "Key"
241                 StringCchPrintfW(szName, _countof(szName), L"%08lX", dwID);
242                 error = RegCreateKeyExW(hHotKeys, szName, 0, NULL, 0, KEY_ALL_ACCESS,
243                                         NULL, &hKey, NULL);
244                 if (error == ERROR_SUCCESS)
245                 {
246                     bRevertOnFailure = TRUE;
247 
248                     // Set "Virtual Key"
249                     error = RegSetValueExW(hKey, L"Virtual Key", 0, REG_BINARY,
250                                            (LPBYTE)&uVirtualKey, sizeof(uVirtualKey));
251                     if (error == ERROR_SUCCESS)
252                     {
253                         // Set "Key Modifiers"
254                         error = RegSetValueExW(hKey, L"Key Modifiers", 0, REG_BINARY,
255                                                (LPBYTE)&uModifiers, sizeof(uModifiers));
256                         if (error == ERROR_SUCCESS)
257                         {
258                             // Set "Target IME"
259                             error = RegSetValueExW(hKey, L"Target IME", 0, REG_BINARY,
260                                                    (LPBYTE)&hKL, sizeof(hKL));
261                             if (error == ERROR_SUCCESS)
262                             {
263                                 // Success!
264                                 ret = TRUE;
265                                 bRevertOnFailure = FALSE;
266                             }
267                         }
268                     }
269                     RegCloseKey(hKey);
270                 }
271                 RegCloseKey(hHotKeys);
272             }
273             RegCloseKey(hInputMethod);
274         }
275         RegCloseKey(hControlPanel);
276     }
277 
278     if (bRevertOnFailure)
279         CliSaveImeHotKey(dwID, uVirtualKey, uModifiers, hKL, TRUE);
280 
281     return ret;
282 }
283 
284 /*
285  * @implemented
286  * Same as imm32!ImmSetHotKey.
287  */
288 BOOL WINAPI CliImmSetHotKey(DWORD dwID, UINT uModifiers, UINT uVirtualKey, HKL hKL)
289 {
290     BOOL ret;
291 
292     if (uVirtualKey == 0) // Delete?
293     {
294         ret = CliSaveImeHotKey(dwID, uModifiers, uVirtualKey, hKL, TRUE);
295         if (ret)
296             CliImmSetHotKeyWorker(dwID, uModifiers, uVirtualKey, hKL, SETIMEHOTKEY_DELETE);
297         return ret;
298     }
299 
300     // Add
301     ret = CliImmSetHotKeyWorker(dwID, uModifiers, uVirtualKey, hKL, SETIMEHOTKEY_ADD);
302     if (ret)
303     {
304         ret = CliSaveImeHotKey(dwID, uModifiers, uVirtualKey, hKL, FALSE);
305         if (!ret) // Failure?
306             CliImmSetHotKeyWorker(dwID, uModifiers, uVirtualKey, hKL, SETIMEHOTKEY_DELETE);
307     }
308 
309     return ret;
310 }
311 
312 BOOL FASTCALL CliSetSingleHotKey(LPCWSTR pszSubKey, HANDLE hKey)
313 {
314     LONG error;
315     HKEY hSubKey;
316     DWORD dwHotKeyId = 0;
317     UINT uModifiers = 0, uVirtualKey = 0;
318     HKL hKL = NULL;
319     UNICODE_STRING ustrName;
320 
321     error = RegOpenKeyExW(hKey, pszSubKey, 0, KEY_READ, &hSubKey);
322     if (error != ERROR_SUCCESS)
323         return FALSE;
324 
325     RtlInitUnicodeString(&ustrName, pszSubKey);
326     RtlUnicodeStringToInteger(&ustrName, 16, &dwHotKeyId);
327 
328     uModifiers = CliReadRegistryValue(hSubKey, L"Key Modifiers");
329     hKL = (HKL)(ULONG_PTR)CliReadRegistryValue(hSubKey, L"Target IME");
330     uVirtualKey = CliReadRegistryValue(hSubKey, L"Virtual Key");
331 
332     RegCloseKey(hSubKey);
333 
334     return CliImmSetHotKeyWorker(dwHotKeyId, uModifiers, uVirtualKey, hKL, SETIMEHOTKEY_ADD);
335 }
336 
337 BOOL FASTCALL CliGetImeHotKeysFromRegistry(VOID)
338 {
339     HKEY hKey;
340     LONG error;
341     BOOL ret = FALSE;
342     DWORD dwIndex, cchKeyName;
343     WCHAR szKeyName[16];
344 
345     error = RegOpenKeyExW(HKEY_CURRENT_USER,
346                           L"Control Panel\\Input Method\\Hot Keys",
347                           0,
348                           KEY_ALL_ACCESS,
349                           &hKey);
350     if (error != ERROR_SUCCESS)
351         return ret;
352 
353     for (dwIndex = 0; ; ++dwIndex)
354     {
355         cchKeyName = _countof(szKeyName);
356         error = RegEnumKeyExW(hKey, dwIndex, szKeyName, &cchKeyName, NULL, NULL, NULL, NULL);
357         if (error == ERROR_NO_MORE_ITEMS || error != ERROR_SUCCESS)
358             break;
359 
360         szKeyName[_countof(szKeyName) - 1] = 0;
361 
362         if (CliSetSingleHotKey(szKeyName, hKey))
363             ret = TRUE;
364     }
365 
366     RegCloseKey(hKey);
367     return ret;
368 }
369 
370 VOID APIENTRY CliGetPreloadKeyboardLayouts(PBYTE pbFlags)
371 {
372     WCHAR szValueName[8], szValue[16];
373     UNICODE_STRING ustrValue;
374     DWORD dwKL, cbValue, dwType;
375     UINT iNumber;
376     HKEY hKey;
377     LONG error;
378 
379     error = RegOpenKeyExW(HKEY_CURRENT_USER, L"Keyboard Layout\\Preload", 0, KEY_READ, &hKey);
380     if (error != ERROR_SUCCESS)
381         return;
382 
383     for (iNumber = 1; iNumber < 1000; ++iNumber)
384     {
385         StringCchPrintfW(szValueName, _countof(szValueName), L"%u", iNumber);
386 
387         cbValue = sizeof(szValue);
388         error = RegQueryValueExW(hKey, szValueName, NULL, &dwType, (LPBYTE)szValue, &cbValue);
389         if (error != ERROR_SUCCESS || dwType != REG_SZ)
390             break;
391 
392         szValue[_countof(szValue) - 1] = 0;
393 
394         RtlInitUnicodeString(&ustrValue, szValue);
395         RtlUnicodeStringToInteger(&ustrValue, 16, &dwKL);
396 
397         IntSetFeKeyboardFlags(LOWORD(dwKL), pbFlags);
398     }
399 
400     RegCloseKey(hKey);
401 }
402 
403 VOID APIENTRY CliSetDefaultImeHotKeys(PIMEHOTKEYENTRY pEntries, UINT nCount, BOOL bCheck)
404 {
405     UINT uVirtualKey, uModifiers;
406     HKL hKL;
407 
408     while (nCount-- > 0)
409     {
410         if (!bCheck || !NtUserGetImeHotKey(pEntries->dwHotKeyId, &uModifiers, &uVirtualKey, &hKL))
411         {
412             CliImmSetHotKeyWorker(pEntries->dwHotKeyId,
413                                   pEntries->uModifiers,
414                                   pEntries->uVirtualKey,
415                                   pEntries->hKL,
416                                   SETIMEHOTKEY_ADD);
417         }
418         ++pEntries;
419     }
420 }
421 
422 VOID APIENTRY CliImmInitializeHotKeys(DWORD dwAction, HKL hKL)
423 {
424     UINT nCount;
425     LPHKL pList;
426     UINT iIndex;
427     LANGID LangID;
428     BYTE bFlags = 0;
429     BOOL bCheck;
430 
431     NtUserSetImeHotKey(0, 0, 0, NULL, SETIMEHOTKEY_DELETEALL);
432 
433     bCheck = CliGetImeHotKeysFromRegistry();
434 
435     if (dwAction == SETIMEHOTKEY_DELETEALL)
436     {
437         LangID = LANGIDFROMLCID(GetUserDefaultLCID());
438         IntSetFeKeyboardFlags(LangID, &bFlags);
439 
440         CliGetPreloadKeyboardLayouts(&bFlags);
441     }
442     else
443     {
444         nCount = NtUserGetKeyboardLayoutList(0, NULL);
445         if (!nCount)
446             return;
447 
448         pList = RtlAllocateHeap(RtlGetProcessHeap(), 0, nCount * sizeof(HKL));
449         if (!pList)
450             return;
451 
452         NtUserGetKeyboardLayoutList(nCount, pList);
453 
454         for (iIndex = 0; iIndex < nCount; ++iIndex)
455         {
456             LangID = LOWORD(pList[iIndex]);
457             IntSetFeKeyboardFlags(LangID, &bFlags);
458         }
459 
460         RtlFreeHeap(RtlGetProcessHeap(), 0, pList);
461     }
462 
463     if (bFlags & FE_JAPANESE)
464         CliSetDefaultImeHotKeys(DefaultHotKeyTableJ, _countof(DefaultHotKeyTableJ), bCheck);
465 
466     if (bFlags & FE_CHINESE_TRADITIONAL)
467         CliSetDefaultImeHotKeys(DefaultHotKeyTableT, _countof(DefaultHotKeyTableT), bCheck);
468 
469     if (bFlags & FE_CHINESE_SIMPLIFIED)
470         CliSetDefaultImeHotKeys(DefaultHotKeyTableC, _countof(DefaultHotKeyTableC), bCheck);
471 }
472 
473 /*
474  * @implemented
475  */
476 BOOL
477 WINAPI
478 DragDetect(
479     HWND hWnd,
480     POINT pt)
481 {
482     return NtUserDragDetect(hWnd, pt);
483 #if 0
484     MSG msg;
485     RECT rect;
486     POINT tmp;
487     ULONG dx = GetSystemMetrics(SM_CXDRAG);
488     ULONG dy = GetSystemMetrics(SM_CYDRAG);
489 
490     rect.left = pt.x - dx;
491     rect.right = pt.x + dx;
492     rect.top = pt.y - dy;
493     rect.bottom = pt.y + dy;
494 
495     SetCapture(hWnd);
496 
497     for (;;)
498     {
499         while (
500             PeekMessageW(&msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) ||
501             PeekMessageW(&msg, 0, WM_KEYFIRST,   WM_KEYLAST,   PM_REMOVE)
502         )
503         {
504             if (msg.message == WM_LBUTTONUP)
505             {
506                 ReleaseCapture();
507                 return FALSE;
508             }
509             if (msg.message == WM_MOUSEMOVE)
510             {
511                 tmp.x = LOWORD(msg.lParam);
512                 tmp.y = HIWORD(msg.lParam);
513                 if (!PtInRect(&rect, tmp))
514                 {
515                     ReleaseCapture();
516                     return TRUE;
517                 }
518             }
519             if (msg.message == WM_KEYDOWN)
520             {
521                 if (msg.wParam == VK_ESCAPE)
522                 {
523                     ReleaseCapture();
524                     return TRUE;
525                 }
526             }
527         }
528         WaitMessage();
529     }
530     return 0;
531 #endif
532 }
533 
534 /*
535  * @implemented
536  */
537 BOOL WINAPI
538 EnableWindow(HWND hWnd, BOOL bEnable)
539 {
540     return NtUserxEnableWindow(hWnd, bEnable);
541 }
542 
543 /*
544  * @implemented
545  */
546 SHORT
547 WINAPI
548 DECLSPEC_HOTPATCH
549 GetAsyncKeyState(int vKey)
550 {
551     if (vKey < 0 || vKey > 256)
552         return 0;
553     return (SHORT)NtUserGetAsyncKeyState((DWORD)vKey);
554 }
555 
556 
557 /*
558  * @implemented
559  */
560 HKL WINAPI
561 GetKeyboardLayout(DWORD idThread)
562 {
563     return NtUserxGetKeyboardLayout(idThread);
564 }
565 
566 
567 /*
568  * @implemented
569  */
570 UINT WINAPI
571 GetKBCodePage(VOID)
572 {
573     return GetOEMCP();
574 }
575 
576 
577 /*
578  * @implemented
579  */
580 int WINAPI
581 GetKeyNameTextA(LONG lParam,
582                 LPSTR lpString,
583                 int nSize)
584 {
585     LPWSTR pwszBuf;
586     UINT cchBuf = 0;
587     int iRet = 0;
588     BOOL defChar = FALSE;
589 
590     pwszBuf = HeapAlloc(GetProcessHeap(), 0, nSize * sizeof(WCHAR));
591     if (!pwszBuf)
592         return 0;
593 
594     cchBuf = NtUserGetKeyNameText(lParam, pwszBuf, nSize);
595 
596     iRet = WideCharToMultiByte(CP_ACP, 0,
597                               pwszBuf, cchBuf,
598                               lpString, nSize, ".", &defChar); // FIXME: do we need defChar?
599     lpString[iRet] = 0;
600     HeapFree(GetProcessHeap(), 0, pwszBuf);
601 
602     return iRet;
603 }
604 
605 /*
606  * @implemented
607  */
608 int WINAPI
609 GetKeyNameTextW(LONG lParam,
610                 LPWSTR lpString,
611                 int nSize)
612 {
613     return NtUserGetKeyNameText(lParam, lpString, nSize);
614 }
615 
616 /*
617  * @implemented
618  */
619 SHORT
620 WINAPI
621 DECLSPEC_HOTPATCH
622 GetKeyState(int nVirtKey)
623 {
624     return (SHORT)NtUserGetKeyState((DWORD)nVirtKey);
625 }
626 
627 /*
628  * @implemented
629  */
630 BOOL WINAPI
631 GetKeyboardLayoutNameA(LPSTR pwszKLID)
632 {
633     WCHAR buf[KL_NAMELENGTH];
634 
635     if (!GetKeyboardLayoutNameW(buf))
636         return FALSE;
637 
638     if (!WideCharToMultiByte(CP_ACP, 0, buf, -1, pwszKLID, KL_NAMELENGTH, NULL, NULL))
639         return FALSE;
640 
641     return TRUE;
642 }
643 
644 /*
645  * @implemented
646  */
647 BOOL WINAPI
648 GetKeyboardLayoutNameW(LPWSTR pwszKLID)
649 {
650     UNICODE_STRING Name;
651 
652     RtlInitEmptyUnicodeString(&Name,
653                               pwszKLID,
654                               KL_NAMELENGTH * sizeof(WCHAR));
655 
656     return NtUserGetKeyboardLayoutName(&Name);
657 }
658 
659 /*
660  * @implemented
661  */
662 int WINAPI
663 GetKeyboardType(int nTypeFlag)
664 {
665     return NtUserxGetKeyboardType(nTypeFlag);
666 }
667 
668 /*
669  * @implemented
670  */
671 BOOL WINAPI
672 GetLastInputInfo(PLASTINPUTINFO plii)
673 {
674     TRACE("%p\n", plii);
675 
676     if (plii->cbSize != sizeof (*plii))
677     {
678         SetLastError(ERROR_INVALID_PARAMETER);
679         return FALSE;
680     }
681 
682     plii->dwTime = gpsi->dwLastRITEventTickCount;
683     return TRUE;
684 }
685 
686 /*
687  * @implemented
688  */
689 HKL WINAPI
690 LoadKeyboardLayoutA(LPCSTR pszKLID,
691                     UINT Flags)
692 {
693     WCHAR wszKLID[16];
694 
695     if (!MultiByteToWideChar(CP_ACP, 0, pszKLID, -1,
696                              wszKLID, sizeof(wszKLID)/sizeof(wszKLID[0])))
697     {
698         return FALSE;
699     }
700 
701     return LoadKeyboardLayoutW(wszKLID, Flags);
702 }
703 
704 inline BOOL IsValidKLID(_In_ LPCWSTR pwszKLID)
705 {
706     return (pwszKLID != NULL) && (wcsspn(pwszKLID, L"0123456789ABCDEFabcdef") == (KL_NAMELENGTH - 1));
707 }
708 
709 VOID GetSystemLibraryPath(LPWSTR pszPath, INT cchPath, LPCWSTR pszFileName)
710 {
711     WCHAR szSysDir[MAX_PATH];
712     GetSystemDirectoryW(szSysDir, _countof(szSysDir));
713     StringCchPrintfW(pszPath, cchPath, L"%s\\%s", szSysDir, pszFileName);
714 }
715 
716 #define ENGLISH_US MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)
717 
718 /*
719  * @unimplemented
720  *
721  * NOTE: We adopt a different design from Microsoft's one for security reason.
722  */
723 /* Win: LoadKeyboardLayoutWorker */
724 HKL APIENTRY
725 IntLoadKeyboardLayout(
726     _In_    HKL     hklUnload,
727     _In_z_  LPCWSTR pwszKLID,
728     _In_    LANGID  wLangID,
729     _In_    UINT    Flags,
730     _In_    BOOL    unknown5)
731 {
732     DWORD dwKLID, dwHKL, dwType, dwSize;
733     UNICODE_STRING ustrKbdName;
734     UNICODE_STRING ustrKLID;
735     WCHAR wszRegKey[256] = L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\";
736     WCHAR wszLayoutId[10], wszNewKLID[KL_NAMELENGTH], szImeFileName[80];
737     HKL hNewKL;
738     HKEY hKey;
739     BOOL bIsIME;
740     WORD wLow, wHigh;
741 
742     if (!IsValidKLID(pwszKLID))
743     {
744         ERR("pwszKLID: %s\n", debugstr_w(pwszKLID));
745         return UlongToHandle(MAKELONG(ENGLISH_US, ENGLISH_US));
746     }
747 
748     dwKLID = wcstoul(pwszKLID, NULL, 16);
749     bIsIME = IS_IME_HKL(UlongToHandle(dwKLID));
750 
751     wLow = LOWORD(dwKLID);
752     wHigh = HIWORD(dwKLID);
753 
754     if (Flags & KLF_SUBSTITUTE_OK)
755     {
756         /* Check substitutes key */
757         if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Keyboard Layout\\Substitutes", 0,
758                           KEY_READ, &hKey) == ERROR_SUCCESS)
759         {
760             dwSize = sizeof(wszNewKLID);
761             if (RegQueryValueExW(hKey, pwszKLID, NULL, &dwType, (LPBYTE)wszNewKLID,
762                                  &dwSize) == ERROR_SUCCESS &&
763                 dwType == REG_SZ)
764             {
765                 /* Use new KLID value */
766                 pwszKLID = wszNewKLID;
767                 dwKLID = wcstoul(pwszKLID, NULL, 16);
768                 wHigh = LOWORD(dwKLID);
769             }
770 
771             /* Close the key now */
772             RegCloseKey(hKey);
773         }
774     }
775 
776     /* Append KLID at the end of registry key */
777     StringCbCatW(wszRegKey, sizeof(wszRegKey), pwszKLID);
778 
779     /* Open layout registry key for read */
780     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
781     {
782         dwSize = sizeof(wszLayoutId);
783         if (RegQueryValueExW(hKey, L"Layout Id", NULL, &dwType, (LPBYTE)wszLayoutId,
784                              &dwSize) == ERROR_SUCCESS && dwType == REG_SZ)
785         {
786             /* If Layout Id is specified, use this value | f000 as HIWORD */
787             wHigh = (0xF000 | wcstoul(wszLayoutId, NULL, 16));
788         }
789 
790         if (bIsIME)
791         {
792             /* Check "IME File" value */
793             dwSize = sizeof(szImeFileName);
794             if (RegQueryValueExW(hKey, L"IME File", NULL, &dwType, (LPBYTE)szImeFileName,
795                                  &dwSize) != ERROR_SUCCESS)
796             {
797                 bIsIME = FALSE;
798                 wHigh = 0;
799             }
800             else
801             {
802                 WCHAR szPath[MAX_PATH];
803                 szImeFileName[_countof(szImeFileName) - 1] = UNICODE_NULL;
804                 GetSystemLibraryPath(szPath, _countof(szPath), szImeFileName);
805 
806                 /* We don't allow the invalid "IME File" values for security reason */
807                 if (dwType != REG_SZ || szImeFileName[0] == 0 ||
808                     wcscspn(szImeFileName, L":\\/") != wcslen(szImeFileName) ||
809                     GetFileAttributesW(szPath) == INVALID_FILE_ATTRIBUTES) /* Does not exist? */
810                 {
811                     bIsIME = FALSE;
812                     wHigh = 0;
813                 }
814             }
815         }
816 
817         /* Close the key now */
818         RegCloseKey(hKey);
819     }
820     else
821     {
822         ERR("Could not find keyboard layout %S.\n", pwszKLID);
823         return NULL;
824     }
825 
826     if (wHigh == 0)
827         wHigh = wLow;
828 
829     dwHKL = MAKELONG(wLow, wHigh);
830 
831     ZeroMemory(&ustrKbdName, sizeof(ustrKbdName));
832     RtlInitUnicodeString(&ustrKLID, pwszKLID);
833     hNewKL = NtUserLoadKeyboardLayoutEx(NULL, 0, &ustrKbdName, NULL, &ustrKLID, dwHKL, Flags);
834     CliImmInitializeHotKeys(SETIMEHOTKEY_ADD, hNewKL);
835     return hNewKL;
836 }
837 
838 /*
839  * @implemented
840  */
841 HKL WINAPI
842 LoadKeyboardLayoutW(LPCWSTR pwszKLID,
843                     UINT Flags)
844 {
845     TRACE("(%s, 0x%X)\n", debugstr_w(pwszKLID), Flags);
846     return IntLoadKeyboardLayout(NULL, pwszKLID, 0, Flags, FALSE);
847 }
848 
849 /*
850  * @unimplemented
851  */
852 HKL WINAPI
853 LoadKeyboardLayoutEx(HKL hklUnload,
854                      LPCWSTR pwszKLID,
855                      UINT Flags)
856 {
857     FIXME("(%p, %s, 0x%X)", hklUnload, debugstr_w(pwszKLID), Flags);
858     if (!hklUnload)
859         return NULL;
860     return IntLoadKeyboardLayout(hklUnload, pwszKLID, 0, Flags, FALSE);
861 }
862 
863 /*
864  * @implemented
865  */
866 BOOL WINAPI UnloadKeyboardLayout(HKL hKL)
867 {
868     if (!NtUserUnloadKeyboardLayout(hKL))
869         return FALSE;
870 
871     CliImmInitializeHotKeys(SETIMEHOTKEY_DELETE, hKL);
872     return TRUE;
873 }
874 
875 /*
876  * @implemented
877  */
878 UINT WINAPI
879 MapVirtualKeyA(UINT uCode,
880                UINT uMapType)
881 {
882     return MapVirtualKeyExA(uCode, uMapType, GetKeyboardLayout(0));
883 }
884 
885 /*
886  * @implemented
887  */
888 UINT WINAPI
889 MapVirtualKeyExA(UINT uCode,
890                  UINT uMapType,
891                  HKL dwhkl)
892 {
893     return MapVirtualKeyExW(uCode, uMapType, dwhkl);
894 }
895 
896 
897 /*
898  * @implemented
899  */
900 UINT WINAPI
901 MapVirtualKeyExW(UINT uCode,
902                  UINT uMapType,
903                  HKL dwhkl)
904 {
905     return NtUserMapVirtualKeyEx(uCode, uMapType, 0, dwhkl);
906 }
907 
908 
909 /*
910  * @implemented
911  */
912 UINT WINAPI
913 MapVirtualKeyW(UINT uCode,
914                UINT uMapType)
915 {
916     return MapVirtualKeyExW(uCode, uMapType, GetKeyboardLayout(0));
917 }
918 
919 
920 /*
921  * @implemented
922  */
923 DWORD WINAPI
924 OemKeyScan(WORD wOemChar)
925 {
926     WCHAR p;
927     SHORT Vk;
928     UINT Scan;
929 
930     MultiByteToWideChar(CP_OEMCP, 0, (PCSTR)&wOemChar, 1, &p, 1);
931     Vk = VkKeyScanW(p);
932     Scan = MapVirtualKeyW((Vk & 0x00ff), 0);
933     if (!Scan) return -1;
934     /*
935        Page 450-1, MS W2k SuperBible by SAMS. Return, low word has the
936        scan code and high word has the shift state.
937      */
938     return ((Vk & 0xff00) << 8) | Scan;
939 }
940 
941 
942 /*
943  * @implemented
944  */
945 BOOL WINAPI
946 SetDoubleClickTime(UINT uInterval)
947 {
948     return (BOOL)NtUserSystemParametersInfo(SPI_SETDOUBLECLICKTIME,
949                                             uInterval,
950                                             NULL,
951                                             0);
952 }
953 
954 
955 /*
956  * @implemented
957  */
958 BOOL
959 WINAPI
960 SwapMouseButton(
961     BOOL fSwap)
962 {
963     return NtUserxSwapMouseButton(fSwap);
964 }
965 
966 
967 /*
968  * @implemented
969  */
970 int WINAPI
971 ToAscii(UINT uVirtKey,
972         UINT uScanCode,
973         CONST BYTE *lpKeyState,
974         LPWORD lpChar,
975         UINT uFlags)
976 {
977     return ToAsciiEx(uVirtKey, uScanCode, lpKeyState, lpChar, uFlags, 0);
978 }
979 
980 
981 /*
982  * @implemented
983  */
984 int WINAPI
985 ToAsciiEx(UINT uVirtKey,
986           UINT uScanCode,
987           CONST BYTE *lpKeyState,
988           LPWORD lpChar,
989           UINT uFlags,
990           HKL dwhkl)
991 {
992     WCHAR UniChars[2];
993     int Ret, CharCount;
994 
995     Ret = ToUnicodeEx(uVirtKey, uScanCode, lpKeyState, UniChars, 2, uFlags, dwhkl);
996     CharCount = (Ret < 0 ? 1 : Ret);
997     WideCharToMultiByte(CP_ACP, 0, UniChars, CharCount, (LPSTR)lpChar, 2, NULL, NULL);
998 
999     return Ret;
1000 }
1001 
1002 
1003 /*
1004  * @implemented
1005  */
1006 int WINAPI
1007 ToUnicode(UINT wVirtKey,
1008           UINT wScanCode,
1009           CONST BYTE *lpKeyState,
1010           LPWSTR pwszBuff,
1011           int cchBuff,
1012           UINT wFlags)
1013 {
1014     return ToUnicodeEx(wVirtKey, wScanCode, lpKeyState, pwszBuff, cchBuff,
1015                        wFlags, 0);
1016 }
1017 
1018 
1019 /*
1020  * @implemented
1021  */
1022 int WINAPI
1023 ToUnicodeEx(UINT wVirtKey,
1024             UINT wScanCode,
1025             CONST BYTE *lpKeyState,
1026             LPWSTR pwszBuff,
1027             int cchBuff,
1028             UINT wFlags,
1029             HKL dwhkl)
1030 {
1031     return NtUserToUnicodeEx(wVirtKey, wScanCode, (PBYTE)lpKeyState, pwszBuff, cchBuff,
1032                              wFlags, dwhkl);
1033 }
1034 
1035 
1036 
1037 /*
1038  * @implemented
1039  */
1040 SHORT WINAPI
1041 VkKeyScanA(CHAR ch)
1042 {
1043     WCHAR wChar;
1044 
1045     if (IsDBCSLeadByte(ch))
1046         return -1;
1047 
1048     MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wChar, 1);
1049     return VkKeyScanW(wChar);
1050 }
1051 
1052 
1053 /*
1054  * @implemented
1055  */
1056 SHORT WINAPI
1057 VkKeyScanExA(CHAR ch,
1058              HKL dwhkl)
1059 {
1060     WCHAR wChar;
1061 
1062     if (IsDBCSLeadByte(ch))
1063         return -1;
1064 
1065     MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wChar, 1);
1066     return VkKeyScanExW(wChar, dwhkl);
1067 }
1068 
1069 
1070 /*
1071  * @implemented
1072  */
1073 SHORT WINAPI
1074 VkKeyScanExW(WCHAR ch,
1075              HKL dwhkl)
1076 {
1077     return (SHORT)NtUserVkKeyScanEx(ch, dwhkl, TRUE);
1078 }
1079 
1080 
1081 /*
1082  * @implemented
1083  */
1084 SHORT WINAPI
1085 VkKeyScanW(WCHAR ch)
1086 {
1087     return (SHORT)NtUserVkKeyScanEx(ch, 0, FALSE);
1088 }
1089 
1090 
1091 /*
1092  * @implemented
1093  */
1094 VOID
1095 WINAPI
1096 keybd_event(
1097     BYTE bVk,
1098     BYTE bScan,
1099     DWORD dwFlags,
1100     ULONG_PTR dwExtraInfo)
1101 {
1102     INPUT Input;
1103 
1104     Input.type = INPUT_KEYBOARD;
1105     Input.ki.wVk = bVk;
1106     Input.ki.wScan = bScan;
1107     Input.ki.dwFlags = dwFlags;
1108     Input.ki.time = 0;
1109     Input.ki.dwExtraInfo = dwExtraInfo;
1110 
1111     NtUserSendInput(1, &Input, sizeof(INPUT));
1112 }
1113 
1114 
1115 /*
1116  * @implemented
1117  */
1118 VOID
1119 WINAPI
1120 mouse_event(
1121     DWORD dwFlags,
1122     DWORD dx,
1123     DWORD dy,
1124     DWORD dwData,
1125     ULONG_PTR dwExtraInfo)
1126 {
1127     INPUT Input;
1128 
1129     Input.type = INPUT_MOUSE;
1130     Input.mi.dx = dx;
1131     Input.mi.dy = dy;
1132     Input.mi.mouseData = dwData;
1133     Input.mi.dwFlags = dwFlags;
1134     Input.mi.time = 0;
1135     Input.mi.dwExtraInfo = dwExtraInfo;
1136 
1137     NtUserSendInput(1, &Input, sizeof(INPUT));
1138 }
1139 
1140 /* EOF */
1141