[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
This commit is contained in:
Katayama Hirofumi MZ 2022-05-08 20:16:17 +09:00 committed by GitHub
parent a849125947
commit 242e0b4303
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 290 additions and 18 deletions

View file

@ -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,

View file

@ -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 */

View file

@ -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);

View file

@ -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 */

View file

@ -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);