From 242e0b43036ce5acea66bc5d8be8bd37296c4130 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Sun, 8 May 2022 20:16:17 +0900 Subject: [PATCH] [NTUSER][USER32] Implement IME status (#4472) - Add IntCheckImeShowStatus, IntSendMessageToUI, IntSendOpenStatusNotify, IntNotifyImeShowStatus, and xxxBroadcastImeShowStatusChange helper functions. - Renaming: s/X_ROUTINE_IMESHOWSTATUSCHANGE/TWOPARAM_ROUTINE_IMESHOWSTATUSCHANGE/ - Implement NtUserCallNoParam.NOPARAM_ROUTINE_GETIMESHOWSTATUS. - Implement NtUserCallHwndParamLock.TWOPARAM_ROUTINE_IMESHOWSTATUSCHANGE. - Fix hung in User32GetTopLevelWindow and rename it as IntGetTopLevelWindow. CORE-11700 --- win32ss/include/ntuser.h | 2 +- win32ss/user/ntuser/ime.c | 265 +++++++++++++++++++++++++++++++ win32ss/user/ntuser/simplecall.c | 11 +- win32ss/user/ntuser/window.h | 4 + win32ss/user/user32/misc/imm.c | 26 +-- 5 files changed, 290 insertions(+), 18 deletions(-) diff --git a/win32ss/include/ntuser.h b/win32ss/include/ntuser.h index 68999e7f932..7dde3b3810f 100644 --- a/win32ss/include/ntuser.h +++ b/win32ss/include/ntuser.h @@ -1727,7 +1727,7 @@ enum SimpleCallRoutines HWNDLOCK_ROUTINE_SETSYSMENU, HWNDLOCK_ROUTINE_UPDATECKIENTRECT, HWNDLOCK_ROUTINE_UPDATEWINDOW, - X_ROUTINE_IMESHOWSTATUSCHANGE, + TWOPARAM_ROUTINE_IMESHOWSTATUSCHANGE, TWOPARAM_ROUTINE_ENABLEWINDOW, TWOPARAM_ROUTINE_REDRAWTITLE, TWOPARAM_ROUTINE_SHOWOWNEDPOPUPS, diff --git a/win32ss/user/ntuser/ime.c b/win32ss/user/ntuser/ime.c index 966e4d0e5d0..370854ad2e8 100644 --- a/win32ss/user/ntuser/ime.c +++ b/win32ss/user/ntuser/ime.c @@ -41,6 +41,7 @@ DBG_DEFAULT_CHANNEL(UserMisc); HIMC ghIMC = NULL; BOOL gfImeOpen = (BOOL)-1; DWORD gdwImeConversion = (DWORD)-1; +BOOL gfIMEShowStatus = (BOOL)-1; typedef struct tagIMEHOTKEY { @@ -2105,4 +2106,268 @@ BOOL FASTCALL IntImeCanDestroyDefIME(PWND pImeWnd, PWND pwndTarget) 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) + { + 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. +// Win: xxxSendMessageToUI +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) + { + 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) + { + 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) + { + 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. +// Win: xxxNotifyImeShowStatus +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) + { + if (pti != ptiIME) + KeDetachProcess(); + 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; +} + /* EOF */ diff --git a/win32ss/user/ntuser/simplecall.c b/win32ss/user/ntuser/simplecall.c index ed7583b0bac..b228746a9e4 100644 --- a/win32ss/user/ntuser/simplecall.c +++ b/win32ss/user/ntuser/simplecall.c @@ -114,6 +114,10 @@ NtUserCallNoParam(DWORD Routine) break; } + case NOPARAM_ROUTINE_GETIMESHOWSTATUS: + Result = !!gfIMEShowStatus; + break; + /* this is a ReactOS only case and is needed for gui-on-demand */ case NOPARAM_ROUTINE_ISCONSOLEMODE: Result = (ScreenDeviceContext == NULL); @@ -895,11 +899,10 @@ NtUserCallHwndParamLock( switch (Routine) { - case X_ROUTINE_IMESHOWSTATUSCHANGE: - { - // TODO: + case TWOPARAM_ROUTINE_IMESHOWSTATUSCHANGE: + Ret = IntBroadcastImeShowStatusChange(Window, !!Param); break; - } + case TWOPARAM_ROUTINE_VALIDATERGN: { PREGION Rgn = REGION_LockRgn((HRGN)Param); diff --git a/win32ss/user/ntuser/window.h b/win32ss/user/ntuser/window.h index a093e4e4ee8..b68c1657c5d 100644 --- a/win32ss/user/ntuser/window.h +++ b/win32ss/user/ntuser/window.h @@ -112,9 +112,13 @@ VOID FASTCALL IntFreeHwndList(PWINDOWLIST pwlTarget); (((pWnd)->pcls->style & CS_IME) || \ ((pWnd)->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])) +extern BOOL gfIMEShowStatus; + BOOL FASTCALL IntWantImeWindow(PWND pwndTarget); PWND FASTCALL co_IntCreateDefaultImeWindow(PWND pwndTarget, HINSTANCE hInst); BOOL FASTCALL IntImeCanDestroyDefIMEforChild(PWND pImeWnd, PWND pwndTarget); BOOL FASTCALL IntImeCanDestroyDefIME(PWND pImeWnd, PWND pwndTarget); +BOOL FASTCALL IntBroadcastImeShowStatusChange(PWND pImeWnd, BOOL bShow); +VOID FASTCALL IntNotifyImeShowStatus(PWND pImeWnd); /* EOF */ diff --git a/win32ss/user/user32/misc/imm.c b/win32ss/user/user32/misc/imm.c index f14db5ddf15..9099235c8ee 100644 --- a/win32ss/user/user32/misc/imm.c +++ b/win32ss/user/user32/misc/imm.c @@ -23,16 +23,18 @@ BOOL gbImmInitializing = FALSE; // Win: bImmInitializing INT gfConIme = -1; // Win: gfConIme -// Win: GetTopLevelWindow -PWND FASTCALL User32GetTopLevelWindow(PWND pwnd) +HWND FASTCALL IntGetTopLevelWindow(HWND hWnd) { - if (!pwnd) - return NULL; + DWORD style; - while (pwnd->style & WS_CHILD) - pwnd = pwnd->spwndParent; + for (; hWnd; hWnd = GetParent(hWnd)) + { + style = (DWORD)GetWindowLongPtrW(hWnd, GWL_STYLE); + if (!(style & WS_CHILD)) + break; + } - return pwnd; + return hWnd; } /* define stub functions */ @@ -573,7 +575,7 @@ static LRESULT ImeWnd_OnImeSystem(PIMEUI pimeui, WPARAM wParam, LPARAM lParam) if (User32GetImeShowStatus() == !lParam) { hImeWnd = UserHMGetHandle(pimeui->spwnd); - NtUserCallHwndParamLock(hImeWnd, lParam, X_ROUTINE_IMESHOWSTATUSCHANGE); + NtUserCallHwndParamLock(hImeWnd, lParam, TWOPARAM_ROUTINE_IMESHOWSTATUSCHANGE); } break; @@ -707,7 +709,7 @@ LRESULT ImeWnd_OnImeSetContext(PIMEUI pimeui, WPARAM wParam, LPARAM lParam) HIMC hIMC; LPINPUTCONTEXTDX pIC; HWND hwndFocus, hwndOldImc, hwndNewImc, hImeWnd, hwndActive, hwndOwner; - PWND pwndFocus, pwndOldImc, pwndNewImc, pImeWnd, pwndOwner; + PWND pwndFocus, pImeWnd, pwndOwner; COMPOSITIONFORM CompForm; pimeui->fActivate = !!wParam; @@ -813,10 +815,8 @@ LRESULT ImeWnd_OnImeSetContext(PIMEUI pimeui, WPARAM wParam, LPARAM lParam) hwndNewImc = pimeui->hwndIMC; if (pimeui->fShowStatus) { - pwndNewImc = ValidateHwnd(hwndNewImc); - pwndOldImc = ValidateHwnd(hwndOldImc); - if (pwndNewImc && pwndOldImc && pwndNewImc != pwndOldImc && - User32GetTopLevelWindow(pwndNewImc) != User32GetTopLevelWindow(pwndOldImc)) + if (hwndOldImc && hwndNewImc && hwndOldImc != hwndNewImc && + IntGetTopLevelWindow(hwndOldImc) != IntGetTopLevelWindow(hwndNewImc)) { User32NotifyOpenStatus(pimeui, hwndOldImc, FALSE); User32NotifyOpenStatus(pimeui, hwndNewImc, TRUE);