From 8f719cb97e8e2148ef149edb984e60a9a95b30c2 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Sat, 23 Apr 2022 07:11:48 +0900 Subject: [PATCH] [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 --- dll/win32/imm32/imm.c | 19 ++++-- sdk/include/ddk/immdev.h | 5 ++ win32ss/user/ntuser/focus.c | 60 +++++++++++++++++- win32ss/user/ntuser/ime.c | 112 +++++++++++++++++++++++++++++++-- win32ss/user/ntuser/window.c | 109 ++++++++++++++++++++++---------- win32ss/user/ntuser/window.h | 2 + win32ss/user/user32/misc/imm.c | 6 +- 7 files changed, 265 insertions(+), 48 deletions(-) diff --git a/dll/win32/imm32/imm.c b/dll/win32/imm32/imm.c index 5d199630229..45328abb595 100644 --- a/dll/win32/imm32/imm.c +++ b/dll/win32/imm32/imm.c @@ -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; diff --git a/sdk/include/ddk/immdev.h b/sdk/include/ddk/immdev.h index 61641ba1633..58bbc1dc1d6 100644 --- a/sdk/include/ddk/immdev.h +++ b/sdk/include/ddk/immdev.h @@ -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)) diff --git a/win32ss/user/ntuser/focus.c b/win32ss/user/ntuser/focus.c index a9ea0965177..7ecb9c88f73 100644 --- a/win32ss/user/ntuser/focus.c +++ b/win32ss/user/ntuser/focus.c @@ -7,6 +7,7 @@ */ #include +#include 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) { diff --git a/win32ss/user/ntuser/ime.c b/win32ss/user/ntuser/ime.c index e869608724f..f8cd3778512 100644 --- a/win32ss/user/ntuser/ime.c +++ b/win32ss/user/ntuser/ime.c @@ -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; diff --git a/win32ss/user/ntuser/window.c b/win32ss/user/ntuser/window.c index 9ec554b4a24..a740fcc11e8 100644 --- a/win32ss/user/ntuser/window.c +++ b/win32ss/user/ntuser/window.c @@ -8,6 +8,7 @@ */ #include +#include 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 */ diff --git a/win32ss/user/ntuser/window.h b/win32ss/user/ntuser/window.h index 86daff24d97..a093e4e4ee8 100644 --- a/win32ss/user/ntuser/window.h +++ b/win32ss/user/ntuser/window.h @@ -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); diff --git a/win32ss/user/user32/misc/imm.c b/win32ss/user/user32/misc/imm.c index 6fa8eafd1d6..f14db5ddf15 100644 --- a/win32ss/user/user32/misc/imm.c +++ b/win32ss/user/user32/misc/imm.c @@ -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;