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