/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Win32k subsystem * PURPOSE: Input Method Editor and Input Method Manager support * FILE: win32ss/user/ntuser/ime.c * PROGRAMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */ #include #include 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) #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 { struct tagIMEHOTKEY *pNext; DWORD dwHotKeyId; UINT uVirtualKey; UINT uModifiers; HKL hKL; } IMEHOTKEY, *PIMEHOTKEY; 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; return 0; } // Win: GetActiveHKL HKL FASTCALL IntGetActiveKeyboardLayout(VOID) { PTHREADINFO pti; 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) { #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 }; if (IME_CHOTKEY <= dwHotKeyId && dwHotKeyId < IME_XHOTKEY) return s_array[(dwHotKeyId & 0xF0) >> 4]; return LANGID_NEUTRAL; } // Win: AddImeHotKey static VOID FASTCALL IntAddImeHotKey(PIMEHOTKEY *ppList, PIMEHOTKEY pHotKey) { PIMEHOTKEY pNode; if (!*ppList) { *ppList = pHotKey; return; } 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; } // Win: FindImeHotKeyByKeyWithLang static PIMEHOTKEY APIENTRY IntGetImeHotKeyByKeyAndLang(PIMEHOTKEY pList, UINT uModKeys, UINT uLeftRight, UINT uVirtualKey, LANGID TargetLangId) { PIMEHOTKEY pNode; LANGID LangID; UINT uModifiers; 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; } // Win: DeleteImeHotKey static VOID FASTCALL IntDeleteImeHotKey(PIMEHOTKEY *ppList, PIMEHOTKEY pHotKey) { 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; 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) { 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; } // Win: FreeImeHotKeys VOID FASTCALL IntFreeImeHotKeys(VOID) { 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 NTAPI NtUserCheckImeHotKey(UINT uVirtualKey, LPARAM lParam) { 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 NTAPI NtUserSetThreadLayoutHandles(HKL hNewKL, HKL hOldKL) { 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 NTAPI NtUserNotifyIMEStatus(HWND hwnd, BOOL fOpen, DWORD dwConversion) { 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; } BOOL NTAPI NtUserDisableThreadIme( DWORD dwThreadID) { 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; 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 */