* PROJECT: ReactOS Win32k subsystem
* PURPOSE: Input Method Editor and Input Method Manager support
* FILE: win32ss/user/ntuser/ime.c
- * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * PROGRAMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
*/
#include <win32k.h>
+#include <jpnvkeys.h>
+
DBG_DEFAULT_CHANNEL(UserMisc);
+#define INVALID_THREAD_ID ((ULONG)-1)
+#define INVALID_HOTKEY ((UINT)-1)
+#define MOD_KEYS (MOD_CONTROL | MOD_SHIFT | MOD_ALT | MOD_WIN)
+#define MOD_LEFT_RIGHT (MOD_LEFT | MOD_RIGHT)
-UINT FASTCALL
-IntImmProcessKey(PUSER_MESSAGE_QUEUE MessageQueue, PWND pWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
+#define LANGID_CHINESE_SIMPLIFIED MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)
+#define LANGID_JAPANESE MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT)
+#define LANGID_KOREAN MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN)
+#define LANGID_CHINESE_TRADITIONAL MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
+#define LANGID_NEUTRAL MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)
+
+HIMC ghIMC = NULL;
+BOOL gfImeOpen = (BOOL)-1;
+DWORD gdwImeConversion = (DWORD)-1;
+BOOL gfIMEShowStatus = (BOOL)-1;
+
+typedef struct tagIMEHOTKEY
{
- PKL pKbdLayout;
+ struct tagIMEHOTKEY *pNext;
+ DWORD dwHotKeyId;
+ UINT uVirtualKey;
+ UINT uModifiers;
+ HKL hKL;
+} IMEHOTKEY, *PIMEHOTKEY;
- ASSERT_REFS_CO(pWnd);
+PIMEHOTKEY gpImeHotKeyList = NULL; // Win: gpImeHotKeyListHeader
+LCID glcidSystem = 0; // Win: glcidSystem
+
+// Win: GetAppImeCompatFlags
+DWORD FASTCALL IntGetImeCompatFlags(PTHREADINFO pti)
+{
+ if (!pti)
+ pti = PsGetCurrentThreadWin32Thread();
+
+ return pti->ppi->dwImeCompatFlags;
+}
+
+// Win: GetLangIdMatchLevel
+UINT FASTCALL IntGetImeHotKeyLanguageScore(HKL hKL, LANGID HotKeyLangId)
+{
+ LCID lcid;
+
+ if (HotKeyLangId == LANGID_NEUTRAL || HotKeyLangId == LOWORD(hKL))
+ return 3;
+
+ _SEH2_TRY
+ {
+ lcid = NtCurrentTeb()->CurrentLocale;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p\n", NtCurrentTeb());
+ lcid = MAKELCID(LANGID_NEUTRAL, SORT_DEFAULT);
+ }
+ _SEH2_END;
+
+ if (HotKeyLangId == LANGIDFROMLCID(lcid))
+ return 2;
+
+ if (glcidSystem == 0)
+ ZwQueryDefaultLocale(FALSE, &glcidSystem);
+
+ if (HotKeyLangId == LANGIDFROMLCID(glcidSystem))
+ return 1;
- if ( Msg == WM_KEYDOWN ||
- Msg == WM_SYSKEYDOWN ||
- Msg == WM_KEYUP ||
- Msg == WM_SYSKEYUP )
- {
- //Vk = wParam & 0xff;
- pKbdLayout = pWnd->head.pti->KeyboardLayout;
- if (pKbdLayout == NULL) return 0;
- //
- if (!(gpsi->dwSRVIFlags & SRVINFO_IMM32)) return 0;
- // need ime.h!
- }
- // Call User32:
- // Anything but BOOL!
- //ImmRet = co_IntImmProcessKey(UserHMGetHandle(pWnd), pKbdLayout->hkl, Vk, lParam, HotKey);
- FIXME(" is UNIMPLEMENTED.\n");
return 0;
}
+// Win: GetActiveHKL
+HKL FASTCALL IntGetActiveKeyboardLayout(VOID)
+{
+ PTHREADINFO pti;
-DWORD
-APIENTRY
-NtUserGetImeHotKey(
- DWORD Unknown0,
- DWORD Unknown1,
- DWORD Unknown2,
- DWORD Unknown3)
+ if (gpqForeground && gpqForeground->spwndActive)
+ {
+ pti = gpqForeground->spwndActive->head.pti;
+ if (pti && pti->KeyboardLayout)
+ return pti->KeyboardLayout->hkl;
+ }
+
+ return UserGetKeyboardLayout(0);
+}
+
+// Win: GetHotKeyLangID
+static LANGID FASTCALL IntGetImeHotKeyLangId(DWORD dwHotKeyId)
{
- STUB
+#define IME_CHOTKEY 0x10
+#define IME_JHOTKEY 0x30
+#define IME_KHOTKEY 0x50
+#define IME_THOTKEY 0x70
+#define IME_XHOTKEY 0x90
+ static const LANGID s_array[] =
+ {
+ /* 0x00 */ (WORD)-1,
+ /* 0x10 */ LANGID_CHINESE_SIMPLIFIED,
+ /* 0x20 */ LANGID_CHINESE_SIMPLIFIED,
+ /* 0x30 */ LANGID_JAPANESE,
+ /* 0x40 */ LANGID_JAPANESE,
+ /* 0x50 */ LANGID_KOREAN,
+ /* 0x60 */ LANGID_KOREAN,
+ /* 0x70 */ LANGID_CHINESE_TRADITIONAL,
+ /* 0x80 */ LANGID_CHINESE_TRADITIONAL
+ };
- return 0;
+ if (IME_CHOTKEY <= dwHotKeyId && dwHotKeyId < IME_XHOTKEY)
+ return s_array[(dwHotKeyId & 0xF0) >> 4];
+ return LANGID_NEUTRAL;
}
-DWORD
-APIENTRY
-NtUserNotifyIMEStatus(
- DWORD Unknown0,
- DWORD Unknown1,
- DWORD Unknown2)
+// Win: AddImeHotKey
+static VOID FASTCALL IntAddImeHotKey(PIMEHOTKEY *ppList, PIMEHOTKEY pHotKey)
{
- STUB
+ PIMEHOTKEY pNode;
+
+ if (!*ppList)
+ {
+ *ppList = pHotKey;
+ return;
+ }
- return 0;
+ for (pNode = *ppList; pNode; pNode = pNode->pNext)
+ {
+ if (!pNode->pNext)
+ {
+ pNode->pNext = pHotKey;
+ return;
+ }
+ }
}
+// Win: FindImeHotKeyByID
+static PIMEHOTKEY FASTCALL IntGetImeHotKeyById(PIMEHOTKEY pList, DWORD dwHotKeyId)
+{
+ PIMEHOTKEY pNode;
+ for (pNode = pList; pNode; pNode = pNode->pNext)
+ {
+ if (pNode->dwHotKeyId == dwHotKeyId)
+ return pNode;
+ }
+ return NULL;
+}
-DWORD
-APIENTRY
-NtUserSetImeHotKey(
- DWORD Unknown0,
- DWORD Unknown1,
- DWORD Unknown2,
- DWORD Unknown3,
- DWORD Unknown4)
+// Win: FindImeHotKeyByKeyWithLang
+static PIMEHOTKEY APIENTRY
+IntGetImeHotKeyByKeyAndLang(PIMEHOTKEY pList, UINT uModKeys, UINT uLeftRight,
+ UINT uVirtualKey, LANGID TargetLangId)
{
- STUB
+ PIMEHOTKEY pNode;
+ LANGID LangID;
+ UINT uModifiers;
- return 0;
+ for (pNode = pList; pNode; pNode = pNode->pNext)
+ {
+ if (pNode->uVirtualKey != uVirtualKey)
+ continue;
+
+ LangID = IntGetImeHotKeyLangId(pNode->dwHotKeyId);
+ if (LangID != TargetLangId && LangID != 0)
+ continue;
+
+ uModifiers = pNode->uModifiers;
+ if (uModifiers & MOD_IGNORE_ALL_MODIFIER)
+ return pNode;
+
+ if ((uModifiers & MOD_KEYS) != uModKeys)
+ continue;
+
+ if ((uModifiers & uLeftRight) || (uModifiers & MOD_LEFT_RIGHT) == uLeftRight)
+ return pNode;
+ }
+
+ return NULL;
}
-DWORD
-APIENTRY
-NtUserCheckImeHotKey(
- DWORD VirtualKey,
- LPARAM lParam)
+// Win: DeleteImeHotKey
+static VOID FASTCALL IntDeleteImeHotKey(PIMEHOTKEY *ppList, PIMEHOTKEY pHotKey)
{
- STUB;
- return 0;
+ PIMEHOTKEY pNode;
+
+ if (*ppList == pHotKey)
+ {
+ *ppList = pHotKey->pNext;
+ ExFreePoolWithTag(pHotKey, USERTAG_IMEHOTKEY);
+ return;
+ }
+
+ for (pNode = *ppList; pNode; pNode = pNode->pNext)
+ {
+ if (pNode->pNext == pHotKey)
+ {
+ pNode->pNext = pHotKey->pNext;
+ ExFreePoolWithTag(pHotKey, USERTAG_IMEHOTKEY);
+ return;
+ }
+ }
}
+// Win: FindImeHotKeyByKey
+PIMEHOTKEY
+IntGetImeHotKeyByKey(PIMEHOTKEY pList, UINT uModKeys, UINT uLeftRight, UINT uVirtualKey)
+{
+ PIMEHOTKEY pNode, ret = NULL;
+ PTHREADINFO pti = GetW32ThreadInfo();
+ LANGID LangId;
+ HKL hKL = IntGetActiveKeyboardLayout();
+ BOOL fKorean = (PRIMARYLANGID(LOWORD(hKL)) == LANG_KOREAN);
+ UINT nScore, nMaxScore = 0;
+
+ for (pNode = pList; pNode; pNode = pNode->pNext)
+ {
+ if (pNode->uVirtualKey != uVirtualKey)
+ continue;
-DWORD
-APIENTRY
-NtUserDisableThreadIme(
- DWORD dwUnknown1)
+ if ((pNode->uModifiers & MOD_IGNORE_ALL_MODIFIER))
+ {
+ ;
+ }
+ else if ((pNode->uModifiers & MOD_KEYS) != uModKeys)
+ {
+ continue;
+ }
+ else if ((pNode->uModifiers & uLeftRight) ||
+ (pNode->uModifiers & MOD_LEFT_RIGHT) == uLeftRight)
+ {
+ ;
+ }
+ else
+ {
+ continue;
+ }
+
+ LangId = IntGetImeHotKeyLangId(pNode->dwHotKeyId);
+ nScore = IntGetImeHotKeyLanguageScore(hKL, LangId);
+ if (nScore >= 3)
+ return pNode;
+
+ if (fKorean)
+ continue;
+
+ if (nScore == 0)
+ {
+ if (pNode->dwHotKeyId == IME_CHOTKEY_IME_NONIME_TOGGLE ||
+ pNode->dwHotKeyId == IME_THOTKEY_IME_NONIME_TOGGLE)
+ {
+ if (LOWORD(pti->hklPrev) == LangId)
+ return pNode;
+ }
+ }
+
+ if (nMaxScore < nScore)
+ {
+ nMaxScore = nScore;
+ ret = pNode;
+ }
+ }
+
+ return ret;
+}
+
+// Win: CheckImeHotKey
+PIMEHOTKEY IntCheckImeHotKey(PUSER_MESSAGE_QUEUE MessageQueue, UINT uVirtualKey, LPARAM lParam)
{
- STUB;
- return 0;
+ PIMEHOTKEY pHotKey;
+ UINT uModifiers;
+ BOOL bKeyUp = (lParam & 0x80000000);
+ const BYTE *KeyState = MessageQueue->afKeyState;
+ static UINT s_uKeyUpVKey = 0;
+
+ if (bKeyUp)
+ {
+ if (s_uKeyUpVKey != uVirtualKey)
+ {
+ s_uKeyUpVKey = 0;
+ return NULL;
+ }
+
+ s_uKeyUpVKey = 0;
+ }
+
+ uModifiers = 0;
+ if (IS_KEY_DOWN(KeyState, VK_LSHIFT)) uModifiers |= (MOD_SHIFT | MOD_LEFT);
+ if (IS_KEY_DOWN(KeyState, VK_RSHIFT)) uModifiers |= (MOD_SHIFT | MOD_RIGHT);
+ if (IS_KEY_DOWN(KeyState, VK_LCONTROL)) uModifiers |= (MOD_CONTROL | MOD_LEFT);
+ if (IS_KEY_DOWN(KeyState, VK_RCONTROL)) uModifiers |= (MOD_CONTROL | MOD_RIGHT);
+ if (IS_KEY_DOWN(KeyState, VK_LMENU)) uModifiers |= (MOD_ALT | MOD_LEFT);
+ if (IS_KEY_DOWN(KeyState, VK_RMENU)) uModifiers |= (MOD_ALT | MOD_RIGHT);
+
+ pHotKey = IntGetImeHotKeyByKey(gpImeHotKeyList,
+ (uModifiers & MOD_KEYS),
+ (uModifiers & MOD_LEFT_RIGHT),
+ uVirtualKey);
+ if (pHotKey)
+ {
+ if (bKeyUp)
+ {
+ if (pHotKey->uModifiers & MOD_ON_KEYUP)
+ return pHotKey;
+ }
+ else
+ {
+ if (pHotKey->uModifiers & MOD_ON_KEYUP)
+ s_uKeyUpVKey = uVirtualKey;
+ else
+ return pHotKey;
+ }
+ }
+
+ return NULL;
}
-DWORD
-APIENTRY
-NtUserGetAppImeLevel(
- DWORD dwUnknown1)
+// Win: FreeImeHotKeys
+VOID FASTCALL IntFreeImeHotKeys(VOID)
{
- STUB;
- return 0;
+ PIMEHOTKEY pNode, pNext;
+ for (pNode = gpImeHotKeyList; pNode; pNode = pNext)
+ {
+ pNext = pNode->pNext;
+ ExFreePoolWithTag(pNode, USERTAG_IMEHOTKEY);
+ }
+ gpImeHotKeyList = NULL;
+}
+
+// Win: SetImeHotKey
+static BOOL APIENTRY
+IntSetImeHotKey(DWORD dwHotKeyId, UINT uModifiers, UINT uVirtualKey, HKL hKL, DWORD dwAction)
+{
+ PIMEHOTKEY pNode;
+ LANGID LangId;
+
+ switch (dwAction)
+ {
+ case SETIMEHOTKEY_DELETE:
+ pNode = IntGetImeHotKeyById(gpImeHotKeyList, dwHotKeyId); /* Find hotkey by ID */
+ if (!pNode)
+ {
+ ERR("dwHotKeyId: 0x%lX\n", dwHotKeyId);
+ return FALSE;
+ }
+
+ IntDeleteImeHotKey(&gpImeHotKeyList, pNode); /* Delete it */
+ return TRUE;
+
+ case SETIMEHOTKEY_ADD:
+ if (LOWORD(uVirtualKey) == VK_PACKET) /* In case of VK_PACKET */
+ return FALSE;
+
+ LangId = IntGetImeHotKeyLangId(dwHotKeyId);
+ if (LangId == LANGID_KOREAN)
+ return FALSE; /* Korean can't add IME hotkeys */
+
+ /* Find hotkey by key and language */
+ pNode = IntGetImeHotKeyByKeyAndLang(gpImeHotKeyList,
+ (uModifiers & MOD_KEYS),
+ (uModifiers & MOD_LEFT_RIGHT),
+ uVirtualKey, LangId);
+ if (pNode == NULL) /* If not found */
+ pNode = IntGetImeHotKeyById(gpImeHotKeyList, dwHotKeyId); /* Find by ID */
+
+ if (pNode) /* Already exists */
+ {
+ pNode->uModifiers = uModifiers;
+ pNode->uVirtualKey = uVirtualKey;
+ pNode->hKL = hKL;
+ return TRUE;
+ }
+
+ /* Allocate new hotkey */
+ pNode = ExAllocatePoolWithTag(PagedPool, sizeof(IMEHOTKEY), USERTAG_IMEHOTKEY);
+ if (!pNode)
+ return FALSE;
+
+ /* Populate */
+ pNode->pNext = NULL;
+ pNode->dwHotKeyId = dwHotKeyId;
+ pNode->uModifiers = uModifiers;
+ pNode->uVirtualKey = uVirtualKey;
+ pNode->hKL = hKL;
+ IntAddImeHotKey(&gpImeHotKeyList, pNode); /* Add it */
+ return TRUE;
+
+ case SETIMEHOTKEY_INITIALIZE:
+ IntFreeImeHotKeys(); /* Delete all the IME hotkeys */
+ return TRUE;
+
+ default:
+ ERR("0x%lX\n", dwAction);
+ return FALSE;
+ }
+}
+
+BOOL NTAPI
+NtUserGetImeHotKey(DWORD dwHotKeyId, LPUINT lpuModifiers, LPUINT lpuVirtualKey, LPHKL lphKL)
+{
+ PIMEHOTKEY pNode = NULL;
+
+ UserEnterExclusive();
+
+ _SEH2_TRY
+ {
+ ProbeForWrite(lpuModifiers, sizeof(UINT), 1);
+ ProbeForWrite(lpuVirtualKey, sizeof(UINT), 1);
+ if (lphKL)
+ ProbeForWrite(lphKL, sizeof(HKL), 1);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p, %p, %p\n", lpuModifiers, lpuVirtualKey, lphKL);
+ _SEH2_YIELD(goto Quit);
+ }
+ _SEH2_END;
+
+ pNode = IntGetImeHotKeyById(gpImeHotKeyList, dwHotKeyId);
+ if (!pNode)
+ goto Quit;
+
+ _SEH2_TRY
+ {
+ *lpuModifiers = pNode->uModifiers;
+ *lpuVirtualKey = pNode->uVirtualKey;
+ if (lphKL)
+ *lphKL = pNode->hKL;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p, %p, %p, %p\n", pNode, lpuModifiers, lpuVirtualKey, lphKL);
+ pNode = NULL;
+ }
+ _SEH2_END;
+
+Quit:
+ UserLeave();
+ return !!pNode;
+}
+
+BOOL
+NTAPI
+NtUserSetImeHotKey(
+ DWORD dwHotKeyId,
+ UINT uModifiers,
+ UINT uVirtualKey,
+ HKL hKL,
+ DWORD dwAction)
+{
+ BOOL ret;
+ UserEnterExclusive();
+ ret = IntSetImeHotKey(dwHotKeyId, uModifiers, uVirtualKey, hKL, dwAction);
+ UserLeave();
+ return ret;
}
DWORD
-APIENTRY
-NtUserGetImeInfoEx(
- PIMEINFOEX pImeInfoEx,
- DWORD dwUnknown2)
+NTAPI
+NtUserCheckImeHotKey(UINT uVirtualKey, LPARAM lParam)
{
- STUB;
- return 0;
+ PIMEHOTKEY pNode;
+ DWORD ret = INVALID_HOTKEY;
+
+ UserEnterExclusive();
+
+ if (!gpqForeground || !IS_IMM_MODE())
+ goto Quit;
+
+ pNode = IntCheckImeHotKey(gpqForeground, uVirtualKey, lParam);
+ if (pNode)
+ ret = pNode->dwHotKeyId;
+
+Quit:
+ UserLeave();
+ return ret;
+}
+
+// Win: GetTopLevelWindow
+PWND FASTCALL IntGetTopLevelWindow(PWND pwnd)
+{
+ if (!pwnd)
+ return NULL;
+
+ while (pwnd->style & WS_CHILD)
+ pwnd = pwnd->spwndParent;
+
+ return pwnd;
}
+// Win: AssociateInputContext
+HIMC FASTCALL IntAssociateInputContext(PWND pWnd, PIMC pImc)
+{
+ HIMC hOldImc = pWnd->hImc;
+ pWnd->hImc = (pImc ? UserHMGetHandle(pImc) : NULL);
+ return hOldImc;
+}
DWORD
-APIENTRY
-NtUserSetAppImeLevel(
- DWORD dwUnknown1,
- DWORD dwUnknown2)
+NTAPI
+NtUserSetThreadLayoutHandles(HKL hNewKL, HKL hOldKL)
{
- STUB;
+ PTHREADINFO pti;
+ PKL pOldKL, pNewKL;
+
+ UserEnterExclusive();
+
+ pti = GetW32ThreadInfo();
+ pOldKL = pti->KeyboardLayout;
+ if (pOldKL && pOldKL->hkl != hOldKL)
+ goto Quit;
+
+ pNewKL = UserHklToKbl(hNewKL);
+ if (!pNewKL)
+ goto Quit;
+
+ if (IS_IME_HKL(hNewKL) != IS_IME_HKL(hOldKL))
+ pti->hklPrev = hOldKL;
+
+ UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pNewKL);
+ pti->pClientInfo->hKL = pNewKL->hkl;
+
+Quit:
+ UserLeave();
return 0;
}
+// Win: BuildHimcList
+DWORD FASTCALL UserBuildHimcList(PTHREADINFO pti, DWORD dwCount, HIMC *phList)
+{
+ PIMC pIMC;
+ DWORD dwRealCount = 0;
+
+ if (pti)
+ {
+ for (pIMC = pti->spDefaultImc; pIMC; pIMC = pIMC->pImcNext)
+ {
+ if (dwRealCount < dwCount)
+ phList[dwRealCount] = UserHMGetHandle(pIMC);
+
+ ++dwRealCount;
+ }
+ }
+ else
+ {
+ for (pti = gptiCurrent->ppi->ptiList; pti; pti = pti->ptiSibling)
+ {
+ for (pIMC = pti->spDefaultImc; pIMC; pIMC = pIMC->pImcNext)
+ {
+ if (dwRealCount < dwCount)
+ phList[dwRealCount] = UserHMGetHandle(pIMC);
+
+ ++dwRealCount;
+ }
+ }
+ }
+
+ return dwRealCount;
+}
+
+UINT FASTCALL
+IntImmProcessKey(PUSER_MESSAGE_QUEUE MessageQueue, PWND pWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+{
+ UINT uVirtualKey, ret;
+ DWORD dwHotKeyId;
+ PKL pKL;
+ PIMC pIMC;
+ PIMEHOTKEY pImeHotKey;
+ HKL hKL;
+ HWND hWnd;
+
+ ASSERT_REFS_CO(pWnd);
+
+ switch (uMsg)
+ {
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ break;
+
+ default:
+ return 0;
+ }
+
+ pIMC = NULL;
+ hWnd = UserHMGetHandle(pWnd);
+ pKL = pWnd->head.pti->KeyboardLayout;
+ if (!pKL)
+ return 0;
+
+ uVirtualKey = LOBYTE(wParam);
+ pImeHotKey = IntCheckImeHotKey(MessageQueue, uVirtualKey, lParam);
+ if (pImeHotKey)
+ {
+ dwHotKeyId = pImeHotKey->dwHotKeyId;
+ hKL = pImeHotKey->hKL;
+ }
+ else
+ {
+ dwHotKeyId = INVALID_HOTKEY;
+ hKL = NULL;
+ }
+
+ if (IME_HOTKEY_DSWITCH_FIRST <= dwHotKeyId && dwHotKeyId <= IME_HOTKEY_DSWITCH_LAST)
+ {
+ if (pKL->hkl != hKL)
+ {
+ UserPostMessage(hWnd, WM_INPUTLANGCHANGEREQUEST,
+ ((pKL->dwFontSigs & gSystemFS) ? INPUTLANGCHANGE_SYSCHARSET : 0),
+ (LPARAM)hKL);
+ }
+
+ if (IntGetImeCompatFlags(pWnd->head.pti) & 0x800000)
+ return 0;
+
+ return IPHK_HOTKEY;
+ }
+
+ if (!IS_IMM_MODE())
+ return 0;
+
+ if (dwHotKeyId == INVALID_HOTKEY)
+ {
+ if (!pKL->piiex)
+ return 0;
+
+ if (pWnd->hImc)
+ pIMC = UserGetObject(gHandleTable, pWnd->hImc, TYPE_INPUTCONTEXT);
+ if (!pIMC)
+ return 0;
+
+ if ((lParam & (KF_UP << 16)) &&
+ (pKL->piiex->ImeInfo.fdwProperty & IME_PROP_IGNORE_UPKEYS))
+ {
+ return 0;
+ }
+
+ switch (uVirtualKey)
+ {
+ case VK_DBE_CODEINPUT:
+ case VK_DBE_ENTERCONFIGMODE:
+ case VK_DBE_ENTERWORDREGISTERMODE:
+ case VK_DBE_HIRAGANA:
+ case VK_DBE_KATAKANA:
+ case VK_DBE_NOCODEINPUT:
+ case VK_DBE_NOROMAN:
+ case VK_DBE_ROMAN:
+ break;
+
+ default:
+ {
+ if (uMsg == WM_SYSKEYDOWN || uMsg == WM_SYSKEYUP)
+ {
+ if (uVirtualKey != VK_MENU && uVirtualKey != VK_F10)
+ return 0;
+ }
+
+ if (!(pKL->piiex->ImeInfo.fdwProperty & IME_PROP_NEED_ALTKEY))
+ {
+ if (uVirtualKey == VK_MENU || (lParam & 0x20000000))
+ return 0;
+ }
+ break;
+ }
+ }
+ }
+
+ if (LOBYTE(uVirtualKey) == VK_PACKET)
+ uVirtualKey = MAKELONG(wParam, GetW32ThreadInfo()->wchInjected);
+
+ ret = co_IntImmProcessKey(hWnd, pKL->hkl, uVirtualKey, lParam, dwHotKeyId);
+
+ if (IntGetImeCompatFlags(pWnd->head.pti) & 0x800000)
+ ret &= ~IPHK_HOTKEY;
+
+ return ret;
+}
+
+NTSTATUS
+NTAPI
+NtUserBuildHimcList(DWORD dwThreadId, DWORD dwCount, HIMC *phList, LPDWORD pdwCount)
+{
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+ DWORD dwRealCount;
+ PTHREADINFO pti;
+
+ UserEnterExclusive();
+
+ if (!IS_IMM_MODE())
+ {
+ ERR("!IS_IMM_MODE()\n");
+ EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ goto Quit;
+ }
+
+ if (dwThreadId == 0)
+ {
+ pti = gptiCurrent;
+ }
+ else if (dwThreadId == INVALID_THREAD_ID)
+ {
+ pti = NULL;
+ }
+ else
+ {
+ pti = IntTID2PTI(UlongToHandle(dwThreadId));
+ if (!pti || !pti->rpdesk)
+ goto Quit;
+ }
+
+ _SEH2_TRY
+ {
+ ProbeForWrite(phList, dwCount * sizeof(HIMC), 1);
+ ProbeForWrite(pdwCount, sizeof(DWORD), 1);
+ *pdwCount = dwRealCount = UserBuildHimcList(pti, dwCount, phList);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p, %p\n", phList, pdwCount);
+ _SEH2_YIELD(goto Quit);
+ }
+ _SEH2_END;
+
+ if (dwCount < dwRealCount)
+ ret = STATUS_BUFFER_TOO_SMALL;
+ else
+ ret = STATUS_SUCCESS;
+
+Quit:
+ UserLeave();
+ return ret;
+}
+
+// Win: SetConvMode
+static VOID FASTCALL UserSetImeConversionKeyState(PTHREADINFO pti, DWORD dwConversion)
+{
+ HKL hKL;
+ LANGID LangID;
+ LPBYTE KeyState;
+ BOOL bAlphaNumeric, bKatakana, bHiragana, bFullShape, bRoman, bCharCode;
+
+ if (!pti->KeyboardLayout)
+ return;
+
+ hKL = pti->KeyboardLayout->hkl;
+ LangID = LOWORD(hKL);
+ KeyState = pti->MessageQueue->afKeyState;
+
+ switch (PRIMARYLANGID(LangID))
+ {
+ case LANG_JAPANESE:
+ bAlphaNumeric = !(dwConversion & IME_CMODE_NATIVE);
+ bKatakana = !bAlphaNumeric && (dwConversion & IME_CMODE_KATAKANA);
+ bHiragana = !bAlphaNumeric && !(dwConversion & IME_CMODE_KATAKANA);
+ SET_KEY_DOWN(KeyState, VK_DBE_ALPHANUMERIC, bAlphaNumeric);
+ SET_KEY_LOCKED(KeyState, VK_DBE_ALPHANUMERIC, bAlphaNumeric);
+ SET_KEY_DOWN(KeyState, VK_DBE_HIRAGANA, bHiragana);
+ SET_KEY_LOCKED(KeyState, VK_DBE_HIRAGANA, bHiragana);
+ SET_KEY_DOWN(KeyState, VK_DBE_KATAKANA, bKatakana);
+ SET_KEY_LOCKED(KeyState, VK_DBE_KATAKANA, bKatakana);
+
+ bFullShape = (dwConversion & IME_CMODE_FULLSHAPE);
+ SET_KEY_DOWN(KeyState, VK_DBE_DBCSCHAR, bFullShape);
+ SET_KEY_LOCKED(KeyState, VK_DBE_DBCSCHAR, bFullShape);
+ SET_KEY_DOWN(KeyState, VK_DBE_SBCSCHAR, !bFullShape);
+ SET_KEY_LOCKED(KeyState, VK_DBE_SBCSCHAR, !bFullShape);
+
+ bRoman = (dwConversion & IME_CMODE_ROMAN);
+ SET_KEY_DOWN(KeyState, VK_DBE_ROMAN, bRoman);
+ SET_KEY_LOCKED(KeyState, VK_DBE_ROMAN, bRoman);
+ SET_KEY_DOWN(KeyState, VK_DBE_NOROMAN, !bRoman);
+ SET_KEY_LOCKED(KeyState, VK_DBE_NOROMAN, !bRoman);
+
+ bCharCode = (dwConversion & IME_CMODE_CHARCODE);
+ SET_KEY_DOWN(KeyState, VK_DBE_CODEINPUT, bCharCode);
+ SET_KEY_LOCKED(KeyState, VK_DBE_CODEINPUT, bCharCode);
+ SET_KEY_DOWN(KeyState, VK_DBE_NOCODEINPUT, !bCharCode);
+ SET_KEY_LOCKED(KeyState, VK_DBE_NOCODEINPUT, !bCharCode);
+ break;
+
+ case LANG_KOREAN:
+ SET_KEY_LOCKED(KeyState, VK_HANGUL, (dwConversion & IME_CMODE_NATIVE));
+ SET_KEY_LOCKED(KeyState, VK_JUNJA, (dwConversion & IME_CMODE_FULLSHAPE));
+ SET_KEY_LOCKED(KeyState, VK_HANJA, (dwConversion & IME_CMODE_HANJACONVERT));
+ break;
+
+ default:
+ break;
+ }
+}
+
DWORD
-APIENTRY
-NtUserSetImeInfoEx(
- PIMEINFOEX pImeInfoEx)
+NTAPI
+NtUserNotifyIMEStatus(HWND hwnd, BOOL fOpen, DWORD dwConversion)
{
- STUB;
+ PWND pwnd;
+ PTHREADINFO pti;
+ HKL hKL;
+
+ UserEnterExclusive();
+
+ if (!IS_IMM_MODE())
+ {
+ ERR("!IS_IMM_MODE()\n");
+ goto Quit;
+ }
+
+ pwnd = ValidateHwndNoErr(hwnd);
+ if (!pwnd)
+ goto Quit;
+
+ pti = pwnd->head.pti;
+ if (!pti || !gptiForeground)
+ goto Quit;
+ if (pti != gptiForeground && pti->MessageQueue != gptiForeground->MessageQueue)
+ goto Quit;
+ if (ghIMC == pwnd->hImc && gfImeOpen == !!fOpen && gdwImeConversion == dwConversion)
+ goto Quit;
+
+ ghIMC = pwnd->hImc;
+ if (ghIMC)
+ {
+ gfImeOpen = !!fOpen;
+ gdwImeConversion = dwConversion;
+ UserSetImeConversionKeyState(pti, (fOpen ? dwConversion : IME_CMODE_ALPHANUMERIC));
+ }
+
+ if (ISITHOOKED(WH_SHELL))
+ {
+ hKL = (pti->KeyboardLayout ? pti->KeyboardLayout->hkl : NULL);
+ co_HOOK_CallHooks(WH_SHELL, HSHELL_LANGUAGE, (WPARAM)hwnd, (LPARAM)hKL);
+ }
+
+ // TODO:
+
+Quit:
+ UserLeave();
return 0;
}
-DWORD APIENTRY
-NtUserSetImeOwnerWindow(DWORD Unknown0,
- DWORD Unknown1)
+BOOL
+NTAPI
+NtUserDisableThreadIme(
+ DWORD dwThreadID)
{
- STUB
+ PTHREADINFO pti, ptiCurrent;
+ PPROCESSINFO ppi;
+ BOOL ret = FALSE;
+
+ UserEnterExclusive();
+
+ if (!IS_IMM_MODE())
+ {
+ ERR("!IS_IMM_MODE()\n");
+ EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ goto Quit;
+ }
+
+ ptiCurrent = GetW32ThreadInfo();
+
+ if (dwThreadID == INVALID_THREAD_ID)
+ {
+ ppi = ptiCurrent->ppi;
+ ppi->W32PF_flags |= W32PF_DISABLEIME;
+
+Retry:
+ for (pti = ppi->ptiList; pti; pti = pti->ptiSibling)
+ {
+ pti->TIF_flags |= TIF_DISABLEIME;
+
+ if (pti->spwndDefaultIme)
+ {
+ co_UserDestroyWindow(pti->spwndDefaultIme);
+ pti->spwndDefaultIme = NULL;
+ goto Retry; /* The contents of ppi->ptiList may be changed. */
+ }
+ }
+ }
+ else
+ {
+ if (dwThreadID == 0)
+ {
+ pti = ptiCurrent;
+ }
+ else
+ {
+ pti = IntTID2PTI(UlongToHandle(dwThreadID));
+
+ /* The thread needs to reside in the current process. */
+ if (!pti || pti->ppi != ptiCurrent->ppi)
+ goto Quit;
+ }
+
+ pti->TIF_flags |= TIF_DISABLEIME;
- return 0;
+ if (pti->spwndDefaultIme)
+ {
+ co_UserDestroyWindow(pti->spwndDefaultIme);
+ pti->spwndDefaultIme = NULL;
+ }
+ }
+
+ ret = TRUE;
+
+Quit:
+ UserLeave();
+ return ret;
}
+DWORD
+NTAPI
+NtUserGetAppImeLevel(HWND hWnd)
+{
+ DWORD ret = 0;
+ PWND pWnd;
+ PTHREADINFO pti;
+
+ UserEnterShared();
+
+ pWnd = ValidateHwndNoErr(hWnd);
+ if (!pWnd)
+ goto Quit;
+
+ if (!IS_IMM_MODE())
+ {
+ ERR("!IS_IMM_MODE()\n");
+ EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ goto Quit;
+ }
+
+ pti = PsGetCurrentThreadWin32Thread();
+ if (pWnd->head.pti->ppi == pti->ppi)
+ ret = (DWORD)(ULONG_PTR)UserGetProp(pWnd, AtomImeLevel, TRUE);
+
+Quit:
+ UserLeave();
+ return ret;
+}
+
+// Win: GetImeInfoEx
+BOOL FASTCALL
+UserGetImeInfoEx(
+ _Inout_ PWINSTATION_OBJECT pWinSta,
+ _Inout_ PIMEINFOEX pInfoEx,
+ _In_ IMEINFOEXCLASS SearchType)
+{
+ PKL pkl, pklHead;
+
+ if (!pWinSta || !gspklBaseLayout)
+ return FALSE;
+
+ pkl = pklHead = gspklBaseLayout;
+
+ /* Find the matching entry from the list and get info */
+ if (SearchType == ImeInfoExKeyboardLayout)
+ {
+ do
+ {
+ if (pInfoEx->hkl == pkl->hkl)
+ {
+ if (!pkl->piiex)
+ {
+ ERR("!pkl->piiex at %p\n", pkl->hkl);
+ break;
+ }
+
+ *pInfoEx = *pkl->piiex;
+ return TRUE;
+ }
+
+ pkl = pkl->pklNext;
+ } while (pkl != pklHead);
+ }
+ else if (SearchType == ImeInfoExImeFileName)
+ {
+ do
+ {
+ if (pkl->piiex &&
+ _wcsnicmp(pkl->piiex->wszImeFile, pInfoEx->wszImeFile,
+ RTL_NUMBER_OF(pkl->piiex->wszImeFile)) == 0)
+ {
+ *pInfoEx = *pkl->piiex;
+ return TRUE;
+ }
+
+ pkl = pkl->pklNext;
+ } while (pkl != pklHead);
+ }
+ else
+ {
+ ERR("SearchType: %d\n", SearchType);
+ }
+
+ return FALSE;
+}
+
+BOOL
+NTAPI
+NtUserGetImeInfoEx(
+ PIMEINFOEX pImeInfoEx,
+ IMEINFOEXCLASS SearchType)
+{
+ IMEINFOEX ImeInfoEx;
+ BOOL ret = FALSE;
+ PWINSTATION_OBJECT pWinSta;
+
+ UserEnterShared();
+
+ if (!IS_IMM_MODE())
+ {
+ ERR("!IS_IMM_MODE()\n");
+ goto Quit;
+ }
+
+ _SEH2_TRY
+ {
+ ProbeForRead(pImeInfoEx, sizeof(*pImeInfoEx), 1);
+ ImeInfoEx = *pImeInfoEx;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p\n", pImeInfoEx);
+ _SEH2_YIELD(goto Quit);
+ }
+ _SEH2_END;
+
+ pWinSta = IntGetProcessWindowStation(NULL);
+ ret = UserGetImeInfoEx(pWinSta, &ImeInfoEx, SearchType);
+ if (!ret)
+ goto Quit;
+
+ _SEH2_TRY
+ {
+ ProbeForWrite(pImeInfoEx, sizeof(*pImeInfoEx), 1);
+ *pImeInfoEx = ImeInfoEx;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p\n", pImeInfoEx);
+ ret = FALSE;
+ }
+ _SEH2_END;
+
+Quit:
+ UserLeave();
+ return ret;
+}
+
+BOOL
+NTAPI
+NtUserSetAppImeLevel(HWND hWnd, DWORD dwLevel)
+{
+ BOOL ret = FALSE;
+ PWND pWnd;
+ PTHREADINFO pti;
+
+ UserEnterExclusive();
+
+ if (!IS_IMM_MODE())
+ {
+ ERR("!IS_IMM_MODE()\n");
+ EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ goto Quit;
+ }
+
+ pWnd = ValidateHwndNoErr(hWnd);
+ if (!pWnd)
+ goto Quit;
+
+ pti = PsGetCurrentThreadWin32Thread();
+ if (pWnd->head.pti->ppi == pti->ppi)
+ ret = UserSetProp(pWnd, AtomImeLevel, (HANDLE)(ULONG_PTR)dwLevel, TRUE);
+
+Quit:
+ UserLeave();
+ return ret;
+}
+
+// Win: SetImeInfoEx
+BOOL FASTCALL
+UserSetImeInfoEx(
+ _Inout_ PWINSTATION_OBJECT pWinSta,
+ _Inout_ PIMEINFOEX pImeInfoEx)
+{
+ PKL pklHead, pkl;
+
+ if (!pWinSta || !gspklBaseLayout)
+ return FALSE;
+
+ pkl = pklHead = gspklBaseLayout;
+
+ do
+ {
+ if (pkl->hkl != pImeInfoEx->hkl)
+ {
+ pkl = pkl->pklNext;
+ continue;
+ }
+
+ if (!pkl->piiex)
+ {
+ ERR("!pkl->piiex at %p\n", pkl->hkl);
+ return FALSE;
+ }
+
+ if (!pkl->piiex->fLoadFlag)
+ *pkl->piiex = *pImeInfoEx;
+
+ return TRUE;
+ } while (pkl != pklHead);
+
+ return FALSE;
+}
+
+BOOL
+NTAPI
+NtUserSetImeInfoEx(PIMEINFOEX pImeInfoEx)
+{
+ BOOL ret = FALSE;
+ IMEINFOEX ImeInfoEx;
+ PWINSTATION_OBJECT pWinSta;
+
+ UserEnterExclusive();
+
+ if (!IS_IMM_MODE())
+ {
+ ERR("!IS_IMM_MODE()\n");
+ goto Quit;
+ }
+
+ _SEH2_TRY
+ {
+ ProbeForRead(pImeInfoEx, sizeof(*pImeInfoEx), 1);
+ ImeInfoEx = *pImeInfoEx;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p\n", pImeInfoEx);
+ _SEH2_YIELD(goto Quit);
+ }
+ _SEH2_END;
+
+ pWinSta = IntGetProcessWindowStation(NULL);
+ ret = UserSetImeInfoEx(pWinSta, &ImeInfoEx);
+
+Quit:
+ UserLeave();
+ return ret;
+}
+
+// Choose the preferred owner of the IME window.
+// Win: ImeSetFutureOwner
+VOID FASTCALL IntImeSetFutureOwner(PWND pImeWnd, PWND pwndOwner)
+{
+ PWND pwndNode, pwndNextOwner, pwndParent, pwndSibling;
+ PTHREADINFO pti = pImeWnd->head.pti;
+
+ if (!pwndOwner || (pwndOwner->style & WS_CHILD)) // invalid owner
+ return;
+
+ // Get the top-level owner of the same thread
+ for (pwndNode = pwndOwner; ; pwndNode = pwndNextOwner)
+ {
+ pwndNextOwner = pwndNode->spwndOwner;
+ if (!pwndNextOwner || pwndNextOwner->head.pti != pti)
+ break;
+ }
+
+ // Don't choose the IME-like windows and the bottom-most windows unless necessary.
+ if (IS_WND_IMELIKE(pwndNode) ||
+ ((pwndNode->state2 & WNDS2_BOTTOMMOST) && !(pwndOwner->state2 & WNDS2_BOTTOMMOST)))
+ {
+ pwndNode = pwndOwner;
+ }
+
+ pwndParent = pwndNode->spwndParent;
+ if (!pwndParent || pwndOwner != pwndNode)
+ {
+ WndSetOwner(pImeWnd, pwndNode);
+ return;
+ }
+
+ for (pwndSibling = pwndParent->spwndChild; pwndSibling; pwndSibling = pwndSibling->spwndNext)
+ {
+ if (pwndNode->head.pti != pwndSibling->head.pti)
+ continue;
+
+ if (IS_WND_MENU(pwndSibling) || IS_WND_IMELIKE(pwndSibling))
+ continue;
+
+ if (pwndSibling->state2 & WNDS2_INDESTROY)
+ continue;
+
+ if (pwndNode == pwndSibling || (pwndSibling->style & WS_CHILD))
+ continue;
+
+ if (pwndSibling->spwndOwner == NULL ||
+ pwndSibling->head.pti != pwndSibling->spwndOwner->head.pti)
+ {
+ pwndNode = pwndSibling;
+ break;
+ }
+ }
+
+ WndSetOwner(pImeWnd, pwndNode);
+}
+
+// Get the last non-IME-like top-most window on the desktop.
+// Win: GetLastTopMostWindowNoIME
+PWND FASTCALL IntGetLastTopMostWindowNoIME(PWND pImeWnd)
+{
+ PWND pwndNode, pwndOwner, pwndLastTopMost = NULL;
+ BOOL bFound;
+
+ pwndNode = UserGetDesktopWindow();
+ if (!pwndNode || pwndNode->spwndChild == NULL)
+ return NULL;
+
+ for (pwndNode = pwndNode->spwndChild;
+ pwndNode && (pwndNode->ExStyle & WS_EX_TOPMOST);
+ pwndNode = pwndNode->spwndNext)
+ {
+ bFound = FALSE;
+
+ if (IS_WND_IMELIKE(pwndNode)) // An IME-like window
+ {
+ // Search the IME window from owners
+ for (pwndOwner = pwndNode; pwndOwner; pwndOwner = pwndOwner->spwndOwner)
+ {
+ if (pImeWnd == pwndOwner)
+ {
+ bFound = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!bFound)
+ pwndLastTopMost = pwndNode;
+ }
+
+ return pwndLastTopMost;
+}
+
+// Adjust the ordering of the windows around the IME window.
+// Win: ImeSetTopMost
+VOID FASTCALL IntImeSetTopMost(PWND pImeWnd, BOOL bTopMost, PWND pwndInsertBefore)
+{
+ PWND pwndParent, pwndChild, pwndNode, pwndNext, pwndInsertAfter = NULL;
+ PWND pwndInsertAfterSave;
+
+ pwndParent = pImeWnd->spwndParent;
+ if (!pwndParent)
+ return;
+
+ pwndChild = pwndParent->spwndChild;
+
+ if (!bTopMost)
+ {
+ // Calculate pwndInsertAfter
+ pwndInsertAfter = IntGetLastTopMostWindowNoIME(pImeWnd);
+ if (pwndInsertBefore)
+ {
+ for (pwndNode = pwndInsertAfter; pwndNode; pwndNode = pwndNode->spwndNext)
+ {
+ if (pwndNode->spwndNext == pwndInsertBefore)
+ break;
+
+ if (pwndNode == pImeWnd)
+ return;
+ }
+
+ if (!pwndNode)
+ return;
+
+ pwndInsertAfter = pwndNode;
+ }
+
+ // Adjust pwndInsertAfter if the owner is bottom-most
+ if (pImeWnd->spwndOwner->state2 & WNDS2_BOTTOMMOST)
+ {
+ for (pwndNode = pwndInsertAfter; pwndNode; pwndNode = pwndNode->spwndNext)
+ {
+ if (pwndNode == pImeWnd->spwndOwner)
+ break;
+
+ if (!IS_WND_IMELIKE(pwndNode))
+ pwndInsertAfter = pwndNode;
+ }
+ }
+ }
+
+ pwndInsertAfterSave = pwndInsertAfter;
+
+ while (pwndChild)
+ {
+ pwndNext = pwndChild->spwndNext;
+
+ // If pwndChild is a good IME-like window, ...
+ if (IS_WND_IMELIKE(pwndChild) && pwndChild != pwndInsertAfter &&
+ pwndChild->head.pti == pImeWnd->head.pti)
+ {
+ // Find pImeWnd from the owners
+ for (pwndNode = pwndChild; pwndNode; pwndNode = pwndNode->spwndOwner)
+ {
+ if (pwndNode != pImeWnd)
+ continue;
+
+ // Adjust the ordering and the linking
+ IntUnlinkWindow(pwndChild);
+
+ if (bTopMost)
+ pwndChild->ExStyle |= WS_EX_TOPMOST;
+ else
+ pwndChild->ExStyle &= ~WS_EX_TOPMOST;
+
+ if (!pwndInsertAfter)
+ IntLinkHwnd(pwndChild, HWND_TOP);
+ else
+ IntLinkHwnd(pwndChild, UserHMGetHandle(pwndInsertAfter));
+
+ // Update the preferred position
+ pwndInsertAfter = pwndChild;
+ break;
+ }
+ }
+
+ // Get the next child, with ignoring pwndInsertAfterSave
+ pwndChild = pwndNext;
+ if (pwndChild && pwndChild == pwndInsertAfterSave && pwndInsertAfter)
+ pwndChild = pwndInsertAfter->spwndNext;
+ }
+}
+
+// Make the IME window top-most if necessary.
+// Win: ImeCheckTopmost
+VOID FASTCALL IntImeCheckTopmost(PWND pImeWnd)
+{
+ BOOL bTopMost;
+ PWND pwndOwner = pImeWnd->spwndOwner, pwndInsertBefore = NULL;
+
+ if (!pwndOwner)
+ return;
+
+ if (pImeWnd->head.pti != gptiForeground)
+ pwndInsertBefore = pwndOwner;
+
+ bTopMost = !!(pwndOwner->ExStyle & WS_EX_TOPMOST);
+ IntImeSetTopMost(pImeWnd, bTopMost, pwndInsertBefore);
+}
+
+BOOL NTAPI
+NtUserSetImeOwnerWindow(HWND hImeWnd, HWND hwndFocus)
+{
+ BOOL ret = FALSE;
+ PWND pImeWnd, pwndFocus, pwndTopLevel, pwndNode, pwndActive;
+ PTHREADINFO ptiIme;
+
+ UserEnterExclusive();
+
+ if (!IS_IMM_MODE())
+ {
+ ERR("!IS_IMM_MODE()\n");
+ goto Quit;
+ }
+
+ pImeWnd = ValidateHwndNoErr(hImeWnd);
+ if (!pImeWnd || pImeWnd->fnid != FNID_IME)
+ goto Quit;
+
+ pwndFocus = ValidateHwndNoErr(hwndFocus);
+ if (pwndFocus)
+ {
+ if (IS_WND_IMELIKE(pwndFocus))
+ goto Quit;
+
+ pwndTopLevel = IntGetTopLevelWindow(pwndFocus);
+
+ for (pwndNode = pwndTopLevel; pwndNode; pwndNode = pwndNode->spwndOwner)
+ {
+ if (pwndNode->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
+ {
+ pwndTopLevel = NULL;
+ break;
+ }
+ }
+
+ WndSetOwner(pImeWnd, pwndTopLevel);
+ IntImeCheckTopmost(pImeWnd);
+ }
+ else
+ {
+ ptiIme = pImeWnd->head.pti;
+ pwndActive = ptiIme->MessageQueue->spwndActive;
+
+ if (!pwndActive || pwndActive != pImeWnd->spwndOwner)
+ {
+ if (pwndActive && ptiIme == pwndActive->head.pti && !IS_WND_IMELIKE(pwndActive))
+ {
+ WndSetOwner(pImeWnd, pwndActive);
+ }
+ else
+ {
+ IntImeSetFutureOwner(pImeWnd, pImeWnd->spwndOwner);
+ }
+
+ IntImeCheckTopmost(pImeWnd);
+ }
+ }
+
+ ret = TRUE;
+
+Quit:
+ UserLeave();
+ return ret;
+}
+
+PVOID
+AllocInputContextObject(PDESKTOP pDesk,
+ PTHREADINFO pti,
+ SIZE_T Size,
+ PVOID* HandleOwner)
+{
+ PTHRDESKHEAD ObjHead;
+
+ ASSERT(Size > sizeof(*ObjHead));
+ ASSERT(pti != NULL);
+
+ if (!pDesk)
+ pDesk = pti->rpdesk;
+
+ ObjHead = DesktopHeapAlloc(pDesk, Size);
+ if (!ObjHead)
+ return NULL;
+
+ RtlZeroMemory(ObjHead, Size);
+
+ ObjHead->pSelf = ObjHead;
+ ObjHead->rpdesk = pDesk;
+ ObjHead->pti = pti;
+ IntReferenceThreadInfo(pti);
+ *HandleOwner = pti;
+ pti->ppi->UserHandleCount++;
+
+ return ObjHead;
+}
+
+VOID UserFreeInputContext(PVOID Object)
+{
+ PTHRDESKHEAD ObjHead = Object;
+ PDESKTOP pDesk = ObjHead->rpdesk;
+ PIMC pNode, pIMC = Object;
+ PTHREADINFO pti;
+
+ if (!pIMC)
+ return;
+
+ // Remove pIMC from the list except spDefaultImc
+ pti = pIMC->head.pti;
+ for (pNode = pti->spDefaultImc; pNode; pNode = pNode->pImcNext)
+ {
+ if (pNode->pImcNext == pIMC)
+ {
+ pNode->pImcNext = pIMC->pImcNext;
+ break;
+ }
+ }
+
+ DesktopHeapFree(pDesk, Object);
+
+ pti->ppi->UserHandleCount--;
+ IntDereferenceThreadInfo(pti);
+}
+
+BOOLEAN UserDestroyInputContext(PVOID Object)
+{
+ PIMC pIMC = Object;
+ if (!pIMC)
+ return TRUE;
+
+ UserMarkObjectDestroy(pIMC);
+ UserDeleteObject(UserHMGetHandle(pIMC), TYPE_INPUTCONTEXT);
+ return TRUE;
+}
+
+// Win: DestroyInputContext
+BOOL IntDestroyInputContext(PIMC pIMC)
+{
+ HIMC hIMC = UserHMGetHandle(pIMC);
+ PTHREADINFO pti = pIMC->head.pti;
+ PWND pwndChild;
+ PWINDOWLIST pwl;
+ HWND *phwnd;
+ PWND pWnd;
+
+ if (pti != gptiCurrent)
+ {
+ EngSetLastError(ERROR_ACCESS_DENIED);
+ return FALSE;
+ }
+
+ if (pIMC == pti->spDefaultImc)
+ {
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ pwndChild = pti->rpdesk->pDeskInfo->spwnd->spwndChild;
+ pwl = IntBuildHwndList(pwndChild, IACE_LIST | IACE_CHILDREN, pti);
+ if (pwl)
+ {
+ for (phwnd = pwl->ahwnd; *phwnd != HWND_TERMINATOR; ++phwnd)
+ {
+ pWnd = UserGetObjectNoErr(gHandleTable, *phwnd, TYPE_WINDOW);
+ if (pWnd && pWnd->hImc == hIMC)
+ IntAssociateInputContext(pWnd, pti->spDefaultImc);
+ }
+
+ IntFreeHwndList(pwl);
+ }
+
+ UserDeleteObject(hIMC, TYPE_INPUTCONTEXT);
+ return TRUE;
+}
+
+BOOL NTAPI NtUserDestroyInputContext(HIMC hIMC)
+{
+ BOOL ret = FALSE;
+ PIMC pIMC;
+
+ UserEnterExclusive();
+
+ if (!IS_IMM_MODE())
+ {
+ EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ goto Quit;
+ }
+
+ pIMC = UserGetObjectNoErr(gHandleTable, hIMC, TYPE_INPUTCONTEXT);
+ if (pIMC)
+ ret = IntDestroyInputContext(pIMC);
+
+Quit:
+ UserLeave();
+ return ret;
+}
+
+// Win: CreateInputContext
+PIMC FASTCALL UserCreateInputContext(ULONG_PTR dwClientImcData)
+{
+ PIMC pIMC;
+ PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
+ PDESKTOP pdesk = pti->rpdesk;
+
+ if (!IS_IMM_MODE() || (pti->TIF_flags & TIF_DISABLEIME)) // Disabled?
+ {
+ ERR("IME is disabled\n");
+ return NULL;
+ }
+
+ if (!pdesk) // No desktop?
+ return NULL;
+
+ // pti->spDefaultImc should be already set if non-first time.
+ if (dwClientImcData && !pti->spDefaultImc)
+ return NULL;
+
+ // Create an input context user object.
+ pIMC = UserCreateObject(gHandleTable, pdesk, pti, NULL, TYPE_INPUTCONTEXT, sizeof(IMC));
+ if (!pIMC)
+ return NULL;
+
+ // Release the extra reference (UserCreateObject added 2 references).
+ UserDereferenceObject(pIMC);
+ ASSERT(pIMC->head.cLockObj == 1);
+
+ if (dwClientImcData) // Non-first time.
+ {
+ // Insert pIMC to the second position (non-default) of the list.
+ pIMC->pImcNext = pti->spDefaultImc->pImcNext;
+ pti->spDefaultImc->pImcNext = pIMC;
+ }
+ else // First time. It's the default IMC.
+ {
+ // Add the first one (default) to the list.
+ UserAssignmentLock((PVOID*)&pti->spDefaultImc, pIMC);
+ pIMC->pImcNext = NULL;
+ ASSERT(pIMC->head.cLockObj == 2); // UserAssignmentUnlock'ed at ExitThreadCallback
+ }
+
+ pIMC->dwClientImcData = dwClientImcData; // Set it.
+ return pIMC;
+}
+
+HIMC
+NTAPI
+NtUserCreateInputContext(ULONG_PTR dwClientImcData)
+{
+ PIMC pIMC;
+ HIMC ret = NULL;
+
+ UserEnterExclusive();
+
+ if (!IS_IMM_MODE())
+ {
+ ERR("!IS_IMM_MODE()\n");
+ EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ goto Quit;
+ }
+
+ if (!dwClientImcData)
+ {
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ goto Quit;
+ }
+
+ pIMC = UserCreateInputContext(dwClientImcData);
+ if (pIMC)
+ ret = UserHMGetHandle(pIMC);
+
+Quit:
+ UserLeave();
+ return ret;
+}
+
+// Win: AssociateInputContextEx
+DWORD FASTCALL IntAssociateInputContextEx(PWND pWnd, PIMC pIMC, DWORD dwFlags)
+{
+ DWORD ret = 0;
+ PWINDOWLIST pwl;
+ BOOL bIgnoreNullImc = (dwFlags & IACE_IGNORENOCONTEXT);
+ PTHREADINFO pti = pWnd->head.pti;
+ PWND pwndTarget, pwndFocus = pti->MessageQueue->spwndFocus;
+ HWND *phwnd;
+ HIMC hIMC;
+
+ if (dwFlags & IACE_DEFAULT)
+ {
+ pIMC = pti->spDefaultImc;
+ }
+ else
+ {
+ if (pIMC && pti != pIMC->head.pti)
+ return 2;
+ }
+
+ if (pWnd->head.pti->ppi != GetW32ThreadInfo()->ppi ||
+ (pIMC && pIMC->head.rpdesk != pWnd->head.rpdesk))
+ {
+ return 2;
+ }
+
+ if ((dwFlags & IACE_CHILDREN) && pWnd->spwndChild)
+ {
+ pwl = IntBuildHwndList(pWnd->spwndChild, IACE_CHILDREN | IACE_LIST, pti);
+ if (pwl)
+ {
+ for (phwnd = pwl->ahwnd; *phwnd != HWND_TERMINATOR; ++phwnd)
+ {
+ pwndTarget = ValidateHwndNoErr(*phwnd);
+ if (!pwndTarget)
+ continue;
+
+ hIMC = (pIMC ? UserHMGetHandle(pIMC) : NULL);
+ if (pwndTarget->hImc == hIMC || (bIgnoreNullImc && !pwndTarget->hImc))
+ continue;
+
+ IntAssociateInputContext(pwndTarget, pIMC);
+ if (pwndTarget == pwndFocus)
+ ret = 1;
+ }
+
+ IntFreeHwndList(pwl);
+ }
+ }
+
+ if (!bIgnoreNullImc || pWnd->hImc)
+ {
+ hIMC = (pIMC ? UserHMGetHandle(pIMC) : NULL);
+ if (pWnd->hImc != hIMC)
+ {
+ IntAssociateInputContext(pWnd, pIMC);
+ if (pWnd == pwndFocus)
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+DWORD
+NTAPI
+NtUserAssociateInputContext(HWND hWnd, HIMC hIMC, DWORD dwFlags)
+{
+ DWORD ret = 2;
+ PWND pWnd;
+ PIMC pIMC;
+
+ UserEnterExclusive();
+
+ if (!IS_IMM_MODE())
+ {
+ ERR("!IS_IMM_MODE()\n");
+ goto Quit;
+ }
+
+ pWnd = ValidateHwndNoErr(hWnd);
+ if (!pWnd)
+ goto Quit;
+
+ pIMC = (hIMC ? UserGetObjectNoErr(gHandleTable, hIMC, TYPE_INPUTCONTEXT) : NULL);
+ ret = IntAssociateInputContextEx(pWnd, pIMC, dwFlags);
+
+Quit:
+ UserLeave();
+ return ret;
+}
+
+// Win: UpdateInputContext
+BOOL FASTCALL UserUpdateInputContext(PIMC pIMC, DWORD dwType, DWORD_PTR dwValue)
+{
+ PTHREADINFO pti = GetW32ThreadInfo();
+ PTHREADINFO ptiIMC = pIMC->head.pti;
+
+ if (pti->ppi != ptiIMC->ppi) // Different process?
+ return FALSE;
+
+ switch (dwType)
+ {
+ case UIC_CLIENTIMCDATA:
+ if (pIMC->dwClientImcData)
+ return FALSE; // Already set
+
+ pIMC->dwClientImcData = dwValue;
+ break;
+
+ case UIC_IMEWINDOW:
+ if (!ValidateHwndNoErr((HWND)dwValue))
+ return FALSE; // Invalid HWND
+
+ pIMC->hImeWnd = (HWND)dwValue;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL
+NTAPI
+NtUserUpdateInputContext(
+ HIMC hIMC,
+ DWORD dwType,
+ DWORD_PTR dwValue)
+{
+ PIMC pIMC;
+ BOOL ret = FALSE;
+
+ UserEnterExclusive();
+
+ if (!IS_IMM_MODE())
+ {
+ ERR("!IS_IMM_MODE()\n");
+ goto Quit;
+ }
+
+ pIMC = UserGetObject(gHandleTable, hIMC, TYPE_INPUTCONTEXT);
+ if (!pIMC)
+ goto Quit;
+
+ ret = UserUpdateInputContext(pIMC, dwType, dwValue);
+
+Quit:
+ UserLeave();
+ return ret;
+}
+
+DWORD_PTR
+NTAPI
+NtUserQueryInputContext(HIMC hIMC, DWORD dwType)
+{
+ PIMC pIMC;
+ PTHREADINFO ptiIMC;
+ DWORD_PTR ret = 0;
+
+ UserEnterExclusive();
+
+ if (!IS_IMM_MODE())
+ {
+ ERR("!IS_IMM_MODE()\n");
+ goto Quit;
+ }
+
+ pIMC = UserGetObject(gHandleTable, hIMC, TYPE_INPUTCONTEXT);
+ if (!pIMC)
+ goto Quit;
+
+ ptiIMC = pIMC->head.pti;
+
+ switch (dwType)
+ {
+ case QIC_INPUTPROCESSID:
+ ret = (DWORD_PTR)PsGetThreadProcessId(ptiIMC->pEThread);
+ break;
+
+ case QIC_INPUTTHREADID:
+ ret = (DWORD_PTR)PsGetThreadId(ptiIMC->pEThread);
+ break;
+
+ case QIC_DEFAULTWINDOWIME:
+ if (ptiIMC->spwndDefaultIme)
+ ret = (DWORD_PTR)UserHMGetHandle(ptiIMC->spwndDefaultIme);
+ break;
+
+ case QIC_DEFAULTIMC:
+ if (ptiIMC->spDefaultImc)
+ ret = (DWORD_PTR)UserHMGetHandle(ptiIMC->spDefaultImc);
+ break;
+ }
+
+Quit:
+ UserLeave();
+ return ret;
+}
+
+// Searchs a non-IME-related window of the same thread of pwndTarget,
+// other than pwndTarget, around pwndParent. Returns TRUE if found.
+//
+// Win: IsChildSameThread
+BOOL IntFindNonImeRelatedWndOfSameThread(PWND pwndParent, PWND pwndTarget)
+{
+ PWND pwnd, pwndOwner, pwndNode;
+ PTHREADINFO ptiTarget = pwndTarget->head.pti;
+
+ // For all the children of pwndParent, ...
+ for (pwnd = pwndParent->spwndChild; pwnd; pwnd = pwnd->spwndNext)
+ {
+ if (pwnd == pwndTarget || pwnd->head.pti != ptiTarget || IS_WND_MENU(pwnd))
+ continue;
+
+ if (!IS_WND_CHILD(pwnd))
+ {
+ // Check if any IME-like owner.
+ BOOL bFound1 = FALSE;
+ for (pwndOwner = pwnd; pwndOwner; pwndOwner = pwndOwner->spwndOwner)
+ {
+ if (IS_WND_IMELIKE(pwndOwner))
+ {
+ bFound1 = TRUE;
+ break;
+ }
+ }
+ if (bFound1)
+ continue; // Skip if any IME-like owner.
+ }
+
+ pwndNode = pwnd;
+
+ if (IS_WND_CHILD(pwndNode))
+ {
+ // Check if any same-thread IME-like ancestor.
+ BOOL bFound2 = FALSE;
+ for (; IS_WND_CHILD(pwndNode); pwndNode = pwndNode->spwndParent)
+ {
+ if (pwndNode->head.pti != ptiTarget)
+ break;
+
+ if (IS_WND_IMELIKE(pwndNode))
+ {
+ bFound2 = TRUE;
+ break;
+ }
+ }
+ if (bFound2)
+ continue;
+ // Now, pwndNode is non-child or non-same-thread window.
+ }
+
+ if (!IS_WND_CHILD(pwndNode)) // pwndNode is non-child
+ {
+ // Check if any same-thread IME-like owner.
+ BOOL bFound3 = FALSE;
+ for (; pwndNode; pwndNode = pwndNode->spwndOwner)
+ {
+ if (pwndNode->head.pti != ptiTarget)
+ break;
+
+ if (IS_WND_IMELIKE(pwndNode))
+ {
+ bFound3 = TRUE;
+ break;
+ }
+ }
+ if (bFound3)
+ continue;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// Determines whether the target window needs the IME window.
+// Win: WantImeWindow(pwndParent, pwndTarget)
+BOOL FASTCALL IntWantImeWindow(PWND pwndTarget)
+{
+ PDESKTOP rpdesk;
+ PWINSTATION_OBJECT rpwinstaParent;
+ PWND pwndNode, pwndParent = pwndTarget->spwndParent;
+
+ if (gptiCurrent->TIF_flags & TIF_DISABLEIME)
+ return FALSE;
+
+ if (IS_WND_IMELIKE(pwndTarget))
+ return FALSE;
+
+ if (pwndTarget->fnid == FNID_DESKTOP || pwndTarget->fnid == FNID_MESSAGEWND)
+ return FALSE;
+
+ if (pwndTarget->state & WNDS_SERVERSIDEWINDOWPROC)
+ return FALSE;
+
+ rpdesk = pwndTarget->head.rpdesk;
+ if (!rpdesk)
+ return FALSE;
+
+ rpwinstaParent = rpdesk->rpwinstaParent;
+ if (!rpwinstaParent || (rpwinstaParent->Flags & WSS_NOIO))
+ return FALSE;
+
+ for (pwndNode = pwndParent; pwndNode; pwndNode = pwndNode->spwndParent)
+ {
+ if (rpdesk != pwndNode->head.rpdesk)
+ break;
+
+ if (pwndNode == rpdesk->spwndMessage)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// Create the default IME window for the target window.
+// Win: xxxCreateDefaultImeWindow(pwndTarget, ATOM, hInst)
+PWND FASTCALL co_IntCreateDefaultImeWindow(PWND pwndTarget, HINSTANCE hInst)
+{
+ LARGE_UNICODE_STRING WindowName;
+ UNICODE_STRING ClassName;
+ PWND pImeWnd;
+ PIMEUI pimeui;
+ CREATESTRUCTW Cs;
+ USER_REFERENCE_ENTRY Ref;
+ PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
+ HANDLE pid = PsGetThreadProcessId(pti->pEThread);
+
+ if (!(pti->spDefaultImc) && pid == gpidLogon)
+ UserCreateInputContext(0);
+
+ if (!(pti->spDefaultImc) || IS_WND_IMELIKE(pwndTarget) || !(pti->rpdesk->pheapDesktop))
+ return NULL;
+
+ if (IS_WND_CHILD(pwndTarget) && !(pwndTarget->style & WS_VISIBLE) &&
+ pwndTarget->spwndParent->head.pti->ppi != pti->ppi)
+ {
+ return NULL;
+ }
+
+ RtlInitLargeUnicodeString(&WindowName, L"Default IME", 0);
+
+ ClassName.Buffer = (PWCH)(ULONG_PTR)gpsi->atomSysClass[ICLS_IME];
+ ClassName.Length = 0;
+ ClassName.MaximumLength = 0;
+
+ UserRefObjectCo(pwndTarget, &Ref);
+
+ RtlZeroMemory(&Cs, sizeof(Cs));
+ Cs.style = WS_POPUP | WS_DISABLED;
+ Cs.hInstance = hInst;
+ Cs.hwndParent = UserHMGetHandle(pwndTarget);
+ Cs.lpszName = WindowName.Buffer;
+ Cs.lpszClass = ClassName.Buffer;
+
+ // NOTE: LARGE_UNICODE_STRING is compatible to LARGE_STRING.
+ pImeWnd = co_UserCreateWindowEx(&Cs, &ClassName, (PLARGE_STRING)&WindowName, NULL, WINVER);
+ if (pImeWnd)
+ {
+ pimeui = ((PIMEWND)pImeWnd)->pimeui;
+ _SEH2_TRY
+ {
+ ProbeForWrite(pimeui, sizeof(IMEUI), 1);
+ pimeui->fDefault = TRUE;
+ if (IS_WND_CHILD(pwndTarget) && pwndTarget->spwndParent->head.pti != pti)
+ pimeui->fChildThreadDef = TRUE;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p\n", pimeui);
+ }
+ _SEH2_END;
+ }
+
+ UserDerefObjectCo(pwndTarget);
+ return pImeWnd;
+}
+
+// Determines whether the system can destroy the default IME window for the target child window.
+// Win: ImeCanDestroyDefIMEforChild
+BOOL FASTCALL IntImeCanDestroyDefIMEforChild(PWND pImeWnd, PWND pwndTarget)
+{
+ PWND pwndNode;
+ PIMEUI pimeui;
+ IMEUI SafeImeUI;
+
+ pimeui = ((PIMEWND)pImeWnd)->pimeui;
+ if (!pimeui || (LONG_PTR)pimeui == (LONG_PTR)-1)
+ return FALSE;
+
+ // Check IMEUI.fChildThreadDef
+ _SEH2_TRY
+ {
+ ProbeForRead(pimeui, sizeof(IMEUI), 1);
+ SafeImeUI = *pimeui;
+ if (!SafeImeUI.fChildThreadDef)
+ return FALSE;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p\n", pimeui);
+ }
+ _SEH2_END;
+
+ // The parent of pwndTarget is NULL or of the same thread of pwndTarget?
+ if (pwndTarget->spwndParent == NULL ||
+ pwndTarget->head.pti == pwndTarget->spwndParent->head.pti)
+ {
+ return FALSE;
+ }
+
+ for (pwndNode = pwndTarget; pwndNode; pwndNode = pwndNode->spwndParent)
+ {
+ if (pwndNode == pwndNode->head.rpdesk->pDeskInfo->spwnd)
+ break;
+
+ if (IntFindNonImeRelatedWndOfSameThread(pwndNode->spwndParent, pwndTarget))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// Determines whether the system can destroy the default IME window for the non-child target window.
+// Win: ImeCanDestroyDefIME
+BOOL FASTCALL IntImeCanDestroyDefIME(PWND pImeWnd, PWND pwndTarget)
+{
+ PWND pwndNode;
+ PIMEUI pimeui;
+ IMEUI SafeImeUI;
+
+ pimeui = ((PIMEWND)pImeWnd)->pimeui;
+ if (!pimeui || (LONG_PTR)pimeui == (LONG_PTR)-1)
+ return FALSE;
+
+ // Check IMEUI.fDestroy
+ _SEH2_TRY
+ {
+ ProbeForRead(pimeui, sizeof(IMEUI), 1);
+ SafeImeUI = *pimeui;
+ if (SafeImeUI.fDestroy)
+ return FALSE;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p\n", pimeui);
+ }
+ _SEH2_END;
+
+ // Any ancestor of pImeWnd is pwndTarget?
+ if (pImeWnd->spwndOwner)
+ {
+ for (pwndNode = pImeWnd->spwndOwner; pwndNode; pwndNode = pwndNode->spwndOwner)
+ {
+ if (pwndNode == pwndTarget)
+ break;
+ }
+
+ if (!pwndNode)
+ return FALSE;
+ }
+
+ // Any ancestor of pwndTarget is IME-like?
+ for (pwndNode = pwndTarget; pwndNode; pwndNode = pwndNode->spwndOwner)
+ {
+ if (IS_WND_IMELIKE(pwndNode))
+ return FALSE;
+ }
+
+ // Adjust the ordering and top-mode status
+ IntImeSetFutureOwner(pImeWnd, pwndTarget);
+ for (pwndNode = pImeWnd->spwndOwner; pwndNode; pwndNode = pwndNode->spwndNext)
+ {
+ if (pwndNode == pImeWnd)
+ break;
+ }
+ if (pwndNode == pImeWnd)
+ IntImeCheckTopmost(pImeWnd);
+
+ // Is the owner of pImeWnd NULL or pwndTarget?
+ if (pImeWnd->spwndOwner && pwndTarget != pImeWnd->spwndOwner)
+ return FALSE;
+
+ WndSetOwner(pImeWnd, NULL);
+ return TRUE;
+}
+
+// Update IMEUI.fShowStatus flags and Send the WM_IME_NOTIFY messages.
+// Win: xxxCheckImeShowStatus
+BOOL FASTCALL IntCheckImeShowStatus(PWND pwndIme, PTHREADINFO pti)
+{
+ BOOL ret = FALSE, bDifferent;
+ PWINDOWLIST pwl;
+ HWND *phwnd;
+ PWND pwndNode, pwndIMC;
+ PTHREADINFO ptiCurrent = GetW32ThreadInfo();
+ PIMEUI pimeui;
+ IMEUI SafeImeUI;
+
+ if (pwndIme->state2 & WNDS2_INDESTROY)
+ return FALSE;
+
+ // Build a window list
+ pwl = IntBuildHwndList(pwndIme->spwndParent->spwndChild, IACE_LIST, NULL);
+ if (!pwl)
+ return FALSE;
+
+ ret = TRUE;
+ for (phwnd = pwl->ahwnd; *phwnd != HWND_TERMINATOR; ++phwnd)
+ {
+ pwndNode = ValidateHwndNoErr(*phwnd);
+
+ if (!pwndNode || pwndIme == pwndNode)
+ continue;
+
+ if (pwndNode->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME] ||
+ (pwndNode->state2 & WNDS2_INDESTROY))
+ {
+ continue;
+ }
+
+ pimeui = ((PIMEWND)pwndNode)->pimeui;
+ if (!pimeui || pimeui == (PIMEUI)-1)
+ continue;
+
+ if (pti && pti != pwndNode->head.pti)
+ continue;
+
+ // Attach to the process if necessary
+ bDifferent = FALSE;
+ if (pwndNode->head.pti->ppi != ptiCurrent->ppi)
+ {
+ KeAttachProcess(&(pwndNode->head.pti->ppi->peProcess->Pcb));
+ bDifferent = TRUE;
+ }
+
+ // Get pwndIMC and update IMEUI.fShowStatus flag
+ _SEH2_TRY
+ {
+ ProbeForWrite(pimeui, sizeof(IMEUI), 1);
+ SafeImeUI = *pimeui;
+ if (SafeImeUI.fShowStatus)
+ {
+ pwndIMC = ValidateHwndNoErr(pimeui->hwndIMC);
+ if (pwndIMC)
+ pimeui->fShowStatus = FALSE;
+ }
+ else
+ {
+ pwndIMC = NULL;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p\n", pimeui);
+ pwndIMC = NULL;
+ }
+ _SEH2_END;
+
+ // Detach from the process if necessary
+ if (bDifferent)
+ KeDetachProcess();
+
+ // Send the WM_IME_NOTIFY message
+ if (pwndIMC && pwndIMC->head.pti && !(pwndIMC->head.pti->TIF_flags & TIF_INCLEANUP))
+ {
+ HWND hImeWnd;
+ USER_REFERENCE_ENTRY Ref;
+
+ UserRefObjectCo(pwndIMC, &Ref);
+
+ hImeWnd = UserHMGetHandle(pwndIMC);
+ co_IntSendMessage(hImeWnd, WM_IME_NOTIFY, IMN_CLOSESTATUSWINDOW, 0);
+
+ UserDerefObjectCo(pwndIMC);
+ }
+ }
+
+ // Free the window list
+ IntFreeHwndList(pwl);
+ return ret;
+}
+
+// Send a UI message.
+LRESULT FASTCALL
+IntSendMessageToUI(PTHREADINFO ptiIME, PIMEUI pimeui, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ PWND pwndUI;
+ LRESULT ret = 0;
+ IMEUI SafeImeUI;
+ BOOL bDifferent = FALSE;
+ USER_REFERENCE_ENTRY Ref;
+
+ // Attach to the process if necessary
+ if (ptiIME != GetW32ThreadInfo())
+ {
+ bDifferent = TRUE;
+ KeAttachProcess(&(ptiIME->ppi->peProcess->Pcb));
+ }
+
+ // Get the pwndUI
+ _SEH2_TRY
+ {
+ ProbeForRead(pimeui, sizeof(IMEUI), 1);
+ SafeImeUI = *pimeui;
+ pwndUI = ValidateHwndNoErr(SafeImeUI.hwndUI);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p\n", pimeui);
+ pwndUI = NULL;
+ }
+ _SEH2_END;
+
+ if (!pwndUI)
+ goto Quit;
+
+ // Increment the recursion count of the IME procedure.
+ // See also ImeWndProc_common of user32.
+ _SEH2_TRY
+ {
+ ProbeForWrite(&pimeui->nCntInIMEProc, sizeof(LONG), 1);
+ InterlockedIncrement(&pimeui->nCntInIMEProc);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p\n", pimeui);
+ _SEH2_YIELD(goto Quit);
+ }
+ _SEH2_END;
+
+ // Detach from the process if necessary
+ if (bDifferent)
+ KeDetachProcess();
+
+ UserRefObjectCo(pwndUI, &Ref);
+ ret = co_IntSendMessage(UserHMGetHandle(pwndUI), uMsg, wParam, lParam);
+ UserDerefObjectCo(pwndUI);
+
+ // Attach to the process if necessary
+ if (bDifferent)
+ KeAttachProcess(&(ptiIME->ppi->peProcess->Pcb));
+
+ // Decrement the recursion count of the IME procedure
+ _SEH2_TRY
+ {
+ ProbeForWrite(&pimeui->nCntInIMEProc, sizeof(LONG), 1);
+ InterlockedDecrement(&pimeui->nCntInIMEProc);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p\n", pimeui);
+ _SEH2_YIELD(goto Quit);
+ }
+ _SEH2_END;
+
+Quit:
+ // Detach from the process if necessary
+ if (bDifferent)
+ KeDetachProcess();
+
+ return ret;
+}
+
+// Send the open status notification.
+// Win: xxxSendOpenStatusNotify
+VOID FASTCALL
+IntSendOpenStatusNotify(PTHREADINFO ptiIME, PIMEUI pimeui, PWND pWnd, BOOL bOpen)
+{
+ WPARAM wParam = (bOpen ? IMN_OPENSTATUSWINDOW : IMN_CLOSESTATUSWINDOW);
+ PTHREADINFO ptiWnd = pWnd->head.pti;
+ USER_REFERENCE_ENTRY Ref;
+
+ if (ptiWnd->dwExpWinVer >= WINVER_WINNT4 && pWnd->hImc)
+ {
+ UserRefObjectCo(pWnd, &Ref);
+ co_IntSendMessage(UserHMGetHandle(pWnd), WM_IME_NOTIFY, wParam, 0);
+ UserDerefObjectCo(pWnd);
+ }
+ else
+ {
+ IntSendMessageToUI(ptiIME, pimeui, WM_IME_NOTIFY, wParam, 0);
+ }
+}
+
+// Update the IME status and send a notification.
+VOID FASTCALL IntNotifyImeShowStatus(PWND pImeWnd)
+{
+ PIMEUI pimeui;
+ PWND pWnd;
+ PTHREADINFO pti, ptiIME;
+ BOOL bShow, bSendNotify = FALSE;
+ IMEUI SafeImeUI;
+
+ if (!IS_IMM_MODE() || (pImeWnd->state2 & WNDS2_INDESTROY))
+ return;
+
+ pti = PsGetCurrentThreadWin32Thread();
+ ptiIME = pImeWnd->head.pti;
+
+ // Attach to the process if necessary
+ if (pti != ptiIME)
+ KeAttachProcess(&(ptiIME->ppi->peProcess->Pcb));
+
+ // Get an IMEUI and check whether hwndIMC is valid and update fShowStatus
+ _SEH2_TRY
+ {
+ ProbeForWrite(pImeWnd, sizeof(IMEWND), 1);
+ pimeui = ((PIMEWND)pImeWnd)->pimeui;
+ SafeImeUI = *pimeui;
+
+ bShow = (gfIMEShowStatus == TRUE) && SafeImeUI.fCtrlShowStatus;
+
+ pWnd = ValidateHwndNoErr(SafeImeUI.hwndIMC);
+ if (!pWnd)
+ pWnd = ptiIME->MessageQueue->spwndFocus;
+
+ if (pWnd)
+ {
+ bSendNotify = TRUE;
+ pimeui->fShowStatus = bShow;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ ERR("%p, %p\n", pImeWnd, pimeui);
+
+ if (pti != ptiIME)
+ KeDetachProcess();
+
+ _SEH2_YIELD(return);
+ }
+ _SEH2_END;
+
+ // Detach from the process if necessary
+ if (pti != ptiIME)
+ KeDetachProcess();
+
+ if (bSendNotify)
+ IntSendOpenStatusNotify(ptiIME, &SafeImeUI, pWnd, bShow);
+
+ if (!(pImeWnd->state2 & WNDS2_INDESTROY))
+ IntCheckImeShowStatus(pImeWnd, NULL);
+}
+
+// Win: xxxBroadcastImeShowStatusChange
+BOOL FASTCALL IntBroadcastImeShowStatusChange(PWND pImeWnd, BOOL bShow)
+{
+ if (gfIMEShowStatus == bShow || !IS_IMM_MODE())
+ return TRUE;
+
+ gfIMEShowStatus = bShow;
+ IntNotifyImeShowStatus(pImeWnd);
+ return TRUE;
+}
+
+/* Win: xxxCheckImeShowStatusInThread */
+VOID FASTCALL IntCheckImeShowStatusInThread(PWND pImeWnd)
+{
+ if (IS_IMM_MODE() && !(pImeWnd->state2 & WNDS2_INDESTROY))
+ IntCheckImeShowStatus(pImeWnd, pImeWnd->head.pti);
+}
/* EOF */