diff --git a/dll/win32/imm32/CtfImeTable.h b/dll/win32/imm32/CtfImeTable.h new file mode 100644 index 00000000000..14f701b6c46 --- /dev/null +++ b/dll/win32/imm32/CtfImeTable.h @@ -0,0 +1,18 @@ +/* + * PROJECT: ReactOS IMM32 + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Defining the CTF IME file interface + * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ + */ + +/* The valid CTF IME file contains the following functions: */ + +/* DEFINE_CTF_IME_FN(func_name, ret_type, params) */ +DEFINE_CTF_IME_FN(CtfImeCreateThreadMgr, HRESULT, (VOID)) +DEFINE_CTF_IME_FN(CtfImeDestroyThreadMgr, HRESULT, (VOID)) +DEFINE_CTF_IME_FN(CtfImeCreateInputContext, HRESULT, (HIMC hIMC)) +DEFINE_CTF_IME_FN(CtfImeDestroyInputContext, HRESULT, (HIMC hIMC)) +DEFINE_CTF_IME_FN(CtfImeSetActiveContextAlways, HRESULT, (DWORD dwFIXME1, DWORD dwFIXME2, DWORD dwFIXME3, DWORD dwFIXME4)) +DEFINE_CTF_IME_FN(CtfImeProcessCicHotkey, HRESULT, (DWORD dwFIXME1, DWORD dwFIXME2, DWORD dwFIXME3)) +DEFINE_CTF_IME_FN(CtfImeDispatchDefImeMessage, LRESULT, (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)) +DEFINE_CTF_IME_FN(CtfImeIsIME, BOOL, (HKL hKL)) diff --git a/dll/win32/imm32/ctf.c b/dll/win32/imm32/ctf.c index 8495725ed0f..eeeae62b588 100644 --- a/dll/win32/imm32/ctf.c +++ b/dll/win32/imm32/ctf.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS IMM32 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) * PURPOSE: Implementing the IMM32 Cicero-aware Text Framework (CTF) - * COPYRIGHT: Copyright 2022 Katayama Hirofumi MZ + * COPYRIGHT: Copyright 2022-2023 Katayama Hirofumi MZ */ #include "precomp.h" @@ -17,62 +17,326 @@ WINE_DEFAULT_DEBUG_CHANNEL(imm); * https://googleprojectzero.blogspot.com/2019/08/down-rabbit-hole.html */ -// Win: LoadCtfIme -HMODULE APIENTRY Imm32LoadCtfIme(VOID) +/* + * TSF stands for "Text Services Framework". "Cicero" is the code name of TSF. + * CTF stands for "Cicero-aware Text Framework". + * + * Comparing with old-style IMM IME, the combination of CTF IME and TSF provides + * new-style and high-level input method. + * + * The CTF IME file is a DLL file that the software developer distributes. + * The export functions of the CTF IME file are defined in "CtfImeTable.h" of + * this folder. + */ + +/* The instance of the CTF IME file */ +HINSTANCE g_hCtfIme = NULL; + +/* Define the function types (FN_...) for CTF IME functions */ +#undef DEFINE_CTF_IME_FN +#define DEFINE_CTF_IME_FN(func_name, ret_type, params) \ + typedef ret_type (WINAPI *FN_##func_name)params; +#include "CtfImeTable.h" + +/* Define the global variables (g_pfn...) for CTF IME functions */ +#undef DEFINE_CTF_IME_FN +#define DEFINE_CTF_IME_FN(func_name, ret_type, params) \ + FN_##func_name g_pfn##func_name = NULL; +#include "CtfImeTable.h" + +/* The macro that gets the variable name from the CTF IME function name */ +#define CTF_IME_FN(func_name) g_pfn##func_name + +/* The type of ApphelpCheckIME function in apphelp.dll */ +typedef BOOL (WINAPI *FN_ApphelpCheckIME)(_In_z_ LPCWSTR AppName); + +/* FIXME: This is kernel32 function. We have to declare this in some header. */ +BOOL WINAPI +BaseCheckAppcompatCache(_In_z_ LPCWSTR ApplicationName, + _In_ HANDLE FileHandle, + _In_opt_z_ LPCWSTR Environment, + _Out_ PULONG pdwReason); + +/*********************************************************************** + * This function checks whether the app's IME is disabled by application + * compatibility patcher. + */ +BOOL +Imm32CheckAndApplyAppCompat( + _In_ ULONG dwReason, + _In_z_ LPCWSTR pszAppName) { - return NULL; + HINSTANCE hinstApphelp; + FN_ApphelpCheckIME pApphelpCheckIME; + + /* Query the application compatibility patcher */ + if (BaseCheckAppcompatCache(pszAppName, INVALID_HANDLE_VALUE, NULL, &dwReason)) + return TRUE; /* The app's IME is not disabled */ + + /* Load apphelp.dll if necessary */ + hinstApphelp = GetModuleHandleW(L"apphelp.dll"); + if (!hinstApphelp) + { + hinstApphelp = LoadLibraryW(L"apphelp.dll"); + if (!hinstApphelp) + return TRUE; /* There is no apphelp.dll. The app's IME is not disabled */ + } + + /* Is ApphelpCheckIME implemented? */ + pApphelpCheckIME = (FN_ApphelpCheckIME)GetProcAddress(hinstApphelp, "ApphelpCheckIME"); + if (!pApphelpCheckIME) + return TRUE; /* Not implemented. The app's IME is not disabled */ + + /* Is the app's IME disabled or not? */ + return pApphelpCheckIME(pszAppName); } -// Win: Internal_CtfImeDestroyInputContext -HRESULT APIENTRY Imm32CtfImeDestroyInputContext(HIMC hIMC) +/*********************************************************************** + * This function loads the CTF IME file if necessary and establishes + * communication with the CTF IME. + */ +HINSTANCE +Imm32LoadCtfIme(VOID) +{ + BOOL bSuccess = FALSE; + IMEINFOEX ImeInfoEx; + WCHAR szImeFile[MAX_PATH]; + + /* Lock the IME interface */ + RtlEnterCriticalSection(&gcsImeDpi); + + do + { + if (g_hCtfIme) /* Already loaded? */ + { + bSuccess = TRUE; + break; + } + + /* + * NOTE: (HKL)0x04090409 is English US keyboard (default). + * The Cicero keyboard logically uses English US keyboard. + */ + if (!ImmLoadLayout((HKL)ULongToHandle(0x04090409), &ImeInfoEx)) + break; + + /* Build a path string in system32. The installed IME file must be in system32. */ + Imm32GetSystemLibraryPath(szImeFile, _countof(szImeFile), ImeInfoEx.wszImeFile); + + /* Is the CTF IME disabled by app compatibility patcher? */ + if (!Imm32CheckAndApplyAppCompat(0, szImeFile)) + break; /* This IME is disabled */ + + /* Load a CTF IME file */ + g_hCtfIme = LoadLibraryW(szImeFile); + if (!g_hCtfIme) + break; + + /* Assume success */ + bSuccess = TRUE; + + /* Retrieve the CTF IME functions */ +#undef DEFINE_CTF_IME_FN +#define DEFINE_CTF_IME_FN(func_name, ret_type, params) \ + CTF_IME_FN(func_name) = (FN_##func_name)GetProcAddress(g_hCtfIme, #func_name); \ + if (!CTF_IME_FN(func_name)) \ + { \ + bSuccess = FALSE; /* Failed */ \ + break; \ + } +#include "CtfImeTable.h" + } while (0); + + /* Unload the CTF IME if failed */ + if (!bSuccess) + { + /* Set NULL to the function pointers */ +#undef DEFINE_CTF_IME_FN +#define DEFINE_CTF_IME_FN(func_name, ret_type, params) CTF_IME_FN(func_name) = NULL; +#include "CtfImeTable.h" + + if (g_hCtfIme) + { + FreeLibrary(g_hCtfIme); + g_hCtfIme = NULL; + } + } + + /* Unlock the IME interface */ + RtlLeaveCriticalSection(&gcsImeDpi); + + return g_hCtfIme; +} + +/*********************************************************************** + * This function calls the same name function of the CTF IME side. + */ +HRESULT +CtfImeCreateThreadMgr(VOID) { if (!Imm32LoadCtfIme()) return E_FAIL; -#if 1 - FIXME("(%p)\n", hIMC); - return E_NOTIMPL; -#else - return g_pfnCtfImeDestroyInputContext(hIMC); -#endif + return CTF_IME_FN(CtfImeCreateThreadMgr)(); } -// Win: CtfImmTIMDestroyInputContext -HRESULT APIENTRY CtfImmTIMDestroyInputContext(HIMC hIMC) +/*********************************************************************** + * This function calls the same name function of the CTF IME side. + */ +HRESULT +CtfImeDestroyThreadMgr(VOID) +{ + if (!Imm32LoadCtfIme()) + return E_FAIL; + + return CTF_IME_FN(CtfImeDestroyThreadMgr)(); +} + +/*********************************************************************** + * This function calls the same name function of the CTF IME side. + */ +HRESULT +CtfImeCreateInputContext( + _In_ HIMC hIMC) +{ + if (!Imm32LoadCtfIme()) + return E_FAIL; + + return CTF_IME_FN(CtfImeCreateInputContext)(hIMC); +} + +/*********************************************************************** + * This function calls the same name function of the CTF IME side. + */ +HRESULT +CtfImeDestroyInputContext(_In_ HIMC hIMC) +{ + if (!Imm32LoadCtfIme()) + return E_FAIL; + + return CTF_IME_FN(CtfImeDestroyInputContext)(hIMC); +} + +/*********************************************************************** + * The callback function to activate CTF IMEs. Used in CtfAImmActivate. + */ +static BOOL CALLBACK +Imm32EnumCreateCtfICProc( + _In_ HIMC hIMC, + _In_ LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + CtfImeCreateInputContext(hIMC); + return TRUE; /* Continue */ +} + +/*********************************************************************** + * This function calls CtfImeDestroyInputContext if possible. + */ +HRESULT +CtfImmTIMDestroyInputContext( + _In_ HIMC hIMC) { if (!IS_CICERO_MODE() || (GetWin32ClientInfo()->dwCompatFlags2 & 2)) return E_NOINTERFACE; - return Imm32CtfImeDestroyInputContext(hIMC); + return CtfImeDestroyInputContext(hIMC); } -// Win: CtfImmTIMCreateInputContext -HRESULT APIENTRY CtfImmTIMCreateInputContext(HIMC hIMC) +HRESULT +CtfImmTIMCreateInputContext( + _In_ HIMC hIMC) { TRACE("(%p)\n", hIMC); return E_NOTIMPL; } /*********************************************************************** - * CtfImmIsCiceroEnabled (IMM32.@) + * CtfAImmActivate (IMM32.@) + * + * This function activates "Active IMM" (AIMM) and TSF. */ -BOOL WINAPI CtfImmIsCiceroEnabled(VOID) +HRESULT WINAPI +CtfAImmActivate( + _Out_opt_ HINSTANCE *phinstCtfIme) +{ + HRESULT hr; + HINSTANCE hinstCtfIme; + + TRACE("(%p)\n", phinstCtfIme); + + /* Load a CTF IME file if necessary */ + hinstCtfIme = Imm32LoadCtfIme(); + + /* Create a thread manager of the CTF IME */ + hr = CtfImeCreateThreadMgr(); + if (hr == S_OK) + { + /* Update CI_... flags of the thread client info */ + GetWin32ClientInfo()->CI_flags |= CI_AIMMACTIVATED; /* Activate AIMM */ + GetWin32ClientInfo()->CI_flags &= ~CI_TSFDISABLED; /* Enable TSF */ + + /* Create the CTF input contexts */ + ImmEnumInputContext(0, Imm32EnumCreateCtfICProc, 0); + } + + if (phinstCtfIme) + *phinstCtfIme = hinstCtfIme; + + return hr; +} + +/*********************************************************************** + * CtfAImmDeactivate (IMM32.@) + * + * This function de-activates "Active IMM" (AIMM) and TSF. + */ +HRESULT WINAPI +CtfAImmDeactivate( + _In_ BOOL bDestroy) +{ + HRESULT hr; + + if (!bDestroy) + return E_FAIL; + + hr = CtfImeDestroyThreadMgr(); + if (hr == S_OK) + { + GetWin32ClientInfo()->CI_flags &= ~CI_AIMMACTIVATED; /* Deactivate AIMM */ + GetWin32ClientInfo()->CI_flags |= CI_TSFDISABLED; /* Disable TSF */ + } + + return hr; +} + +/*********************************************************************** + * CtfImmIsCiceroEnabled (IMM32.@) + * + * @return TRUE if Cicero is enabled. + */ +BOOL WINAPI +CtfImmIsCiceroEnabled(VOID) { return IS_CICERO_MODE(); } /*********************************************************************** * CtfImmIsTextFrameServiceDisabled(IMM32.@) + * + * @return TRUE if TSF is disabled. */ -BOOL WINAPI CtfImmIsTextFrameServiceDisabled(VOID) +BOOL WINAPI +CtfImmIsTextFrameServiceDisabled(VOID) { - return !!(GetWin32ClientInfo()->CI_flags & CI_TFSDISABLED); + return !!(GetWin32ClientInfo()->CI_flags & CI_TSFDISABLED); } /*********************************************************************** * CtfImmTIMActivate(IMM32.@) */ -HRESULT WINAPI CtfImmTIMActivate(HKL hKL) +HRESULT WINAPI +CtfImmTIMActivate(_In_ HKL hKL) { FIXME("(%p)\n", hKL); return E_NOTIMPL; @@ -81,7 +345,8 @@ HRESULT WINAPI CtfImmTIMActivate(HKL hKL) /*********************************************************************** * CtfImmRestoreToolbarWnd(IMM32.@) */ -VOID WINAPI CtfImmRestoreToolbarWnd(DWORD dwStatus) +VOID WINAPI +CtfImmRestoreToolbarWnd(_In_ DWORD dwStatus) { FIXME("(0x%lx)\n", dwStatus); } @@ -89,7 +354,8 @@ VOID WINAPI CtfImmRestoreToolbarWnd(DWORD dwStatus) /*********************************************************************** * CtfImmHideToolbarWnd(IMM32.@) */ -DWORD WINAPI CtfImmHideToolbarWnd(VOID) +DWORD WINAPI +CtfImmHideToolbarWnd(VOID) { FIXME("()\n"); return 0; @@ -98,7 +364,12 @@ DWORD WINAPI CtfImmHideToolbarWnd(VOID) /*********************************************************************** * CtfImmDispatchDefImeMessage(IMM32.@) */ -LRESULT WINAPI CtfImmDispatchDefImeMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +LRESULT WINAPI +CtfImmDispatchDefImeMessage( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam) { /* FIXME("(%p, %u, %p, %p)\n", hWnd, uMsg, wParam, lParam); */ return 0; @@ -107,7 +378,9 @@ LRESULT WINAPI CtfImmDispatchDefImeMessage(HWND hWnd, UINT uMsg, WPARAM wParam, /*********************************************************************** * CtfImmIsGuidMapEnable(IMM32.@) */ -BOOL WINAPI CtfImmIsGuidMapEnable(HIMC hIMC) +BOOL WINAPI +CtfImmIsGuidMapEnable( + _In_ HIMC hIMC) { DWORD dwThreadId; HKL hKL; @@ -138,7 +411,11 @@ BOOL WINAPI CtfImmIsGuidMapEnable(HIMC hIMC) /*********************************************************************** * CtfImmGetGuidAtom(IMM32.@) */ -HRESULT WINAPI CtfImmGetGuidAtom(HIMC hIMC, DWORD dwUnknown, LPDWORD pdwGuidAtom) +HRESULT WINAPI +CtfImmGetGuidAtom( + _In_ HIMC hIMC, + _In_ DWORD dwUnknown, + _Out_ LPDWORD pdwGuidAtom) { HRESULT hr = E_FAIL; PIMEDPI pImeDpi; diff --git a/dll/win32/imm32/imm32.spec b/dll/win32/imm32/imm32.spec index 92ac837fffd..eec7202482a 100644 --- a/dll/win32/imm32/imm32.spec +++ b/dll/win32/imm32/imm32.spec @@ -1,3 +1,5 @@ +@ stdcall CtfAImmActivate(ptr) +@ stdcall CtfAImmDeactivate(long) @ stdcall CtfImmIsCiceroEnabled() @ stdcall CtfImmIsTextFrameServiceDisabled() @ stdcall CtfImmTIMActivate(ptr) diff --git a/dll/win32/imm32/precomp.h b/dll/win32/imm32/precomp.h index 6db1f8d6e4d..6e0512af409 100644 --- a/dll/win32/imm32/precomp.h +++ b/dll/win32/imm32/precomp.h @@ -143,7 +143,6 @@ BOOL WINAPI Imm32IsImcAnsi(HIMC hIMC); #define IS_CROSS_THREAD_HIMC(hIMC) IS_TRUE_UNEXPECTEDLY(Imm32IsCrossThreadAccess(hIMC)) #define IS_CROSS_PROCESS_HWND(hWnd) IS_TRUE_UNEXPECTEDLY(Imm32IsCrossProcessAccess(hWnd)) #define ImeDpi_IsUnicode(pImeDpi) ((pImeDpi)->ImeInfo.fdwProperty & IME_PROP_UNICODE) -#define IS_16BIT_MODE() (GetWin32ClientInfo()->dwTIFlags & TIF_16BIT) DWORD APIENTRY CandidateListWideToAnsi(const CANDIDATELIST *pWideCL, LPCANDIDATELIST pAnsiCL, DWORD dwBufLen, @@ -189,3 +188,6 @@ PTHREADINFO FASTCALL Imm32CurrentPti(VOID); HBITMAP Imm32LoadBitmapFromBytes(const BYTE *pb); BOOL Imm32StoreBitmapToBytes(HBITMAP hbm, LPBYTE pbData, DWORD cbDataMax); + +HRESULT CtfImmTIMCreateInputContext(_In_ HIMC hIMC); +HRESULT CtfImmTIMDestroyInputContext(_In_ HIMC hIMC); diff --git a/sdk/include/reactos/imm32_undoc.h b/sdk/include/reactos/imm32_undoc.h index dadff68b031..49cb047182a 100644 --- a/sdk/include/reactos/imm32_undoc.h +++ b/sdk/include/reactos/imm32_undoc.h @@ -14,14 +14,16 @@ extern "C" { BOOL WINAPI ImmGetImeInfoEx(PIMEINFOEX pImeInfoEx, IMEINFOEXCLASS SearchType, PVOID pvSearchKey); +BOOL WINAPI ImmLoadLayout(HKL hKL, PIMEINFOEX pImeInfoEx); PCLIENTIMC WINAPI ImmLockClientImc(HIMC hImc); VOID WINAPI ImmUnlockClientImc(PCLIENTIMC pClientImc); PIMEDPI WINAPI ImmLockImeDpi(HKL hKL); VOID WINAPI ImmUnlockImeDpi(PIMEDPI pImeDpi); -HRESULT APIENTRY CtfImmTIMCreateInputContext(HIMC hIMC); -HRESULT APIENTRY CtfImmTIMDestroyInputContext(HIMC hIMC); HRESULT WINAPI CtfImmTIMActivate(HKL hKL); +HRESULT WINAPI CtfAImmActivate(_Out_opt_ HINSTANCE *phinstCtfIme); +HRESULT WINAPI CtfAImmDeactivate(_In_ BOOL bDestroy); + #ifdef __cplusplus } // extern "C" #endif diff --git a/win32ss/include/ntuser.h b/win32ss/include/ntuser.h index e1a3ee9788a..1cb0fb2db4c 100644 --- a/win32ss/include/ntuser.h +++ b/win32ss/include/ntuser.h @@ -302,8 +302,9 @@ typedef struct _CALLBACKWND #define CI_INITTHREAD 0x00000008 #define CI_CURTHPRHOOK 0x00000010 #define CI_CLASSESREGISTERED 0x00000020 -#define CI_IMMACTIVATE 0x00000040 -#define CI_TFSDISABLED 0x00000400 +#define CI_IMMACTIVATE 0x00000040 /* IMM/IME (Asian input) */ +#define CI_TSFDISABLED 0x00000400 /* TSF (Text Services Framework a.k.a. Cicero) */ +#define CI_AIMMACTIVATED 0x00000800 /* Active IMM (AIMM) */ /* * CLIENTINFO structure. @@ -1231,6 +1232,7 @@ typedef enum IMEINFOEXCLASS #define IS_IME_HKL(hkl) ((((ULONG_PTR)(hkl)) & 0xF0000000) == 0xE0000000) #define IS_IMM_MODE() (gpsi && (gpsi->dwSRVIFlags & SRVINFO_IMM32)) #define IS_CICERO_MODE() (gpsi && (gpsi->dwSRVIFlags & SRVINFO_CICERO_ENABLED)) +#define IS_16BIT_MODE() (GetWin32ClientInfo()->dwTIFlags & TIF_16BIT) typedef struct tagIMEUI {