[NTUSER][IMM32] Create the default IME window! (retry) (#4463)

The default IME window has to be created for each top-level window in specific condition. It is needed for implementing Japanese input.
- Add IntFocusSetInputContext helper function.
- Call IntFocusSetInputContext after sending WM_KILLFOCUS message.
- Add IntWantImeWindow, co_IntCreateDefaultImeWindow, and IntDestroyOwnedWindows helper functions.
- Create the default IME window (spwndDefaultIme) for the specified window at IntCreateWindow.
- Fix Imm32InternalLockIMC function.
CORE-11700
This commit is contained in:
Katayama Hirofumi MZ 2022-04-23 07:11:48 +09:00 committed by GitHub
parent 2f2795ab4c
commit 8f719cb97e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 265 additions and 48 deletions

View file

@ -830,7 +830,13 @@ LPINPUTCONTEXT APIENTRY Imm32InternalLockIMC(HIMC hIMC, BOOL fSelect)
RtlEnterCriticalSection(&pClientImc->cs);
if (pClientImc->hInputContext)
goto Finish;
{
pIC = LocalLock(pClientImc->hInputContext);
if (pIC)
goto Success;
else
goto Failure;
}
dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID);
if (dwThreadId == GetCurrentThreadId() && Imm32IsCiceroMode() && !Imm32Is16BitMode())
@ -847,14 +853,14 @@ LPINPUTCONTEXT APIENTRY Imm32InternalLockIMC(HIMC hIMC, BOOL fSelect)
}
if (!NtUserQueryInputContext(hIMC, QIC_DEFAULTWINDOWIME))
goto Quit;
goto Failure;
hIC = LocalAlloc(LHND, sizeof(INPUTCONTEXTDX));
pIC = LocalLock(hIC);
if (!pIC)
{
LocalFree(hIC);
goto Quit;
goto Failure;
}
pClientImc->hInputContext = hIC;
@ -862,17 +868,17 @@ LPINPUTCONTEXT APIENTRY Imm32InternalLockIMC(HIMC hIMC, BOOL fSelect)
if (!Imm32CreateInputContext(hIMC, pIC, pClientImc, hNewKL, fSelect))
{
pClientImc->hInputContext = LocalFree(pClientImc->hInputContext);
goto Quit;
goto Failure;
}
Finish:
Success:
CtfImmTIMCreateInputContext(hIMC);
RtlLeaveCriticalSection(&pClientImc->cs);
InterlockedIncrement(&pClientImc->cLockObj);
ImmUnlockClientImc(pClientImc);
return pIC;
Quit:
Failure:
RtlLeaveCriticalSection(&pClientImc->cs);
ImmUnlockClientImc(pClientImc);
return NULL;
@ -1243,6 +1249,7 @@ BOOL WINAPI ImmSetActiveContextConsoleIME(HWND hwnd, BOOL fFlag)
BOOL WINAPI User32InitializeImmEntryTable(DWORD);
// Win: ImmDllInitialize
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
HKL hKL;

View file

