xref: /reactos/win32ss/user/user32/windows/input.c (revision b3194e32)
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_READ,
349                           &hKey);
350     if (error != ERROR_SUCCESS)
351         return ret;
352 
353     for (dwIndex = 0; dwIndex < 1000; ++dwIndex)
354     {
355         cchKeyName = _countof(szKeyName);
356         error = RegEnumKeyExW(hKey, dwIndex, szKeyName, &cchKeyName, NULL, NULL, NULL, NULL);
357         if (error != ERROR_SUCCESS)
358             break;
359 
360         szKeyName[_countof(szKeyName) - 1] = 0; /* Avoid stack overrun */
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[33], 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         _ultow(iNumber, szValueName, 10);
386 
387         cbValue = sizeof(szValue);
388         error = RegQueryValueExW(hKey, szValueName, NULL, &dwType, (LPBYTE)szValue, &cbValue);
389         if (error != ERROR_SUCCESS)
390             break;
391 
392         if (dwType != REG_SZ)
393             continue;
394 
395         szValue[_countof(szValue) - 1] = 0; /* Avoid stack overrun */
396 
397         RtlInitUnicodeString(&ustrValue, szValue);
398         RtlUnicodeStringToInteger(&ustrValue, 16, &dwKL);
399 
400         IntSetFeKeyboardFlags(LOWORD(dwKL), pbFlags);
401     }
402 
403     RegCloseKey(hKey);
404 }
405 
406 VOID APIENTRY CliSetDefaultImeHotKeys(PIMEHOTKEYENTRY pEntries, UINT nCount, BOOL bCheck)
407 {
408     UINT uVirtualKey, uModifiers;
409     HKL hKL;
410 
411     while (nCount-- > 0)
412     {
413         if (!bCheck || !NtUserGetImeHotKey(pEntries->dwHotKeyId, &uModifiers, &uVirtualKey, &hKL))
414         {
415             CliImmSetHotKeyWorker(pEntries->dwHotKeyId,
416                                   pEntries->uModifiers,
417                                   pEntries->uVirtualKey,
418                                   pEntries->hKL,
419                                   SETIMEHOTKEY_ADD);
420         }
421         ++pEntries;
422     }
423 }
424 
425 VOID APIENTRY CliImmInitializeHotKeys(DWORD dwAction, HKL hKL)
426 {
427     UINT nCount;
428     LPHKL pList;
429     UINT iIndex;
430     LANGID LangID;
431     BYTE bFlags = 0;
432     BOOL bCheck;
433 
434     NtUserSetImeHotKey(0, 0, 0, NULL, SETIMEHOTKEY_INITIALIZE);
435 
436     bCheck = CliGetImeHotKeysFromRegistry();
437 
438     if (dwAction == SETIMEHOTKEY_INITIALIZE)
439     {
440         LangID = LANGIDFROMLCID(GetUserDefaultLCID());
441         IntSetFeKeyboardFlags(LangID, &bFlags);
442 
443         CliGetPreloadKeyboardLayouts(&bFlags);
444     }
445     else
446     {
447         nCount = NtUserGetKeyboardLayoutList(0, NULL);
448         if (!nCount)
449             return;
450 
451         pList = RtlAllocateHeap(RtlGetProcessHeap(), 0, nCount * sizeof(HKL));
452         if (!pList)
453             return;
454 
455         NtUserGetKeyboardLayoutList(nCount, pList);
456 
457         for (iIndex = 0; iIndex < nCount; ++iIndex)
458         {
459             LangID = LOWORD(pList[iIndex]);
460             IntSetFeKeyboardFlags(LangID, &bFlags);
461         }
462 
463         RtlFreeHeap(RtlGetProcessHeap(), 0, pList);
464     }
465 
466     if (bFlags & FE_JAPANESE)
467         CliSetDefaultImeHotKeys(DefaultHotKeyTableJ, _countof(DefaultHotKeyTableJ), bCheck);
468 
469     if (bFlags & FE_CHINESE_TRADITIONAL)
470         CliSetDefaultImeHotKeys(DefaultHotKeyTableT, _countof(DefaultHotKeyTableT), bCheck);
471 
472     if (bFlags & FE_CHINESE_SIMPLIFIED)
473         CliSetDefaultImeHotKeys(DefaultHotKeyTableC, _countof(DefaultHotKeyTableC), bCheck);
474 }
475 
476 /*
477  * @implemented
478  */
479 BOOL
480 WINAPI
481 DragDetect(
482     HWND hWnd,
483     POINT pt)
484 {
485     return NtUserDragDetect(hWnd, pt);
486 #if 0
487     MSG msg;
488     RECT rect;
489     POINT tmp;
490     ULONG dx = GetSystemMetrics(SM_CXDRAG);
491     ULONG dy = GetSystemMetrics(SM_CYDRAG);
492 
493     rect.left = pt.x - dx;
494     rect.right = pt.x + dx;
495     rect.top = pt.y - dy;
496     rect.bottom = pt.y + dy;
497 
498     SetCapture(hWnd);
499 
500     for (;;)
501     {
502         while (
503             PeekMessageW(&msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) ||
504             PeekMessageW(&msg, 0, WM_KEYFIRST,   WM_KEYLAST,   PM_REMOVE)
505         )
506         {
507             if (msg.message == WM_LBUTTONUP)
508             {
509                 ReleaseCapture();
510                 return FALSE;
511             }
512             if (msg.message == WM_MOUSEMOVE)
513             {
514                 tmp.x = LOWORD(msg.lParam);
515                 tmp.y = HIWORD(msg.lParam);
516                 if (!PtInRect(&rect, tmp))
517                 {
518                     ReleaseCapture();
519                     return TRUE;
520                 }
521             }
522             if (msg.message == WM_KEYDOWN)
523             {
524                 if (msg.wParam == VK_ESCAPE)
525                 {
526                     ReleaseCapture();
527                     return TRUE;
528                 }
529             }
530         }
531         WaitMessage();
532     }
533     return 0;
534 #endif
535 }
536 
537 /*
538  * @implemented
539  */
540 BOOL WINAPI
541 EnableWindow(HWND hWnd, BOOL bEnable)
542 {
543     return NtUserxEnableWindow(hWnd, bEnable);
544 }
545 
546 /*
547  * @implemented
548  */
549 SHORT
550 WINAPI
551 DECLSPEC_HOTPATCH
552 GetAsyncKeyState(int vKey)
553 {
554     if (vKey < 0 || vKey > 256)
555         return 0;
556     return (SHORT)NtUserGetAsyncKeyState((DWORD)vKey);
557 }
558 
559 
560 /*
561  * @implemented
562  */
563 HKL WINAPI
564 GetKeyboardLayout(DWORD idThread)
565 {
566     return NtUserxGetKeyboardLayout(idThread);
567 }
568 
569 
570 /*
571  * @implemented
572  */
573 UINT WINAPI
574 GetKBCodePage(VOID)
575 {
576     return GetOEMCP();
577 }
578 
579 
580 /*
581  * @implemented
582  */
583 int WINAPI
584 GetKeyNameTextA(LONG lParam,
585                 LPSTR lpString,
586                 int nSize)
587 {
588     LPWSTR pwszBuf;
589     UINT cchBuf = 0;
590     int iRet = 0;
591     BOOL defChar = FALSE;
592 
593     pwszBuf = HeapAlloc(GetProcessHeap(), 0, nSize * sizeof(WCHAR));
594     if (!pwszBuf)
595         return 0;
596 
597     cchBuf = NtUserGetKeyNameText(lParam, pwszBuf, nSize);
598 
599     iRet = WideCharToMultiByte(CP_ACP, 0,
600                               pwszBuf, cchBuf,
601                               lpString, nSize, ".", &defChar); // FIXME: do we need defChar?
602     lpString[iRet] = 0;
603     HeapFree(GetProcessHeap(), 0, pwszBuf);
604 
605     return iRet;
606 }
607 
608 /*
609  * @implemented
610  */
611 int WINAPI
612 GetKeyNameTextW(LONG lParam,
613                 LPWSTR lpString,
614                 int nSize)
615 {
616     return NtUserGetKeyNameText(lParam, lpString, nSize);
617 }
618 
619 /*
620  * @implemented
621  */
622 SHORT
623 WINAPI
624 DECLSPEC_HOTPATCH
625 GetKeyState(int nVirtKey)
626 {
627     return (SHORT)NtUserGetKeyState((DWORD)nVirtKey);
628 }
629 
630 /*
631  * @implemented
632  */
633 BOOL WINAPI
634 GetKeyboardLayoutNameA(LPSTR pwszKLID)
635 {
636     WCHAR buf[KL_NAMELENGTH];
637 
638     if (!GetKeyboardLayoutNameW(buf))
639         return FALSE;
640 
641     if (!WideCharToMultiByte(CP_ACP, 0, buf, -1, pwszKLID, KL_NAMELENGTH, NULL, NULL))
642         return FALSE;
643 
644     return TRUE;
645 }
646 
647 /*
648  * @implemented
649  */
650 BOOL WINAPI
651 GetKeyboardLayoutNameW(LPWSTR pwszKLID)
652 {
653     UNICODE_STRING Name;
654 
655     RtlInitEmptyUnicodeString(&Name,
656                               pwszKLID,
657                               KL_NAMELENGTH * sizeof(WCHAR));
658 
659     return NtUserGetKeyboardLayoutName(&Name);
660 }
661 
662 /*
663  * @implemented
664  */
665 int WINAPI
666 GetKeyboardType(int nTypeFlag)
667 {
668     return NtUserxGetKeyboardType(nTypeFlag);
669 }
670 
671 /*
672  * @implemented
673  */
674 BOOL WINAPI
675 GetLastInputInfo(PLASTINPUTINFO plii)
676 {
677     TRACE("%p\n", plii);
678 
679     if (plii->cbSize != sizeof (*plii))
680     {
681         SetLastError(ERROR_INVALID_PARAMETER);
682         return FALSE;
683     }
684 
685     plii->dwTime = gpsi->dwLastRITEventTickCount;
686     return TRUE;
687 }
688 
689 /*
690  * @implemented
691  */
692 HKL WINAPI
693 LoadKeyboardLayoutA(LPCSTR pszKLID,
694                     UINT Flags)
695 {
696     WCHAR wszKLID[16];
697 
698     if (!MultiByteToWideChar(CP_ACP, 0, pszKLID, -1,
699                              wszKLID, sizeof(wszKLID)/sizeof(wszKLID[0])))
700     {
701         return FALSE;
702     }
703 
704     return LoadKeyboardLayoutW(wszKLID, Flags);
705 }
706 
707 static inline BOOL IsValidKLID(_In_ LPCWSTR pwszKLID)
708 {
709     return (pwszKLID != NULL) && (wcsspn(pwszKLID, L"0123456789ABCDEFabcdef") == (KL_NAMELENGTH - 1));
710 }
711 
712 VOID GetSystemLibraryPath(LPWSTR pszPath, INT cchPath, LPCWSTR pszFileName)
713 {
714     WCHAR szSysDir[MAX_PATH];
715     GetSystemDirectoryW(szSysDir, _countof(szSysDir));
716     StringCchPrintfW(pszPath, cchPath, L"%s\\%s", szSysDir, pszFileName);
717 }
718 
719 #define ENGLISH_US MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)
720 
721 /*
722  * @unimplemented
723  *
724  * NOTE: We adopt a different design from Microsoft's one due to security reason.
725  *       See NtUserLoadKeyboardLayoutEx.
726  */
727 HKL APIENTRY
728 IntLoadKeyboardLayout(
729     _In_    HKL     hklUnload,
730     _In_z_  LPCWSTR pwszKLID,
731     _In_    LANGID  wLangID,
732     _In_    UINT    Flags,
733     _In_    BOOL    unknown5)
734 {
735     DWORD dwKLID, dwHKL, dwType, dwSize;
736     UNICODE_STRING ustrKLID;
737     WCHAR wszRegKey[256] = L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\";
738     WCHAR wszLayoutId[10], wszNewKLID[KL_NAMELENGTH], szImeFileName[80];
739     HKL hNewKL;
740     HKEY hKey;
741     BOOL bIsIME;
742     WORD wLow, wHigh;
743 
744     if (!IsValidKLID(pwszKLID))
745     {
746         ERR("pwszKLID: %s\n", debugstr_w(pwszKLID));
747         return UlongToHandle(MAKELONG(ENGLISH_US, ENGLISH_US));
748     }
749 
750     dwKLID = wcstoul(pwszKLID, NULL, 16);
751     bIsIME = IS_IME_HKL(UlongToHandle(dwKLID));
752 
753     wLow = LOWORD(dwKLID);
754     wHigh = HIWORD(dwKLID);
755 
756     if (Flags & KLF_SUBSTITUTE_OK)
757     {
758         /* Check substitutes key */
759         if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Keyboard Layout\\Substitutes", 0,
760                           KEY_READ, &hKey) == ERROR_SUCCESS)
761         {
762             dwSize = sizeof(wszNewKLID);
763             if (RegQueryValueExW(hKey, pwszKLID, NULL, &dwType, (LPBYTE)wszNewKLID,
764                                  &dwSize) == ERROR_SUCCESS &&
765                 dwType == REG_SZ)
766             {
767                 /* Use new KLID value */
768                 pwszKLID = wszNewKLID;
769                 dwKLID = wcstoul(pwszKLID, NULL, 16);
770                 wHigh = LOWORD(dwKLID);
771             }
772 
773             /* Close the key now */
774             RegCloseKey(hKey);
775         }
776     }
777 
778     /* Append KLID at the end of registry key */
779     StringCbCatW(wszRegKey, sizeof(wszRegKey), pwszKLID);
780 
781     /* Open layout registry key for read */
782     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
783     {
784         dwSize = sizeof(wszLayoutId);
785         if (RegQueryValueExW(hKey, L"Layout Id", NULL, &dwType, (LPBYTE)wszLayoutId,
786                              &dwSize) == ERROR_SUCCESS && dwType == REG_SZ)
787         {
788             /* If Layout Id is specified, use this value | f000 as HIWORD */
789             wHigh = (0xF000 | wcstoul(wszLayoutId, NULL, 16));
790         }
791 
792         if (bIsIME)
793         {
794             /* Check "IME File" value */
795             dwSize = sizeof(szImeFileName);
796             if (RegQueryValueExW(hKey, L"IME File", NULL, &dwType, (LPBYTE)szImeFileName,
797                                  &dwSize) != ERROR_SUCCESS)
798             {
799                 bIsIME = FALSE;
800                 wHigh = 0;
801                 ERR("0x%X\n", dwKLID);
802             }
803             else
804             {
805                 WCHAR szPath[MAX_PATH];
806                 szImeFileName[_countof(szImeFileName) - 1] = UNICODE_NULL;
807                 GetSystemLibraryPath(szPath, _countof(szPath), szImeFileName);
808 
809                 /* We don't allow the invalid "IME File" values due to security reason */
810                 if (dwType != REG_SZ || szImeFileName[0] == 0 ||
811                     wcscspn(szImeFileName, L":\\/") != wcslen(szImeFileName) ||
812                     GetFileAttributesW(szPath) == INVALID_FILE_ATTRIBUTES) /* Does not exist? */
813                 {
814                     bIsIME = FALSE;
815                     wHigh = 0;
816                     ERR("'%s'\n", debugstr_w(szPath));
817                 }
818             }
819         }
820 
821         /* Close the key now */
822         RegCloseKey(hKey);
823     }
824     else
825     {
826         ERR("Could not find keyboard layout %S.\n", pwszKLID);
827         return NULL;
828     }
829 
830     if (wHigh == 0)
831         wHigh = wLow;
832 
833     dwHKL = MAKELONG(wLow, wHigh);
834 
835     RtlInitUnicodeString(&ustrKLID, pwszKLID);
836     hNewKL = NtUserLoadKeyboardLayoutEx(NULL, 0, NULL, hklUnload, &ustrKLID, dwHKL, Flags);
837     CliImmInitializeHotKeys(SETIMEHOTKEY_ADD, hNewKL);
838     return hNewKL;
839 }
840 
841 /*
842  * @implemented
843  */
844 HKL WINAPI
845 LoadKeyboardLayoutW(LPCWSTR pwszKLID,
846                     UINT Flags)
847 {
848     TRACE("(%s, 0x%X)\n", debugstr_w(pwszKLID), Flags);
849     return IntLoadKeyboardLayout(NULL, pwszKLID, 0, Flags, FALSE);
850 }
851 
852 /*
853  * @unimplemented
854  */
855 HKL WINAPI
856 LoadKeyboardLayoutEx(HKL hklUnload,
857                      LPCWSTR pwszKLID,
858                      UINT Flags)
859 {
860     FIXME("(%p, %s, 0x%X)", hklUnload, debugstr_w(pwszKLID), Flags);
861     if (!hklUnload)
862         return NULL;
863     return IntLoadKeyboardLayout(hklUnload, pwszKLID, 0, Flags, FALSE);
864 }
865 
866 /*
867  * @implemented
868  */
869 BOOL WINAPI UnloadKeyboardLayout(HKL hKL)
870 {
871     if (!NtUserUnloadKeyboardLayout(hKL))
872         return FALSE;
873 
874     CliImmInitializeHotKeys(SETIMEHOTKEY_DELETE, hKL);
875     return TRUE;
876 }
877 
878 /*
879  * @implemented
880  */
881 UINT WINAPI
882 MapVirtualKeyA(UINT uCode,
883                UINT uMapType)
884 {
885     return MapVirtualKeyExA(uCode, uMapType, GetKeyboardLayout(0));
886 }
887 
888 /*
889  * @implemented
890  */
891 UINT WINAPI
892 MapVirtualKeyExA(UINT uCode,
893                  UINT uMapType,
894                  HKL dwhkl)
895 {
896     return MapVirtualKeyExW(uCode, uMapType, dwhkl);
897 }
898 
899 
900 /*
901  * @implemented
902  */
903 UINT WINAPI
904 MapVirtualKeyExW(UINT uCode,
905                  UINT uMapType,
906                  HKL dwhkl)
907 {
908     return NtUserMapVirtualKeyEx(uCode, uMapType, 0, dwhkl);
909 }
910 
911 
912 /*
913  * @implemented
914  */
915 UINT WINAPI
916 MapVirtualKeyW(UINT uCode,
917                UINT uMapType)
918 {
919     return MapVirtualKeyExW(uCode, uMapType, GetKeyboardLayout(0));
920 }
921 
922 
923 /*
924  * @implemented
925  */
926 DWORD WINAPI
927 OemKeyScan(WORD wOemChar)
928 {
929     WCHAR p;
930     SHORT Vk;
931     UINT Scan;
932 
933     MultiByteToWideChar(CP_OEMCP, 0, (PCSTR)&wOemChar, 1, &p, 1);
934     Vk = VkKeyScanW(p);
935     Scan = MapVirtualKeyW((Vk & 0x00ff), 0);
936     if (!Scan) return -1;
937     /*
938        Page 450-1, MS W2k SuperBible by SAMS. Return, low word has the
939        scan code and high word has the shift state.
940      */
941     return ((Vk & 0xff00) << 8) | Scan;
942 }
943 
944 
945 /*
946  * @implemented
947  */
948 BOOL WINAPI
949 SetDoubleClickTime(UINT uInterval)
950 {
951     return (BOOL)NtUserSystemParametersInfo(SPI_SETDOUBLECLICKTIME,
952                                             uInterval,
953                                             NULL,
954                                             0);
955 }
956 
957 
958 /*
959  * @implemented
960  */
961 BOOL
962 WINAPI
963 SwapMouseButton(
964     BOOL fSwap)
965 {
966     return NtUserxSwapMouseButton(fSwap);
967 }
968 
969 
970 /*
971  * @implemented
972  */
973 int WINAPI
974 ToAscii(UINT uVirtKey,
975         UINT uScanCode,
976         CONST BYTE *lpKeyState,
977         LPWORD lpChar,
978         UINT uFlags)
979 {
980     return ToAsciiEx(uVirtKey, uScanCode, lpKeyState, lpChar, uFlags, 0);
981 }
982 
983 
984 /*
985  * @implemented
986  */
987 int WINAPI
988 ToAsciiEx(UINT uVirtKey,
989           UINT uScanCode,
990           CONST BYTE *lpKeyState,
991           LPWORD lpChar,
992           UINT uFlags,
993           HKL dwhkl)
994 {
995     WCHAR UniChars[2];
996     int Ret, CharCount;
997 
998     Ret = ToUnicodeEx(uVirtKey, uScanCode, lpKeyState, UniChars, 2, uFlags, dwhkl);
999     CharCount = (Ret < 0 ? 1 : Ret);
1000     WideCharToMultiByte(CP_ACP, 0, UniChars, CharCount, (LPSTR)lpChar, 2, NULL, NULL);
1001 
1002     return Ret;
1003 }
1004 
1005 
1006 /*
1007  * @implemented
1008  */
1009 int WINAPI
1010 ToUnicode(UINT wVirtKey,
1011           UINT wScanCode,
1012           CONST BYTE *lpKeyState,
1013           LPWSTR pwszBuff,
1014           int cchBuff,
1015           UINT wFlags)
1016 {
1017     return ToUnicodeEx(wVirtKey, wScanCode, lpKeyState, pwszBuff, cchBuff,
1018                        wFlags, 0);
1019 }
1020 
1021 
1022 /*
1023  * @implemented
1024  */
1025 int WINAPI
1026 ToUnicodeEx(UINT wVirtKey,
1027             UINT wScanCode,
1028             CONST BYTE *lpKeyState,
1029             LPWSTR pwszBuff,
1030             int cchBuff,
1031             UINT wFlags,
1032             HKL dwhkl)
1033 {
1034     return NtUserToUnicodeEx(wVirtKey, wScanCode, (PBYTE)lpKeyState, pwszBuff, cchBuff,
1035                              wFlags, dwhkl);
1036 }
1037 
1038 
1039 
1040 /*
1041  * @implemented
1042  */
1043 SHORT WINAPI
1044 VkKeyScanA(CHAR ch)
1045 {
1046     WCHAR wChar;
1047 
1048     if (IsDBCSLeadByte(ch))
1049         return -1;
1050 
1051     MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wChar, 1);
1052     return VkKeyScanW(wChar);
1053 }
1054 
1055 
1056 /*
1057  * @implemented
1058  */
1059 SHORT WINAPI
1060 VkKeyScanExA(CHAR ch,
1061              HKL dwhkl)
1062 {
1063     WCHAR wChar;
1064 
1065     if (IsDBCSLeadByte(ch))
1066         return -1;
1067 
1068     MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wChar, 1);
1069     return VkKeyScanExW(wChar, dwhkl);
1070 }
1071 
1072 
1073 /*
1074  * @implemented
1075  */
1076 SHORT WINAPI
1077 VkKeyScanExW(WCHAR ch,
1078              HKL dwhkl)
1079 {
1080     return (SHORT)NtUserVkKeyScanEx(ch, dwhkl, TRUE);
1081 }
1082 
1083 
1084 /*
1085  * @implemented
1086  */
1087 SHORT WINAPI
1088 VkKeyScanW(WCHAR ch)
1089 {
1090     return (SHORT)NtUserVkKeyScanEx(ch, 0, FALSE);
1091 }
1092 
1093 
1094 /*
1095  * @implemented
1096  */
1097 VOID
1098 WINAPI
1099 keybd_event(
1100     BYTE bVk,
1101     BYTE bScan,
1102     DWORD dwFlags,
1103     ULONG_PTR dwExtraInfo)
1104 {
1105     INPUT Input;
1106 
1107     Input.type = INPUT_KEYBOARD;
1108     Input.ki.wVk = bVk;
1109     Input.ki.wScan = bScan;
1110     Input.ki.dwFlags = dwFlags;
1111     Input.ki.time = 0;
1112     Input.ki.dwExtraInfo = dwExtraInfo;
1113 
1114     NtUserSendInput(1, &Input, sizeof(INPUT));
1115 }
1116 
1117 
1118 /*
1119  * @implemented
1120  */
1121 VOID
1122 WINAPI
1123 mouse_event(
1124     DWORD dwFlags,
1125     DWORD dx,
1126     DWORD dy,
1127     DWORD dwData,
1128     ULONG_PTR dwExtraInfo)
1129 {
1130     INPUT Input;
1131 
1132     Input.type = INPUT_MOUSE;
1133     Input.mi.dx = dx;
1134     Input.mi.dy = dy;
1135     Input.mi.mouseData = dwData;
1136     Input.mi.dwFlags = dwFlags;
1137     Input.mi.time = 0;
1138     Input.mi.dwExtraInfo = dwExtraInfo;
1139 
1140     NtUserSendInput(1, &Input, sizeof(INPUT));
1141 }
1142 
1143 /* EOF */
1144