From: Akihiro Sagawa Subject: [3/3] imm32,user32: Create and destroy the default IME window implicitly. (try 9; resend) Message-Id: <20160823221307.A34C.375B48EC@gmail.com> Date: Tue, 23 Aug 2016 22:13:27 +0900 Try 9: - Withdraw try 8 changes as we have ImmDisableIME now. - Rebased and resolve conflicts against ImmDisableIME patch. Signed-off-by: Akihiro Sagawa --- dlls/imm32/imm.c | 99 +++++++++++++++++++++++++++++++++++----------- dlls/imm32/imm32.spec | 1 + dlls/imm32/tests/imm32.c | 12 +++--- dlls/user32/misc.c | 4 +- dlls/user32/user_private.h | 1 + dlls/user32/win.c | 26 ++++++++++++ dlls/user32/win.h | 1 + 7 files changed, 113 insertions(+), 31 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index d9c9782..8279534 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -96,6 +96,7 @@ typedef struct _tagIMMThreadData { HIMC defaultContext; HWND hwndDefault; BOOL disableIME; + DWORD windowRefs; } IMMThreadData; static struct list ImmHklList = LIST_INIT(ImmHklList); @@ -1618,44 +1619,96 @@ BOOL WINAPI ImmGetConversionStatus( return TRUE; } -/*********************************************************************** - * ImmGetDefaultIMEWnd (IMM32.@) - */ -HWND WINAPI ImmGetDefaultIMEWnd(HWND hWnd) +static BOOL needs_ime_window(HWND hwnd) { - HWND ret, new = NULL; - IMMThreadData* thread_data = IMM_GetThreadData(hWnd, 0); + WCHAR classW[8]; + + if (GetClassNameW(hwnd, classW, sizeof(classW)/sizeof(classW[0])) && + !strcmpW(classW, szwIME)) + return FALSE; + if (GetClassLongPtrW(hwnd, GCL_STYLE) & CS_IME) return FALSE; + + return TRUE; +} + +BOOL WINAPI __wine_register_window(HWND hwnd) +{ + HWND new = NULL; + IMMThreadData* thread_data; + TRACE("(%p)\n", hwnd); + + if (hwnd && !needs_ime_window(hwnd)) + return FALSE; + + thread_data = IMM_GetThreadData(hwnd, 0); if (!thread_data) - return NULL; - if (thread_data->hwndDefault == NULL && thread_data->threadID == GetCurrentThreadId()) + return FALSE; + + if (hwnd) { if (thread_data->disableIME || disable_ime) { TRACE("IME for this thread is disabled\n"); LeaveCriticalSection(&threaddata_cs); - return NULL; + return FALSE; } - /* Do not create the window inside of a critical section */ - LeaveCriticalSection(&threaddata_cs); - new = CreateWindowExW( WS_EX_TOOLWINDOW, - szwIME, NULL, WS_POPUP, 0, 0, 1, 1, 0, 0, 0, 0); - /* thread_data is in the current thread so we can assume it's still valid */ - EnterCriticalSection(&threaddata_cs); - /* See if anyone beat us */ - if (thread_data->hwndDefault == NULL) + thread_data->windowRefs++; + TRACE("windowRefs=%u, hwndDefault=%p\n", + thread_data->windowRefs, thread_data->hwndDefault); + + /* Create default IME window */ + if (thread_data->windowRefs == 1) + { + /* Do not create the window inside of a critical section */ + LeaveCriticalSection(&threaddata_cs); + new = CreateWindowExW( WS_EX_TOOLWINDOW, + szwIME, NULL, WS_POPUP, 0, 0, 1, 1, 0, 0, 0, 0); + /* thread_data is in the current thread so we can assume it's still valid */ + EnterCriticalSection(&threaddata_cs); + /* See if anyone beat us */ + if (thread_data->hwndDefault == NULL) + { + thread_data->hwndDefault = new; + new = NULL; + TRACE("Default is %p\n", thread_data->hwndDefault); + } + } + } + else + { + thread_data->windowRefs--; + TRACE("windowRefs=%u, hwndDefault=%p\n", + thread_data->windowRefs, thread_data->hwndDefault); + + /* Destroy default IME window */ + if (thread_data->windowRefs == 0 && thread_data->hwndDefault) { - thread_data->hwndDefault = new; - new = NULL; + hwnd = thread_data->hwndDefault; + LeaveCriticalSection(&threaddata_cs); + DestroyWindow(hwnd); + EnterCriticalSection(&threaddata_cs); + thread_data->hwndDefault = NULL; } } - ret = thread_data->hwndDefault; LeaveCriticalSection(&threaddata_cs); - TRACE("Default is %p\n",ret); + /* Clean up an unused new window outside of the critical section */ if (new != NULL) - { DestroyWindow(new); - } + return TRUE; +} + +/*********************************************************************** + * ImmGetDefaultIMEWnd (IMM32.@) + */ +HWND WINAPI ImmGetDefaultIMEWnd(HWND hWnd) +{ + HWND ret; + IMMThreadData* thread_data = IMM_GetThreadData(hWnd, 0); + if (!thread_data) + return NULL; + ret = thread_data->hwndDefault; + LeaveCriticalSection(&threaddata_cs); TRACE("Default is %p\n",ret); return ret; } diff --git a/dlls/imm32/imm32.spec b/dlls/imm32/imm32.spec index 0bee92a..7dac8ab 100644 --- a/dlls/imm32/imm32.spec +++ b/dlls/imm32/imm32.spec @@ -114,3 +114,4 @@ ################################################################ # Wine internal extensions @ stdcall __wine_get_ui_window(ptr) +@ stdcall __wine_register_window(long) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index f0f32a7..bed42a0 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -207,7 +207,6 @@ static LRESULT WINAPI wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) case FIRST_WINDOW: case SECOND_WINDOW: case CREATE_CANCEL: - todo_wine_if(test_phase == FIRST_WINDOW || test_phase == CREATE_CANCEL) ok(default_ime_wnd != NULL, "expected IME window existence\n"); break; case IME_DISABLED: @@ -223,7 +222,6 @@ static LRESULT WINAPI wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) case FIRST_WINDOW: case SECOND_WINDOW: case CREATE_CANCEL: - todo_wine_if(test_phase == FIRST_WINDOW || test_phase == CREATE_CANCEL) ok(default_ime_wnd != NULL, "expected IME window existence\n"); break; case IME_DISABLED: @@ -952,9 +950,9 @@ static DWORD WINAPI test_default_ime_window_cb(void *arg) 240, 24, hwnd, NULL, GetModuleHandleW(NULL), NULL); } ime_wnd = get_ime_window(); - todo_wine ok(ime_wnd != NULL, "Expected IME window existence\n"); + ok(ime_wnd != NULL, "Expected IME window existence\n"); default_ime_wnd = ImmGetDefaultIMEWnd(hwnd1); - todo_wine ok(ime_wnd == default_ime_wnd, "Expected %p, got %p\n", ime_wnd, default_ime_wnd); + ok(ime_wnd == default_ime_wnd, "Expected %p, got %p\n", ime_wnd, default_ime_wnd); test_phase = SECOND_WINDOW; hwnd2 = CreateWindowExA(WS_EX_CLIENTEDGE, wndcls, "Wine imm32.dll test", @@ -962,7 +960,7 @@ static DWORD WINAPI test_default_ime_window_cb(void *arg) CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL); DestroyWindow(hwnd2); - todo_wine ok(IsWindow(ime_wnd) || + ok(IsWindow(ime_wnd) || broken(!testcase->visible /* Vista */) || broken(!testcase->top_level_window /* Vista */) , "Expected IME window existence\n"); @@ -1000,9 +998,9 @@ static DWORD WINAPI test_default_ime_window_cancel_cb(void *arg) CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, GetModuleHandleW(NULL), NULL); ime_wnd = get_ime_window(); - todo_wine ok(ime_wnd != NULL, "Expected IME window existence\n"); + ok(ime_wnd != NULL, "Expected IME window existence\n"); default_ime_wnd = ImmGetDefaultIMEWnd(hwnd2); - todo_wine ok(ime_wnd == default_ime_wnd, "Expected %p, got %p\n", ime_wnd, default_ime_wnd); + ok(ime_wnd == default_ime_wnd, "Expected %p, got %p\n", ime_wnd, default_ime_wnd); DestroyWindow(hwnd2); ok(!IsWindow(ime_wnd), "Expected no IME windows\n"); diff --git a/dlls/user32/misc.c b/dlls/user32/misc.c index 7b283cc..0d57d04 100644 --- a/dlls/user32/misc.c +++ b/dlls/user32/misc.c @@ -41,6 +41,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(win); #define IMM_INIT_MAGIC 0x19650412 static HWND (WINAPI *imm_get_ui_window)(HKL); +HWND (WINAPI *imm_register_window)(HWND) = NULL; /* MSIME messages */ static UINT WM_MSIME_SERVICE; @@ -684,7 +685,8 @@ BOOL WINAPI User32InitializeImmEntryTable(DWORD magic) /* this part is not compatible with native imm32.dll */ imm_get_ui_window = (void*)GetProcAddress(imm32, "__wine_get_ui_window"); - if (!imm_get_ui_window) + imm_register_window = (void*)GetProcAddress(imm32, "__wine_register_window"); + if (!imm_get_ui_window || !imm_register_window) FIXME("native imm32.dll not supported\n"); return TRUE; } diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index e4a2ed1..8ad32f1 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -194,6 +194,7 @@ struct user_thread_info C_ASSERT( sizeof(struct user_thread_info) <= sizeof(((TEB *)0)->Win32ClientInfo) ); extern INT global_key_state_counter DECLSPEC_HIDDEN; +extern HWND (WINAPI *imm_register_window)(HWND) DECLSPEC_HIDDEN; struct user_key_state_info { diff --git a/dlls/user32/win.c b/dlls/user32/win.c index 8ece6a1..8a52426 100644 --- a/dlls/user32/win.c +++ b/dlls/user32/win.c @@ -958,6 +958,19 @@ LRESULT WIN_DestroyWindow( HWND hwnd ) TRACE("%p\n", hwnd ); + /* destroy default IME window */ + if (imm_register_window) + { + if (!(wndPtr = WIN_GetPtr( hwnd )) || wndPtr == WND_OTHER_PROCESS) return 0; + if (wndPtr->flags & WIN_HAS_IME_WIN) + { + TRACE("unregister IME window for %p\n", hwnd); + WIN_ReleasePtr( wndPtr ); + imm_register_window( NULL ); + } + else WIN_ReleasePtr( wndPtr ); + } + /* free child windows */ if ((list = WIN_ListChildren( hwnd ))) { @@ -1604,6 +1617,19 @@ HWND WIN_CreateWindowEx( CREATESTRUCTW *cs, LPCWSTR className, HINSTANCE module, goto failed; } + /* create default IME window */ + + if (imm_register_window && !is_desktop_window( hwnd ) && imm_register_window( hwnd )) + { + wndPtr = WIN_GetPtr( hwnd ); + if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP) + { + TRACE("register IME window for %p\n", hwnd); + wndPtr->flags |= WIN_HAS_IME_WIN; + WIN_ReleasePtr( wndPtr ); + } + } + /* send WM_NCCALCSIZE */ if (WIN_GetRectangles( hwnd, COORDS_PARENT, &rect, NULL )) diff --git a/dlls/user32/win.h b/dlls/user32/win.h index 8739cad..d38b669 100644 --- a/dlls/user32/win.h +++ b/dlls/user32/win.h @@ -77,6 +77,7 @@ typedef struct tagWND #define WIN_ISUNICODE 0x0010 /* Window is Unicode */ #define WIN_NEEDS_SHOW_OWNEDPOPUP 0x0020 /* WM_SHOWWINDOW:SC_SHOW must be sent in the next ShowOwnedPopup call */ #define WIN_CHILDREN_MOVED 0x0040 /* children may have moved, ignore stored positions */ +#define WIN_HAS_IME_WIN 0x0080 /* decrement default IME window counter needed */ /* Window functions */ extern HWND get_hwnd_message_parent(void) DECLSPEC_HIDDEN;