@ -24,6 +24,11 @@ extern "C" {
#define IMC_GETSOFTKBDPOS 0x0013
#define IMC_SETSOFTKBDPOS 0x0014
/* wParam for WM_IME_SYSTEM */
#define IMS_IMEACTIVATE 0x17
#define IMS_IMEDEACTIVATE 0x18
#define IMS_ACTIVATELAYOUT 0x19
#define IMMGWL_IMC 0
#define IMMGWL_PRIVATE (sizeof(LONG))

View file

@ -7,6 +7,7 @@
*/
#include <win32k.h>
#include <ddk/immdev.h>
DBG_DEFAULT_CHANNEL(UserFocus);
PUSER_MESSAGE_QUEUE gpqForeground = NULL;
@ -145,11 +146,47 @@ co_IntSendDeactivateMessages(HWND hWndPrev, HWND hWnd, BOOL Clear)
return Ret;
}
// Win: xxxFocusSetInputContext
VOID IntFocusSetInputContext(PWND pWnd, BOOL bActivate, BOOL bCallback)
{
PTHREADINFO pti;
PWND pImeWnd;
USER_REFERENCE_ENTRY Ref;
HWND hImeWnd;
WPARAM wParam;
LPARAM lParam;
if (!pWnd || !pWnd->pcls || IS_WND_IMELIKE(pWnd))
return;
pti = pWnd->head.pti;
if (!pti || (pti->TIF_flags & TIF_INCLEANUP))
return;
pImeWnd = pti->spwndDefaultIme;
if (!pImeWnd)
return;
UserRefObjectCo(pImeWnd, &Ref);
hImeWnd = UserHMGetHandle(pImeWnd);
wParam = (bActivate ? IMS_IMEACTIVATE : IMS_IMEDEACTIVATE);
lParam = (LPARAM)UserHMGetHandle(pWnd);
if (bCallback)
co_IntSendMessageWithCallBack(hImeWnd, WM_IME_SYSTEM, wParam, lParam, NULL, 0, NULL);
else
co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, wParam, lParam);
UserDerefObjectCo(pImeWnd);
}
//
// Deactivating the foreground message queue.
//
// Release Active, Capture and Focus Windows associated with this message queue.
//
// Win: xxxDeactivate
BOOL FASTCALL
IntDeactivateWindow(PTHREADINFO pti, HANDLE tid)
{
@ -297,6 +334,10 @@ IntDeactivateWindow(PTHREADINFO pti, HANDLE tid)
UserRefObjectCo(pwndFocus, &Ref);
co_IntSendMessage(UserHMGetHandle(pwndFocus), WM_KILLFOCUS, 0, 0);
if (IS_IMM_MODE())
{
IntFocusSetInputContext(pwndFocus, FALSE, FALSE);
}
UserDerefObjectCo(pwndFocus);
}
@ -569,11 +610,13 @@ co_IntSendActivateMessages(PWND WindowPrev, PWND Window, BOOL MouseActivate, BOO
return InAAPM;
}
// Win: xxxSendFocusMessages
VOID FASTCALL
IntSendFocusMessages( PTHREADINFO pti, PWND pWnd)
{
PWND pWndPrev;
PUSER_MESSAGE_QUEUE ThreadQueue = pti->MessageQueue; // Queue can change...
HWND hwndPrev;
ThreadQueue->QF_flags &= ~QF_FOCUSNULLSINCEACTIVE;
if (!pWnd && ThreadQueue->spwndActive)
@ -593,12 +636,22 @@ IntSendFocusMessages( PTHREADINFO pti, PWND pWnd)
if (pWndPrev)
{
co_IntSendMessage(UserHMGetHandle(pWndPrev), WM_KILLFOCUS, (WPARAM)UserHMGetHandle(pWnd), 0);
if (IS_IMM_MODE())
{
IntFocusSetInputContext(pWndPrev, FALSE, FALSE);
}
}
if (ThreadQueue->spwndFocus == pWnd)
{
if (IS_IMM_MODE())
{
IntFocusSetInputContext(pWnd, TRUE, FALSE);
}
IntNotifyWinEvent(EVENT_OBJECT_FOCUS, pWnd, OBJID_CLIENT, CHILDID_SELF, 0);
co_IntSendMessage(UserHMGetHandle(pWnd), WM_SETFOCUS, (WPARAM)(pWndPrev ? UserHMGetHandle(pWndPrev) : NULL), 0);
hwndPrev = (pWndPrev ? UserHMGetHandle(pWndPrev) : NULL);
co_IntSendMessage(UserHMGetHandle(pWnd), WM_SETFOCUS, (WPARAM)hwndPrev, 0);
}
}
else
@ -608,6 +661,10 @@ IntSendFocusMessages( PTHREADINFO pti, PWND pWnd)
IntNotifyWinEvent(EVENT_OBJECT_FOCUS, NULL, OBJID_CLIENT, CHILDID_SELF, 0);
co_IntSendMessage(UserHMGetHandle(pWndPrev), WM_KILLFOCUS, 0, 0);
if (IS_IMM_MODE())
{
IntFocusSetInputContext(pWndPrev, FALSE, FALSE);
}
}
}
}
@ -1241,6 +1298,7 @@ UserSetActiveWindow( _In_opt_ PWND Wnd )
return FALSE;
}
// Win: PWND xxxSetFocus(Window)
HWND FASTCALL
co_UserSetFocus(PWND Window)
{

View file

@ -1889,7 +1889,110 @@ BOOL IntFindNonImeRelatedWndOfSameThread(PWND pwndParent, PWND pwndTarget)
return FALSE;
}
// Can we destroy the default IME window for the target child window?
// Determines whether the target window needs the IME window.
// Win: WantImeWindow(pwndParent, pwndTarget)
BOOL FASTCALL IntWantImeWindow(PWND pwndTarget)
{
PDESKTOP rpdesk;
PWINSTATION_OBJECT rpwinstaParent;
PWND pwndNode, pwndParent = pwndTarget->spwndParent;
if (gptiCurrent->TIF_flags & TIF_DISABLEIME)
return FALSE;
if (IS_WND_IMELIKE(pwndTarget))
return FALSE;
if (pwndTarget->fnid == FNID_DESKTOP || pwndTarget->fnid == FNID_MESSAGEWND)
return FALSE;
if (pwndTarget->state & WNDS_SERVERSIDEWINDOWPROC)
return FALSE;
rpdesk = pwndTarget->head.rpdesk;
if (!rpdesk)
return FALSE;
rpwinstaParent = rpdesk->rpwinstaParent;
if (!rpwinstaParent || (rpwinstaParent->Flags & WSS_NOIO))
return FALSE;
for (pwndNode = pwndParent; pwndNode; pwndNode = pwndNode->spwndParent)
{
if (rpdesk != pwndNode->head.rpdesk)
break;
if (pwndNode == rpdesk->spwndMessage)
return FALSE;
}
return TRUE;
}
// Create the default IME window for the target window.
// Win: xxxCreateDefaultImeWindow(pwndTarget, ATOM, hInst)
PWND FASTCALL co_IntCreateDefaultImeWindow(PWND pwndTarget, HINSTANCE hInst)
{
LARGE_UNICODE_STRING WindowName;
UNICODE_STRING ClassName;
PWND pImeWnd;
PIMEUI pimeui;
CREATESTRUCTW Cs;
USER_REFERENCE_ENTRY Ref;
PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
HANDLE pid = PsGetThreadProcessId(pti->pEThread);
if (!(pti->spDefaultImc) && pid == gpidLogon)
UserCreateInputContext(0);
if (!(pti->spDefaultImc) || IS_WND_IMELIKE(pwndTarget) || !(pti->rpdesk->pheapDesktop))
return NULL;
if (IS_WND_CHILD(pwndTarget) && !(pwndTarget->style & WS_VISIBLE) &&
pwndTarget->spwndParent->head.pti->ppi != pti->ppi)
{
return NULL;
}
RtlInitLargeUnicodeString(&WindowName, L"Default IME", 0);
ClassName.Buffer = (PWCH)(ULONG_PTR)gpsi->atomSysClass[ICLS_IME];
ClassName.Length = 0;
ClassName.MaximumLength = 0;
UserRefObjectCo(pwndTarget, &Ref);
RtlZeroMemory(&Cs, sizeof(Cs));
Cs.style = WS_POPUP | WS_DISABLED;
Cs.hInstance = hInst;
Cs.hwndParent = UserHMGetHandle(pwndTarget);
Cs.lpszName = WindowName.Buffer;
Cs.lpszClass = ClassName.Buffer;
// NOTE: LARGE_UNICODE_STRING is compatible to LARGE_STRING.
pImeWnd = co_UserCreateWindowEx(&Cs, &ClassName, (PLARGE_STRING)&WindowName, NULL, WINVER);
if (pImeWnd)
{
pimeui = ((PIMEWND)pImeWnd)->pimeui;
_SEH2_TRY
{
ProbeForWrite(pimeui, sizeof(IMEUI), 1);
pimeui->fDefault = TRUE;
if (IS_WND_CHILD(pwndTarget) && pwndTarget->spwndParent->head.pti != pti)
pimeui->fChildThreadDef = TRUE;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
NOTHING;
}
_SEH2_END;
}
UserDerefObjectCo(pwndTarget);
return pImeWnd;
}
// Determines whether the system can destroy the default IME window for the target child window.
// Win: ImeCanDestroyDefIMEforChild
BOOL FASTCALL IntImeCanDestroyDefIMEforChild(PWND pImeWnd, PWND pwndTarget)
{
@ -1911,7 +2014,7 @@ BOOL FASTCALL IntImeCanDestroyDefIMEforChild(PWND pImeWnd, PWND pwndTarget)
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
;
NOTHING;
}
_SEH2_END;
@ -1934,8 +2037,7 @@ BOOL FASTCALL IntImeCanDestroyDefIMEforChild(PWND pImeWnd, PWND pwndTarget)
return TRUE;
}
// Can we destroy the default IME window for the non-child target window?
// If so, this function sets spwndOwner to NULL.
// Determines whether the system can destroy the default IME window for the non-child target window.
// Win: ImeCanDestroyDefIME
BOOL FASTCALL IntImeCanDestroyDefIME(PWND pImeWnd, PWND pwndTarget)
{
@ -1957,7 +2059,7 @@ BOOL FASTCALL IntImeCanDestroyDefIME(PWND pImeWnd, PWND pwndTarget)
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
;
NOTHING;
}
_SEH2_END;

