https://git.reactos.org/?p=reactos.git;a=commitdiff;h=242e0b43036ce5acea66b…
commit 242e0b43036ce5acea66bc5d8be8bd37296c4130
Author: Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Sun May 8 20:16:17 2022 +0900
Commit: GitHub <noreply(a)github.com>
CommitDate: Sun May 8 20:16:17 2022 +0900
[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);