[NTUSER] Destroy the default IME window (#4462)

- Add IntFindNonImeRelatedWndOfSameThread, IntImeCanDestroyDefIMEforChild, and IntImeCanDestroyDefIME helper functions.
- Do assignment unlock spwndDefaultIme at co_UserFreeWindow.
- Destroy the default IME window of the specified window if necessary at co_UserDestroyWindow.
CORE-11700
This commit is contained in:
Katayama Hirofumi MZ 2022-04-20 16:10:06 +09:00 committed by GitHub
parent 944b942eb2
commit 4bcf23d1de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 221 additions and 10 deletions

View file

@ -21,15 +21,6 @@ DBG_DEFAULT_CHANNEL(UserMisc);
#define LANGID_CHINESE_TRADITIONAL MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
#define LANGID_NEUTRAL MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)
// The IME-like windows are the IME windows and the IME UI windows.
// The IME window's class name is "IME".
// The IME UI window behaves the User Interface of IME for the user.
#define IS_WND_IMELIKE(pwnd) \
(((pwnd)->pcls->style & CS_IME) || \
((pwnd)->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]))
#define IS_WND_MENU(pWnd) \
((pWnd)->pcls->atomClassName == gpsi->atomSysClass[ICLS_MENU])
// The special virtual keys for Japanese: Used for key states.
// https://www.kthree.co.jp/kihelp/index.html?page=app/vkey&type=html
#define VK_DBE_ALPHANUMERIC 0xF0
@ -1820,4 +1811,192 @@ Quit:
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;
}
// Can we 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)
{
;
}
_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;
}
// Can we destroy the default IME window for the non-child target window?
// If so, this function sets spwndOwner to NULL.
// 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)
{
;
}
_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;
pImeWnd->spwndOwner = NULL;
return TRUE;
}
/* EOF */

View file

@ -658,6 +658,9 @@ LRESULT co_UserFreeWindow(PWND Window,
ThreadData->rpdesk->rpwinstaParent->ShellListView = NULL;
}
if (IS_IMM_MODE() && Window == ThreadData->spwndDefaultIme)
UserAssignmentUnlock((PVOID*)&(ThreadData->spwndDefaultIme));
/* Fixes dialog test_focus breakage due to r66237. */
if (ThreadData->MessageQueue->spwndFocus == Window)
ThreadData->MessageQueue->spwndFocus = NULL;
@ -2762,7 +2765,7 @@ BOOLEAN co_UserDestroyWindow(PVOID Object)
TRACE("co_UserDestroyWindow(Window = 0x%p, hWnd = 0x%p)\n", Window, hWnd);
/* Check for owner thread */
if ( Window->head.pti != PsGetCurrentThreadWin32Thread())
if (Window->head.pti != ti)
{
/* Check if we are destroying the desktop window */
if (! ((Window->head.rpdesk->dwDTFlags & DF_DESTROYED) && Window == Window->head.rpdesk->pDeskInfo->spwnd))
@ -2918,6 +2921,22 @@ BOOLEAN co_UserDestroyWindow(PVOID Object)
/* Send destroy messages */
IntSendDestroyMsg(UserHMGetHandle(Window));
// Destroy the default IME window if necessary
if (IS_IMM_MODE() && !(ti->TIF_flags & TIF_INCLEANUP) &&
ti->spwndDefaultIme && !IS_WND_IMELIKE(Window) && !(Window->state & WNDS_DESTROYED))
{
if (IS_WND_CHILD(Window))
{
if (IntImeCanDestroyDefIMEforChild(ti->spwndDefaultIme, Window))
co_UserDestroyWindow(ti->spwndDefaultIme);
}
else
{
if (IntImeCanDestroyDefIME(ti->spwndDefaultIme, Window))
co_UserDestroyWindow(ti->spwndDefaultIme);
}
}
if (!IntIsWindow(UserHMGetHandle(Window)))
{
return TRUE;

View file

@ -102,4 +102,17 @@ VOID FASTCALL IntFreeHwndList(PWINDOWLIST pwlTarget);
/* Undocumented dwFlags for IntBuildHwndList */
#define IACE_LIST 0x0002
#define IS_WND_CHILD(pWnd) ((pWnd)->style & WS_CHILD)
#define IS_WND_MENU(pWnd) ((pWnd)->pcls->atomClassName == gpsi->atomSysClass[ICLS_MENU])
// The IME-like windows are the IME windows and the IME UI windows.
// The IME window's class name is "IME".
// The IME UI window behaves the User Interface of IME for the user.
#define IS_WND_IMELIKE(pWnd) \
(((pWnd)->pcls->style & CS_IME) || \
((pWnd)->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]))
BOOL FASTCALL IntImeCanDestroyDefIMEforChild(PWND pImeWnd, PWND pwndTarget);
BOOL FASTCALL IntImeCanDestroyDefIME(PWND pImeWnd, PWND pwndTarget);
/* EOF */