View file

@ -8,6 +8,7 @@
*/
#include <win32k.h>
#include <ddk/immdev.h>
DBG_DEFAULT_CHANNEL(UserWnd);
INT gNestedWindowLimit = 50;
@ -658,8 +659,16 @@ LRESULT co_UserFreeWindow(PWND Window,
ThreadData->rpdesk->rpwinstaParent->ShellListView = NULL;
}
if (ThreadData->spwndDefaultIme &&
ThreadData->spwndDefaultIme->spwndOwner == Window)
{
ThreadData->spwndDefaultIme->spwndOwner = 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)
@ -1775,7 +1784,7 @@ PWND FASTCALL IntCreateWindow(CREATESTRUCTW* Cs,
{
PWND pWnd = NULL;
HWND hWnd;
PTHREADINFO pti = NULL;
PTHREADINFO pti;
BOOL MenuChanged;
BOOL bUnicodeWindow;
PCALLPROCDATA pcpd;
@ -2027,6 +2036,28 @@ PWND FASTCALL IntCreateWindow(CREATESTRUCTW* Cs,
pWnd->strName.MaximumLength = WindowName->Length + sizeof(UNICODE_NULL);
}
/* Create the IME window for pWnd */
if (IS_IMM_MODE() && !(pti->spwndDefaultIme) && IntWantImeWindow(pWnd))
{
PWND pwndDefaultIme = co_IntCreateDefaultImeWindow(pWnd, pWnd->hModule);
UserAssignmentLock((PVOID*)&(pti->spwndDefaultIme), pwndDefaultIme);
if (pwndDefaultIme && (pti->pClientInfo->CI_flags & CI_IMMACTIVATE))
{
USER_REFERENCE_ENTRY Ref;
HKL hKL;
UserRefObjectCo(pwndDefaultIme, &Ref);
hKL = pti->KeyboardLayout->hkl;
co_IntSendMessage(UserHMGetHandle(pwndDefaultIme), WM_IME_SYSTEM,
IMS_ACTIVATELAYOUT, (LPARAM)hKL);
pti->pClientInfo->CI_flags &= ~CI_IMMACTIVATE;
UserDerefObjectCo(pwndDefaultIme);
}
}
/* Correct the window style. */
if ((pWnd->style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
{
@ -2133,7 +2164,6 @@ Error:
/*
* @implemented
* Win: xxxCreateWindowEx
*/
PWND FASTCALL
co_UserCreateWindowEx(CREATESTRUCTW* Cs,
@ -2748,6 +2778,49 @@ cleanup:
return hwnd;
}
// Win: xxxDW_DestroyOwnedWindows
VOID FASTCALL IntDestroyOwnedWindows(PWND Window)
{
HWND* List;
HWND* phWnd;
PWND pWnd;
PTHREADINFO pti = Window->head.pti;
USER_REFERENCE_ENTRY Ref;
List = IntWinListOwnedPopups(Window);
if (!List)
return;
for (phWnd = List; *phWnd; ++phWnd)
{
pWnd = ValidateHwndNoErr(*phWnd);
if (pWnd == NULL)
continue;
ASSERT(pWnd->spwndOwner == Window);
ASSERT(pWnd != Window);
if (IS_IMM_MODE() && !(pti->TIF_flags & TIF_INCLEANUP) &&
pWnd == pti->spwndDefaultIme)
{
continue;
}
pWnd->spwndOwner = NULL;
if (IntWndBelongsToThread(pWnd, PsGetCurrentThreadWin32Thread()))
{
UserRefObjectCo(pWnd, &Ref); // Temp HACK?
co_UserDestroyWindow(pWnd);
UserDerefObjectCo(pWnd); // Temp HACK?
}
else
{
ERR("IntWndBelongsToThread(0x%p) is FALSE, ignoring.\n", pWnd);
}
}
ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
}
// Win: xxxDestroyWindow
BOOLEAN co_UserDestroyWindow(PVOID Object)
{
@ -2876,37 +2949,7 @@ BOOLEAN co_UserDestroyWindow(PVOID Object)
/* Recursively destroy owned windows */
if (!(Window->style & WS_CHILD))
{
HWND* List;
HWND* phWnd;
PWND pWnd;
List = IntWinListOwnedPopups(Window);
if (List)
{
for (phWnd = List; *phWnd; ++phWnd)
{
pWnd = ValidateHwndNoErr(*phWnd);
if (pWnd == NULL)
continue;
ASSERT(pWnd->spwndOwner == Window);
ASSERT(pWnd != Window);
pWnd->spwndOwner = NULL;
if (IntWndBelongsToThread(pWnd, PsGetCurrentThreadWin32Thread()))
{
USER_REFERENCE_ENTRY Ref;
UserRefObjectCo(pWnd, &Ref); // Temp HACK?
co_UserDestroyWindow(pWnd);
UserDerefObjectCo(pWnd); // Temp HACK?
}
else
{
ERR("IntWndBelongsToThread(0x%p) is FALSE, ignoring.\n", pWnd);
}
}
ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
}
IntDestroyOwnedWindows(Window);
}
/* Generate mouse move message for the next window */

View file

@ -112,6 +112,8 @@ VOID FASTCALL IntFreeHwndList(PWINDOWLIST pwlTarget);
(((pWnd)->pcls->style & CS_IME) || \
((pWnd)->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]))
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);

View file

@ -664,15 +664,15 @@ static LRESULT ImeWnd_OnImeSystem(PIMEUI pimeui, WPARAM wParam, LPARAM lParam)
// TODO:
break;
case 0x17:
case IMS_IMEACTIVATE:
User32SetImeActivenessOfWindow((HWND)lParam, TRUE);
break;
case 0x18:
case IMS_IMEDEACTIVATE:
User32SetImeActivenessOfWindow((HWND)lParam, FALSE);
break;
case 0x19:
case IMS_ACTIVATELAYOUT:
ret = IMM_FN(ImmActivateLayout)((HKL)lParam);
break;