xref: /reactos/win32ss/user/user32/windows/input.c (revision 1734f297)
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 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 for security reason.
725  */
726 /* Win: LoadKeyboardLayoutWorker */
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 ustrKbdName;
737     UNICODE_STRING ustrKLID;
738     WCHAR wszRegKey[256] = L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\";
739     WCHAR wszLayoutId[10], wszNewKLID[KL_NAMELENGTH], szImeFileName[80];
740     HKL hNewKL;
741     HKEY hKey;
742     BOOL bIsIME;
743     WORD wLow, wHigh;
744 
745     if (!IsValidKLID(pwszKLID))
746     {
747         ERR("pwszKLID: %s\n", debugstr_w(pwszKLID));
748         return UlongToHandle(MAKELONG(ENGLISH_US, ENGLISH_US));
749     }
750 
751     dwKLID = wcstoul(pwszKLID, NULL, 16);
752     bIsIME = IS_IME_HKL(UlongToHandle(dwKLID));
753 
754     wLow = LOWORD(dwKLID);
755     wHigh = HIWORD(dwKLID);
756 
757     if (Flags & KLF_SUBSTITUTE_OK)
758     {
759         /* Check substitutes key */
760         if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Keyboard Layout\\Substitutes", 0,
761                           KEY_READ, &hKey) == ERROR_SUCCESS)
762         {
763             dwSize = sizeof(wszNewKLID);
764             if (RegQueryValueExW(hKey, pwszKLID, NULL, &dwType, (LPBYTE)wszNewKLID,
765                                  &dwSize) == ERROR_SUCCESS &&
766                 dwType == REG_SZ)
767             {
768                 /* Use new KLID value */
769                 pwszKLID = wszNewKLID;
770                 dwKLID = wcstoul(pwszKLID, NULL, 16);
771                 wHigh = LOWORD(dwKLID);
772             }
773 
774             /* Close the key now */
775             RegCloseKey(hKey);
776         }
777     }
778 
779     /* Append KLID at the end of registry key */
780     StringCbCatW(wszRegKey, sizeof(wszRegKey), pwszKLID);
781 
782     /* Open layout registry key for read */
783     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
784     {
785         dwSize = sizeof(wszLayoutId);
786         if (RegQueryValueExW(hKey, L"Layout Id", NULL, &dwType, (LPBYTE)wszLayoutId,
787                              &dwSize) == ERROR_SUCCESS && dwType == REG_SZ)
788         {
789             /* If Layout Id is specified, use this value | f000 as HIWORD */
790             wHigh = (0xF000 | wcstoul(wszLayoutId, NULL, 16));
791         }
792 
793         if (bIsIME)
794         {
795             /* Check "IME File" value */
796             dwSize = sizeof(szImeFileName);
797             if (RegQueryValueExW(hKey, L"IME File", NULL, &dwType, (LPBYTE)szImeFileName,
798                                  &dwSize) != ERROR_SUCCESS)
799             {
800                 bIsIME = FALSE;
801                 wHigh = 0;
802                 ERR("0x%X\n", dwKLID);
803             }
804             else
805             {
806                 WCHAR szPath[MAX_PATH];
807                 szImeFileName[_countof(szImeFileName) - 1] = UNICODE_NULL;
808                 GetSystemLibraryPath(szPath, _countof(szPath), szImeFileName);
809 
810                 /* We don't allow the invalid "IME File" values for security reason */
811                 if (dwType != REG_SZ || szImeFileName[0] == 0 ||
812                     wcscspn(szImeFileName, L":\\/") != wcslen(szImeFileName) ||
813                     GetFileAttributesW(szPath) == INVALID_FILE_ATTRIBUTES) /* Does not exist? */
814                 {
815                     bIsIME = FALSE;
816                     wHigh = 0;
817                     ERR("'%s'\n", debugstr_w(szPath));
818                 }
819             }
820         }
821 
822         /* Close the key now */
823         RegCloseKey(hKey);
824     }
825     else
826     {
827         ERR("Could not find keyboard layout %S.\n", pwszKLID);
828         return NULL;
829     }
830 
831     if (wHigh == 0)
832         wHigh = wLow;
833 
834     dwHKL = MAKELONG(wLow, wHigh);
835 
836     ZeroMemory(&ustrKbdName, sizeof(ustrKbdName));
837     RtlInitUnicodeString(&ustrKLID, pwszKLID);
838     hNewKL = NtUserLoadKeyboardLayoutEx(NULL, 0, &ustrKbdName, NULL, &ustrKLID, dwHKL, Flags);
839     CliImmInitializeHotKeys(SETIMEHOTKEY_ADD, hNewKL);
840     return hNewKL;
841 }
842 
843 /*
844  * @implemented
845  */
846 HKL WINAPI
847 LoadKeyboardLayoutW(LPCWSTR pwszKLID,
848                     UINT Flags)
849 {
850     TRACE("(%s, 0x%X)\n", debugstr_w(pwszKLID), Flags);
851     return IntLoadKeyboardLayout(NULL, pwszKLID, 0, Flags, FALSE);
852 }
853 
854 /*
855  * @unimplemented
856  */
857 HKL WINAPI
858 LoadKeyboardLayoutEx(HKL hklUnload,
859                      LPCWSTR pwszKLID,
860                      UINT Flags)
861 {
862     FIXME("(%p, %s, 0x%X)", hklUnload, debugstr_w(pwszKLID), Flags);
863     if (!hklUnload)
864         return NULL;
865     return IntLoadKeyboardLayout(hklUnload, pwszKLID, 0, Flags, FALSE);
866 }
867 
868 /*
869  * @implemented
870  */
871 BOOL WINAPI UnloadKeyboardLayout(HKL hKL)
872 {
873     if (!NtUserUnloadKeyboardLayout(hKL))
874         return FALSE;
875 
876     CliImmInitializeHotKeys(SETIMEHOTKEY_DELETE, hKL);
877     return TRUE;
878 }
879 
880 /*
881  * @implemented
882  */
883 UINT WINAPI
884 MapVirtualKeyA(UINT uCode,
885                UINT uMapType)
886 {
887     return MapVirtualKeyExA(uCode, uMapType, GetKeyboardLayout(0));
888 }
889 
890 /*
891  * @implemented
892  */
893 UINT WINAPI
894 MapVirtualKeyExA(UINT uCode,
895                  UINT uMapType,
896                  HKL dwhkl)
897 {
898     return MapVirtualKeyExW(uCode, uMapType, dwhkl);
899 }
900 
901 
902 /*
903  * @implemented
904  */
905 UINT WINAPI
906 MapVirtualKeyExW(UINT uCode,
907                  UINT uMapType,
908                  HKL dwhkl)
909 {
910     return NtUserMapVirtualKeyEx(uCode, uMapType, 0, dwhkl);
911 }
912 
913 
914 /*
915  * @implemented
916  */
917 UINT WINAPI
918 MapVirtualKeyW(UINT uCode,
919                UINT uMapType)
920 {
921     return MapVirtualKeyExW(uCode, uMapType, GetKeyboardLayout(0));
922 }
923 
924 
925 /*
926  * @implemented
927  */
928 DWORD WINAPI
929 OemKeyScan(WORD wOemChar)
930 {
931     WCHAR p;
932     SHORT Vk;
933     UINT Scan;
934 
935     MultiByteToWideChar(CP_OEMCP, 0, (PCSTR)&wOemChar, 1, &p, 1);
936     Vk = VkKeyScanW(p);
937     Scan = MapVirtualKeyW((Vk & 0x00ff), 0);
938     if (!Scan) return -1;
939     /*
940        Page 450-1, MS W2k SuperBible by SAMS. Return, low word has the
941        scan code and high word has the shift state.
942      */
943     return ((Vk & 0xff00) << 8) | Scan;
944 }
945 
946 
947 /*
948  * @implemented
949  */
950 BOOL WINAPI
951 SetDoubleClickTime(UINT uInterval)
952 {
953     return (BOOL)NtUserSystemParametersInfo(SPI_SETDOUBLECLICKTIME,
954                                             uInterval,
955                                             NULL,
956                                             0);
957 }
958 
959 
960 /*
961  * @implemented
962  */
963 BOOL
964 WINAPI
965 SwapMouseButton(
966     BOOL fSwap)
967 {
968     return NtUserxSwapMouseButton(fSwap);
969 }
970 
971 
972 /*
973  * @implemented
974  */
975 int WINAPI
976 ToAscii(UINT uVirtKey,
977         UINT uScanCode,
978         CONST BYTE *lpKeyState,
979         LPWORD lpChar,
980         UINT uFlags)
981 {
982     return ToAsciiEx(uVirtKey, uScanCode, lpKeyState, lpChar, uFlags, 0);
983 }
984 
985 
986 /*
987  * @implemented
988  */
989 int WINAPI
990 ToAsciiEx(UINT uVirtKey,
991           UINT uScanCode,
992           CONST BYTE *lpKeyState,
993           LPWORD lpChar,
994           UINT uFlags,
995           HKL dwhkl)
996 {
997     WCHAR UniChars[2];
998     int Ret, CharCount;
999 
1000     Ret = ToUnicodeEx(uVirtKey, uScanCode, lpKeyState, UniChars, 2, uFlags, dwhkl);
1001     CharCount = (Ret < 0 ? 1 : Ret);
1002     WideCharToMultiByte(CP_ACP, 0, UniChars, CharCount, (LPSTR)lpChar, 2, NULL, NULL);
1003 
1004     return Ret;
1005 }
1006 
1007 
1008 /*
1009  * @implemented
1010  */
1011 int WINAPI
1012 ToUnicode(UINT wVirtKey,
1013           UINT wScanCode,
1014           CONST BYTE *lpKeyState,
1015           LPWSTR pwszBuff,
1016           int cchBuff,
1017           UINT wFlags)
1018 {
1019     return ToUnicodeEx(wVirtKey, wScanCode, lpKeyState, pwszBuff, cchBuff,
1020                        wFlags, 0);
1021 }
1022 
1023 
1024 /*
1025  * @implemented
1026  */
1027 int WINAPI
1028 ToUnicodeEx(UINT wVirtKey,
1029             UINT wScanCode,
1030             CONST BYTE *lpKeyState,
1031             LPWSTR pwszBuff,
1032             int cchBuff,
1033             UINT wFlags,
1034             HKL dwhkl)
1035 {
1036     return NtUserToUnicodeEx(wVirtKey, wScanCode, (PBYTE)lpKeyState, pwszBuff, cchBuff,
1037                              wFlags, dwhkl);
1038 }
1039 
1040 
1041 
1042 /*
1043  * @implemented
1044  */
1045 SHORT WINAPI
1046 VkKeyScanA(CHAR ch)
1047 {
1048     WCHAR wChar;
1049 
1050     if (IsDBCSLeadByte(ch))
1051         return -1;
1052 
1053     MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wChar, 1);
1054     return VkKeyScanW(wChar);
1055 }
1056 
1057 
1058 /*
1059  * @implemented
1060  */
1061 SHORT WINAPI
1062 VkKeyScanExA(CHAR ch,
1063              HKL dwhkl)
1064 {
1065     WCHAR wChar;
1066 
1067     if (IsDBCSLeadByte(ch))
1068         return -1;
1069 
1070     MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wChar, 1);
1071     return VkKeyScanExW(wChar, dwhkl);
1072 }
1073 
1074 
1075 /*
1076  * @implemented
1077  */
1078 SHORT WINAPI
1079 VkKeyScanExW(WCHAR ch,
1080              HKL dwhkl)
1081 {
1082     return (SHORT)NtUserVkKeyScanEx(ch, dwhkl, TRUE);
1083 }
1084 
1085 
1086 /*
1087  * @implemented
1088  */
1089 SHORT WINAPI
1090 VkKeyScanW(WCHAR ch)
1091 {
1092     return (SHORT)NtUserVkKeyScanEx(ch, 0, FALSE);
1093 }
1094 
1095 
1096 /*
1097  * @implemented
1098  */
1099 VOID
1100 WINAPI
1101 keybd_event(
1102     BYTE bVk,
1103     BYTE bScan,
1104     DWORD dwFlags,
1105     ULONG_PTR dwExtraInfo)
1106 {
1107     INPUT Input;
1108 
1109     Input.type = INPUT_KEYBOARD;
1110     Input.ki.wVk = bVk;
1111     Input.ki.wScan = bScan;
1112     Input.ki.dwFlags = dwFlags;
1113     Input.ki.time = 0;
1114     Input.ki.dwExtraInfo = dwExtraInfo;
1115 
1116     NtUserSendInput(1, &Input, sizeof(INPUT));
1117 }
1118 
1119 
1120 /*
1121  * @implemented
1122  */
1123 VOID
1124 WINAPI
1125 mouse_event(
1126     DWORD dwFlags,
1127     DWORD dx,
1128     DWORD dy,
1129     DWORD dwData,
1130     ULONG_PTR dwExtraInfo)
1131 {
1132     INPUT Input;
1133 
1134     Input.type = INPUT_MOUSE;
1135     Input.mi.dx = dx;
1136     Input.mi.dy = dy;
1137     Input.mi.mouseData = dwData;
1138     Input.mi.dwFlags = dwFlags;
1139     Input.mi.time = 0;
1140     Input.mi.dwExtraInfo = dwExtraInfo;
1141 
1142     NtUserSendInput(1, &Input, sizeof(INPUT));
1143 }
1144 
1145 /* EOF */
1146