From 81fa7d10c836ebb484e22690077fc2ef334e2af7 Mon Sep 17 00:00:00 2001 From: "Lu Fan(SonnyCalcr)" <1730976608@qq.com> Date: Fri, 16 Oct 2026 19:28:02 +0800 Subject: [PATCH] =?UTF-8?q?fix(server):=20=E5=85=B3=E9=97=AD=20Direct2D=20?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E7=AA=97=E5=8F=A3=E5=90=8E=E6=82=AC=E6=B5=AE?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E6=A0=8F=E4=B8=8D=E5=86=8D=E6=B6=88=E5=A4=B1?= =?UTF-8?q?=20(#1002)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Direct2D 设置窗口是获得键盘焦点的 Win32 窗口,进程里的 TIP 会成为普通输入客户端。 正常退出时 DestroyWindow / CoUninitialize 让 TSF 反激活 TIP,发出终结性反激活, Server 当成用户切走了输入法而隐藏工具栏;回到的窗口不一定重新激活,工具栏要等 点击别的窗口才回来。WebView2 前端的焦点在 WebView 子进程里,宿主线程从不持有路由, 所以没有这个问题。 Direct2D 前端在写入排空后不再销毁窗口、跳过 CoUninitialize,直接结束进程: Server 只看到管道断开(仅挂起路由,不动工具栏),与 WebView2 前端一致。 --- server/src/settings/settings_app.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/server/src/settings/settings_app.cpp b/server/src/settings/settings_app.cpp index d0f2e4c3..5be18bb7 100644 --- a/server/src/settings/settings_app.cpp +++ b/server/src/settings/settings_app.cpp @@ -2453,7 +2453,14 @@ LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_pa { g_worker->Drain(!g_closing); if (g_closing && g_worker->Stopped()) - DestroyWindow(hwnd); + { + // The Direct2D frontend leaves without destroying its window: see the + // end of wWinMain for why the process must skip the TSF teardown. + if (g_native) + PostQuitMessage(0); + else + DestroyWindow(hwnd); + } } return 0; case WM_CLOSE: @@ -2649,7 +2656,10 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, PWSTR command_line, int show_ DispatchMessageW(&message); } g_worker.reset(); // Drain accepted writes even if the window was destroyed externally. - CoUninitialize(); + // The Direct2D frontend skips CoUninitialize like it skips DestroyWindow: both tear + // down this thread's TSF state (see below). + if (!use_native) + CoUninitialize(); if (g_relaunch_after_exit) { // The renderer was switched: reopen in it once this instance no longer holds the @@ -2663,5 +2673,18 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, PWSTR command_line, int show_ ShellExecuteW(nullptr, L"open", module_path.c_str(), nullptr, nullptr, SW_SHOWNORMAL); } } + if (use_native) + { + // The Direct2D window is a focused Win32 window, so this thread is an ordinary + // input client of the IME. Destroying the window or uninitializing COM lets TSF + // deactivate the text service here, which reports a terminal deactivation, and + // the Server hides the floating toolbar as if the user had switched input + // methods. The window the user returns to may not re-activate, so the toolbar + // stayed hidden until they clicked elsewhere. Every write has been drained + // above; end the process without that teardown, so the Server only sees the + // pipes close (a route suspension, which leaves the toolbar alone), as it does + // for the WebView2 frontend. + TerminateProcess(GetCurrentProcess(), static_cast(message.wParam)); + } return static_cast(message.wParam); }