https://git.reactos.org/?p=reactos.git;a=commitdiff;h=242e0b43036ce5acea66bc5d8be8bd37296c4130

commit 242e0b43036ce5acea66bc5d8be8bd37296c4130
Author:     Katayama Hirofumi MZ <[email protected]>
AuthorDate: Sun May 8 20:16:17 2022 +0900
Commit:     GitHub <[email protected]>
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);

Reply via email to