From 7396ba84ce54f4841913289da17e3cb9a98edbb4 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Wed, 11 May 2022 10:03:02 +0900 Subject: [PATCH] [USER32] Implement UnloadKeyboardLayout (#4503) - Add IntSetFeKeyboardFlags, CliImmSetHotKeyWorker, CliSetDefaultImeHotKeys, CliGetPreloadKeyboardLayouts, CliGetImeHotKeysFromRegistry, CliImmInitializeHotKeys, CliSetSingleHotKey, and CliReadRegistryValue helper functions. CORE-11700 --- win32ss/user/user32/user32.spec | 2 +- win32ss/user/user32/windows/input.c | 285 ++++++++++++++++++++++++++++ 2 files changed, 286 insertions(+), 1 deletion(-) diff --git a/win32ss/user/user32/user32.spec b/win32ss/user/user32/user32.spec index 9e87f95c49f..c026984892a 100644 --- a/win32ss/user/user32/user32.spec +++ b/win32ss/user/user32/user32.spec @@ -703,7 +703,7 @@ 694 stdcall UnhookWindowsHook(long ptr) 695 stdcall UnhookWindowsHookEx(long) NtUserUnhookWindowsHookEx 696 stdcall UnionRect(ptr ptr ptr) -697 stdcall UnloadKeyboardLayout(long) NtUserUnloadKeyboardLayout +697 stdcall UnloadKeyboardLayout(ptr) 698 stdcall UnlockWindowStation(long) NtUserUnlockWindowStation 699 stdcall UnpackDDElParam(long long ptr ptr) 700 stdcall UnregisterClassA(str long) diff --git a/win32ss/user/user32/windows/input.c b/win32ss/user/user32/windows/input.c index aa398d7dcc5..4cd7832d7b3 100644 --- a/win32ss/user/user32/windows/input.c +++ b/win32ss/user/user32/windows/input.c @@ -31,6 +31,279 @@ WINE_DEFAULT_DEBUG_CHANNEL(user32); +typedef struct tagIMEHOTKEYENTRY +{ + DWORD dwHotKeyId; + UINT uVirtualKey; + UINT uModifiers; + HKL hKL; +} IMEHOTKEYENTRY, *PIMEHOTKEYENTRY; + +// Japanese +IMEHOTKEYENTRY DefaultHotKeyTableJ[] = +{ + { IME_JHOTKEY_CLOSE_OPEN, VK_KANJI, MOD_IGNORE_ALL_MODIFIER, NULL }, +}; + +// Chinese Traditional +IMEHOTKEYENTRY DefaultHotKeyTableT[] = +{ + { IME_THOTKEY_IME_NONIME_TOGGLE, VK_SPACE, MOD_LEFT | MOD_RIGHT | MOD_CONTROL, NULL }, + { IME_THOTKEY_SHAPE_TOGGLE, VK_SPACE, MOD_LEFT | MOD_RIGHT | MOD_SHIFT, NULL }, +}; + +// Chinese Simplified +IMEHOTKEYENTRY DefaultHotKeyTableC[] = +{ + { IME_CHOTKEY_IME_NONIME_TOGGLE, VK_SPACE, MOD_LEFT | MOD_RIGHT | MOD_CONTROL, NULL }, + { IME_CHOTKEY_SHAPE_TOGGLE, VK_SPACE, MOD_LEFT | MOD_RIGHT | MOD_SHIFT, NULL }, +}; + +// The far-east flags +#define FE_JAPANESE (1 << 0) +#define FE_CHINESE_TRADITIONAL (1 << 1) +#define FE_CHINESE_SIMPLIFIED (1 << 2) +#define FE_KOREAN (1 << 3) + +// Sets the far-east flags +// Win: SetFeKeyboardFlags +VOID FASTCALL IntSetFeKeyboardFlags(LANGID LangID, PBYTE pbFlags) +{ + switch (LangID) + { + case MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT): + *pbFlags |= FE_JAPANESE; + break; + + case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL): + case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG): + *pbFlags |= FE_CHINESE_TRADITIONAL; + break; + + case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED): + case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE): + *pbFlags |= FE_CHINESE_SIMPLIFIED; + break; + + case MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN): + *pbFlags |= FE_KOREAN; + break; + + default: + break; + } +} + +DWORD FASTCALL CliReadRegistryValue(HANDLE hKey, LPCWSTR pszName) +{ + DWORD dwValue, cbValue; + LONG error; + + cbValue = sizeof(dwValue); + error = RegQueryValueExW(hKey, pszName, NULL, NULL, (LPBYTE)&dwValue, &cbValue); + if (error != ERROR_SUCCESS || cbValue < sizeof(DWORD)) + return 0; + + return dwValue; +} + +BOOL APIENTRY +CliImmSetHotKeyWorker(DWORD dwHotKeyId, UINT uModifiers, UINT uVirtualKey, HKL hKL, DWORD dwAction) +{ + if (dwAction == SETIMEHOTKEY_ADD) + { + if (IME_HOTKEY_DSWITCH_FIRST <= dwHotKeyId && dwHotKeyId <= IME_HOTKEY_DSWITCH_LAST) + { + if (!hKL) + goto Failure; + } + else + { + if (hKL) + goto Failure; + + if (IME_KHOTKEY_SHAPE_TOGGLE <= dwHotKeyId && + dwHotKeyId < IME_THOTKEY_IME_NONIME_TOGGLE) + { + // The Korean cannot set the IME hotkeys + goto Failure; + } + } + +#define MOD_ALL_MODS (MOD_ALT | MOD_CONTROL | MOD_SHIFT | MOD_WIN) + if ((uModifiers & MOD_ALL_MODS) && !(uModifiers & (MOD_LEFT | MOD_RIGHT))) + goto Failure; +#undef MOD_ALL_MODS + } + + return NtUserSetImeHotKey(dwHotKeyId, uModifiers, uVirtualKey, hKL, dwAction); + +Failure: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; +} + +BOOL FASTCALL CliSetSingleHotKey(LPCWSTR pszSubKey, HANDLE hKey) +{ + LONG error; + HKEY hSubKey; + DWORD dwHotKeyId = 0; + UINT uModifiers = 0, uVirtualKey = 0; + HKL hKL = NULL; + UNICODE_STRING ustrName; + + error = RegOpenKeyExW(hKey, pszSubKey, 0, KEY_READ, &hSubKey); + if (error != ERROR_SUCCESS) + return FALSE; + + RtlInitUnicodeString(&ustrName, pszSubKey); + RtlUnicodeStringToInteger(&ustrName, 16, &dwHotKeyId); + + uModifiers = CliReadRegistryValue(hSubKey, L"Key Modifiers"); + hKL = (HKL)(ULONG_PTR)CliReadRegistryValue(hSubKey, L"Target IME"); + uVirtualKey = CliReadRegistryValue(hSubKey, L"Virtual Key"); + + RegCloseKey(hSubKey); + + return CliImmSetHotKeyWorker(dwHotKeyId, uModifiers, uVirtualKey, hKL, SETIMEHOTKEY_ADD); +} + +BOOL FASTCALL CliGetImeHotKeysFromRegistry(VOID) +{ + HKEY hKey; + LONG error; + BOOL ret = FALSE; + DWORD dwIndex, cchKeyName; + WCHAR szKeyName[16]; + + error = RegOpenKeyExW(HKEY_CURRENT_USER, + L"Control Panel\\Input Method\\Hot Keys", + 0, + KEY_ALL_ACCESS, + &hKey); + if (error != ERROR_SUCCESS) + return ret; + + for (dwIndex = 0; ; ++dwIndex) + { + cchKeyName = _countof(szKeyName); + error = RegEnumKeyExW(hKey, dwIndex, szKeyName, &cchKeyName, NULL, NULL, NULL, NULL); + if (error == ERROR_NO_MORE_ITEMS || error != ERROR_SUCCESS) + break; + + szKeyName[_countof(szKeyName) - 1] = 0; + + if (CliSetSingleHotKey(szKeyName, hKey)) + ret = TRUE; + } + + RegCloseKey(hKey); + return ret; +} + +VOID APIENTRY CliGetPreloadKeyboardLayouts(PBYTE pbFlags) +{ + WCHAR szValueName[8], szValue[16]; + UNICODE_STRING ustrValue; + DWORD dwKL, cbValue, dwType; + UINT iNumber; + HKEY hKey; + LONG error; + + error = RegOpenKeyExW(HKEY_CURRENT_USER, L"Keyboard Layout\\Preload", 0, KEY_READ, &hKey); + if (error != ERROR_SUCCESS) + return; + + for (iNumber = 1; iNumber < 1000; ++iNumber) + { + StringCchPrintfW(szValueName, _countof(szValueName), L"%u", iNumber); + + cbValue = sizeof(szValue); + error = RegQueryValueExW(hKey, szValueName, NULL, &dwType, (LPBYTE)szValue, &cbValue); + if (error != ERROR_SUCCESS || dwType != REG_SZ) + break; + + szValue[_countof(szValue) - 1] = 0; + + RtlInitUnicodeString(&ustrValue, szValue); + RtlUnicodeStringToInteger(&ustrValue, 16, &dwKL); + + IntSetFeKeyboardFlags(LOWORD(dwKL), pbFlags); + } + + RegCloseKey(hKey); +} + +VOID APIENTRY CliSetDefaultImeHotKeys(PIMEHOTKEYENTRY pEntries, UINT nCount, BOOL bCheck) +{ + UINT uVirtualKey, uModifiers; + HKL hKL; + + while (nCount-- > 0) + { + if (!bCheck || !NtUserGetImeHotKey(pEntries->dwHotKeyId, &uModifiers, &uVirtualKey, &hKL)) + { + CliImmSetHotKeyWorker(pEntries->dwHotKeyId, + pEntries->uModifiers, + pEntries->uVirtualKey, + pEntries->hKL, + SETIMEHOTKEY_ADD); + } + ++pEntries; + } +} + +VOID APIENTRY CliImmInitializeHotKeys(DWORD dwAction, HKL hKL) +{ + UINT nCount; + LPHKL pList; + UINT iIndex; + LANGID LangID; + BYTE bFlags = 0; + BOOL bCheck; + + NtUserSetImeHotKey(0, 0, 0, NULL, SETIMEHOTKEY_DELETEALL); + + bCheck = CliGetImeHotKeysFromRegistry(); + + if (dwAction == SETIMEHOTKEY_DELETEALL) + { + LangID = LANGIDFROMLCID(GetUserDefaultLCID()); + IntSetFeKeyboardFlags(LangID, &bFlags); + + CliGetPreloadKeyboardLayouts(&bFlags); + } + else + { + nCount = NtUserGetKeyboardLayoutList(0, NULL); + if (!nCount) + return; + + pList = RtlAllocateHeap(RtlGetProcessHeap(), 0, nCount * sizeof(HKL)); + if (!pList) + return; + + NtUserGetKeyboardLayoutList(nCount, pList); + + for (iIndex = 0; iIndex < nCount; ++iIndex) + { + LangID = LOWORD(pList[iIndex]); + IntSetFeKeyboardFlags(LangID, &bFlags); + } + + RtlFreeHeap(RtlGetProcessHeap(), 0, pList); + } + + if (bFlags & FE_JAPANESE) + CliSetDefaultImeHotKeys(DefaultHotKeyTableJ, _countof(DefaultHotKeyTableJ), bCheck); + + if (bFlags & FE_CHINESE_TRADITIONAL) + CliSetDefaultImeHotKeys(DefaultHotKeyTableT, _countof(DefaultHotKeyTableT), bCheck); + + if (bFlags & FE_CHINESE_SIMPLIFIED) + CliSetDefaultImeHotKeys(DefaultHotKeyTableC, _countof(DefaultHotKeyTableC), bCheck); +} + /* * @implemented */ @@ -330,6 +603,18 @@ LoadKeyboardLayoutW(LPCWSTR pwszKLID, dwhkl, Flags); } +/* + * @implemented + */ +BOOL WINAPI UnloadKeyboardLayout(HKL hKL) +{ + if (!NtUserUnloadKeyboardLayout(hKL)) + return FALSE; + + CliImmInitializeHotKeys(SETIMEHOTKEY_DELETE, hKL); + return TRUE; +} + /* * @implemented */