Add logs.

This commit is contained in:
fanlusky committed 2026-08-22 18:34:13 +08:00
1 parent e42cc92b8e
commit 2e44cc4e24
11 files changed
+280 -8

No files matched your search

+2
View File
@@ -124,6 +124,8 @@ set(SRC_FILES
"./src/kaomoji/kaomoji_ime.h"
"./src/log/fanylog.cpp"
"./src/log/fanylog.h"
"./src/log/candidate_diag_log.cpp"
"./src/log/candidate_diag_log.h"
"./src/log/ftb_diag_log.cpp"
"./src/log/ftb_diag_log.h"
"./src/quick-phrases/quick_phrase_query.cpp"
+2
View File
@@ -10,6 +10,8 @@ theme = "dark"
# 通用
#
[general]
# 候选框诊断日志(默认关闭;不记录输入内容或候选文本)
candidate_window_diagnostic_log = false
# 悬浮工具栏
floating_toolbar = true
# 悬浮工具栏组件(中英文切换始终显示)
+17
View File
@@ -1,6 +1,7 @@
#include "ime_config.h"
#include <fmt/xchar.h>
#include <Windows.h>
#include <atomic>
#include <dwrite.h>
#include <wrl/client.h>
#include <winreg.h>
@@ -53,6 +54,7 @@ std::string g_candidate_english_font = "Segoe UI";
std::string g_candidate_default_font = "Microsoft YaHei";
int g_candidate_font_size = 16;
int g_candidate_window_preedit_font_size = 16;
std::atomic_bool g_candidate_window_diagnostic_log_enabled{false};
std::string g_candidate_text_color = "auto";
std::string g_shuangpin_schema = "xiaohe";
std::string g_wubi_schema = "wubi86";
@@ -648,6 +650,8 @@ bool LoadImeConfig()
g_show_quanpin_helpcode_in_candidate_window =
tbl["helpcode"]["show_qp_helpcode_in_candidate_window"].value_or(true);
g_floating_toolbar_enabled = tbl["general"]["floating_toolbar"].value_or(true);
g_candidate_window_diagnostic_log_enabled.store(
tbl["general"]["candidate_window_diagnostic_log"].value_or(false), std::memory_order_relaxed);
g_floating_toolbar_items.fullwidth = tbl["general"]["floating_toolbar_fullwidth"].value_or(true);
g_floating_toolbar_items.punctuation = tbl["general"]["floating_toolbar_punctuation"].value_or(true);
g_floating_toolbar_items.character_set = tbl["general"]["floating_toolbar_character_set"].value_or(true);
@@ -1266,6 +1270,19 @@ bool SetConfiguredCandidateWindowPreeditFontSize(int font_size)
return true;
}
bool GetConfiguredCandidateWindowDiagnosticLogEnabled()
{
return g_candidate_window_diagnostic_log_enabled.load(std::memory_order_relaxed);
}
bool SetConfiguredCandidateWindowDiagnosticLogEnabled(bool enabled)
{
if (!WriteConfiguredValue("general", "candidate_window_diagnostic_log", enabled ? "true" : "false"))
return false;
g_candidate_window_diagnostic_log_enabled.store(enabled, std::memory_order_relaxed);
return true;
}
namespace
{
bool IsValidCandidateTextColor(const std::string &color)
+2
View File
@@ -130,6 +130,8 @@ int GetConfiguredCandidateFontSize();
bool SetConfiguredCandidateFontSize(int font_size);
int GetConfiguredCandidateWindowPreeditFontSize();
bool SetConfiguredCandidateWindowPreeditFontSize(int font_size);
bool GetConfiguredCandidateWindowDiagnosticLogEnabled();
bool SetConfiguredCandidateWindowDiagnosticLogEnabled(bool enabled);
const std::string &GetConfiguredCandidateTextColor();
bool SetConfiguredCandidateTextColor(const std::string &color);
// Installed font family names for settings dropdowns (cached after first call).
+30 -1
View File
@@ -51,6 +51,7 @@
#include "kaomoji/kaomoji_query.h"
#include "kaomoji/kaomoji_ime.h"
#include "jianpin/jianpin_query.h"
#include "log/candidate_diag_log.h"
#include "log/ftb_diag_log.h"
#include "voice-input/voice_input_service.h"
#include <cwchar>
@@ -208,8 +209,11 @@ void RequestShowCandidateWindow()
{
if (IsUiLessMode() || !::global_hwnd)
{
CAND_DIAG_LOGF(L"show request skipped uiless={} hwnd_present={}", IsUiLessMode(), ::global_hwnd != nullptr);
return;
}
CAND_DIAG_LOGF(L"show request posted raw_units={} candidate_count={}",
GlobalIme::composition.raw_input_with_cases.size(), Global::candidate_ui.items.size());
PostMessage(::global_hwnd, WM_SHOW_MAIN_WINDOW, 0, 0);
}
@@ -1021,6 +1025,11 @@ void RefreshCandidatePageUi(bool show_window)
{
::WriteDataToSharedMemory(string_to_wstring(candidate_string), true);
}
CAND_DIAG_LOGF(L"candidate UI refreshed show={} uiless={} items={} page_words={} selected={} page={} "
L"serialized_units={}",
show_window, IsUiLessMode(), Global::candidate_ui.items.size(),
Global::candidate_ui.page_words.size(), Global::candidate_ui.selected_index_in_page,
Global::candidate_ui.page_index, candidate_string.size());
if (show_window)
{
RequestShowCandidateWindow();
@@ -1030,6 +1039,7 @@ void RefreshCandidatePageUi(bool show_window)
void LogPipeConnectResult(const wchar_t *pipe_name, BOOL connected)
{
const DWORD gle = connected ? ERROR_SUCCESS : GetLastError();
CAND_DIAG_LOGF(L"pipe connect name={} connected={} gle={}", pipe_name, connected != FALSE, gle);
if (connected)
{
FANY_IPC_LOGF(L"[msime]: [ipc] {} connected", pipe_name);
@@ -1042,13 +1052,16 @@ void LogPipeConnectResult(const wchar_t *pipe_name, BOOL connected)
void LogPipeReadFailure(const wchar_t *pipe_name, DWORD bytes_read)
{
const DWORD gle = GetLastError();
FANY_IPC_LOGF(L"[msime]: [ipc] {} ReadFile failed or returned empty: gle={}, bytes_read={}", pipe_name,
GetLastError(), bytes_read);
gle, bytes_read);
CAND_DIAG_LOGF(L"pipe read failure name={} gle={} bytes={}", pipe_name, gle, bytes_read);
}
void LogPipeDisconnect(const wchar_t *pipe_name)
{
FANY_IPC_LOGF(L"[msime]: [ipc] {} disconnected", pipe_name);
CAND_DIAG_LOGF(L"pipe disconnected name={}", pipe_name);
}
void LogPipeEvent(const wchar_t *pipe_name, UINT event_type, UINT keycode, WCHAR wch, UINT modifiers_down)
@@ -1391,6 +1404,9 @@ void WorkerThread()
FTB_DIAG_LOGF(L"task {} epoch={} rejected as stale",
task.type == TaskType::ClientDeactivated ? L"ClientDeactivated" : L"ClientSuspended",
task.activation_epoch);
CAND_DIAG_LOGF(L"candidate lifecycle task rejected stale type={} epoch={}",
task.type == TaskType::ClientDeactivated ? L"deactivated" : L"suspended",
task.activation_epoch);
continue;
}
}
@@ -1428,6 +1444,9 @@ void WorkerThread()
// refreshes (creating-word progress and candidate context-menu
// actions), whose current packet does not carry a valid point.
::ReadDataFromNamedPipe(0b111111);
CAND_DIAG_LOGF(L"task ShowCandidate client={} epoch={} request={} caret=({},{}) input_units={}",
task.client_id, task.activation_epoch, task.pipe_data.request_id, Global::Point[0],
Global::Point[1], GlobalIme::composition.raw_input_with_cases.size());
PrepareCandidateList(task.client_id, task.activation_epoch);
RequestShowCandidateWindow();
break;
@@ -1435,6 +1454,8 @@ void WorkerThread()
case TaskType::HideCandidate: {
::ReadDataFromNamedPipe(0b100000);
CAND_DIAG_LOGF(L"task HideCandidate client={} epoch={} request={}", task.client_id,
task.activation_epoch, task.pipe_data.request_id);
PostMessage(::global_hwnd, WM_HIDE_MAIN_WINDOW, 0, 0);
/* 清理状态 */
ClearState();
@@ -1444,6 +1465,8 @@ void WorkerThread()
case TaskType::MoveCandidate: {
static int cnt = 0;
::ReadDataFromNamedPipe(0b001000);
CAND_DIAG_LOGF(L"task MoveCandidate client={} epoch={} caret=({},{})", task.client_id,
task.activation_epoch, Global::Point[0], Global::Point[1]);
PostMessage(::global_hwnd, WM_MOVE_CANDIDATE_WINDOW, 0, 0);
break;
}
@@ -1527,6 +1550,8 @@ void WorkerThread()
}
case TaskType::ClientActivated: {
CAND_DIAG_LOGF(L"client activated client={} epoch={} hwnd_present={}", task.client_id,
task.activation_epoch, ::global_hwnd != nullptr);
VoiceInput::SetImeActive(true);
// Activation replaces all composition/candidate state from the
// previous focus session. A terminal TIP activation also makes
@@ -1564,6 +1589,7 @@ void WorkerThread()
}
case TaskType::ClientDeactivated: {
CAND_DIAG_LOGF(L"client deactivated client={} epoch={}", task.client_id, task.activation_epoch);
VoiceInput::SetImeActive(false);
// Unlike a route-only suspension, terminal TIP deactivation means
// the user switched to another input method.
@@ -1583,6 +1609,7 @@ void WorkerThread()
}
case TaskType::ClientSuspended: {
CAND_DIAG_LOGF(L"client suspended client={} epoch={}", task.client_id, task.activation_epoch);
// A suspension rotates the IPC focus session while the TIP may
// still own thread focus. It clears candidates just like terminal
// deactivation, but never changes floating-toolbar visibility.
@@ -2390,6 +2417,8 @@ void MainPipeClientThread(HANDLE clientPipe, uint64_t handlerId)
{
FANY_IPC_LOGF(L"[msime]: [ipc] ignored inactive main-pipe event: client_id={}, type={}", clientId,
pipeData.event_type);
CAND_DIAG_LOGF(L"main-pipe event ignored inactive client={} type={} request={}", clientId,
pipeData.event_type, pipeData.request_id);
continue;
}
+109
View File
@@ -0,0 +1,109 @@
#include "candidate_diag_log.h"
#include <windows.h>
#include <mutex>
#include <string>
#include "config/ime_config.h"
#include "global/globals.h"
#include "utils/common_utils.h"
namespace
{
constexpr unsigned long long kMaxLogBytes = 4ull * 1024ull * 1024ull;
std::mutex g_log_mutex;
bool g_path_resolved = false;
std::wstring g_log_path;
const std::wstring &ResolveLogPathUnlocked()
{
if (g_path_resolved)
return g_log_path;
g_path_resolved = true;
const std::string local_appdata = CommonUtils::get_local_appdata_path();
if (local_appdata.empty())
return g_log_path;
const std::wstring app_directory = string_to_wstring(local_appdata) + L"\\" + GlobalIme::AppName;
const std::wstring log_directory = app_directory + L"\\logs";
CreateDirectoryW(app_directory.c_str(), nullptr);
if (!CreateDirectoryW(log_directory.c_str(), nullptr) && GetLastError() != ERROR_ALREADY_EXISTS)
return g_log_path;
g_log_path = log_directory + L"\\candidate-window-diagnostic.log";
return g_log_path;
}
void RotateIfOversizedUnlocked(const std::wstring &path)
{
WIN32_FILE_ATTRIBUTE_DATA attributes{};
if (!GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &attributes))
return;
const unsigned long long size =
(static_cast<unsigned long long>(attributes.nFileSizeHigh) << 32) | attributes.nFileSizeLow;
if (size < kMaxLogBytes)
return;
const std::wstring previous = path + L".1";
DeleteFileW(previous.c_str());
MoveFileW(path.c_str(), previous.c_str());
}
std::wstring Timestamp()
{
SYSTEMTIME now{};
GetLocalTime(&now);
wchar_t buffer[40]{};
swprintf_s(buffer, L"%04u-%02u-%02u %02u:%02u:%02u.%03u", static_cast<unsigned>(now.wYear),
static_cast<unsigned>(now.wMonth), static_cast<unsigned>(now.wDay), static_cast<unsigned>(now.wHour),
static_cast<unsigned>(now.wMinute), static_cast<unsigned>(now.wSecond),
static_cast<unsigned>(now.wMilliseconds));
return buffer;
}
} // namespace
namespace CandidateDiag
{
bool IsEnabled()
{
return GetConfiguredCandidateWindowDiagnosticLogEnabled();
}
void Write(const std::wstring &line)
{
try
{
std::lock_guard<std::mutex> lock(g_log_mutex);
const std::wstring &path = ResolveLogPathUnlocked();
if (path.empty())
return;
RotateIfOversizedUnlocked(path);
const std::wstring record = Timestamp() + L" [p" + std::to_wstring(GetCurrentProcessId()) + L":t" +
std::to_wstring(GetCurrentThreadId()) + L"] " + line + L"\r\n";
const std::string utf8 = wstring_to_string(record);
HANDLE file = CreateFileW(path.c_str(), FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (file == INVALID_HANDLE_VALUE)
return;
if (GetLastError() != ERROR_ALREADY_EXISTS)
{
const char bom[] = "\xEF\xBB\xBF";
DWORD written = 0;
WriteFile(file, bom, 3, &written, nullptr);
}
DWORD written = 0;
WriteFile(file, utf8.data(), static_cast<DWORD>(utf8.size()), &written, nullptr);
CloseHandle(file);
}
catch (...)
{
// Diagnostics must never affect the input path.
}
}
} // namespace CandidateDiag
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <string>
#include "fmt/xchar.h"
// Diagnostic trace for candidate-window disappearance investigations. The
// trace deliberately records only state, counts, identifiers, coordinates and
// error codes; user input and candidate text must never be written here.
namespace CandidateDiag
{
// When disabled, call sites pay for one relaxed atomic load and do not format
// strings or touch the filesystem.
bool IsEnabled();
// Appends one timestamped line. Thread-safe, bounded by rotation, never throws.
void Write(const std::wstring &line);
} // namespace CandidateDiag
#define CAND_DIAG_LOGF(...) \
do \
{ \
if (::CandidateDiag::IsEnabled()) \
{ \
::CandidateDiag::Write(fmt::format(__VA_ARGS__)); \
} \
} while (0)
+4 -1
View File
@@ -237,7 +237,8 @@ void PostConfig()
{"smart_punctuation_repeat_to_chinese", GetConfiguredSmartPunctuationRepeatToChineseEnabled()},
{"paired_punctuation", GetConfiguredPairedPunctuationEnabled()}}},
{"general",
{{"floating_toolbar", GetConfiguredFloatingToolbarEnabled()},
{{"candidate_window_diagnostic_log", GetConfiguredCandidateWindowDiagnosticLogEnabled()},
{"floating_toolbar", GetConfiguredFloatingToolbarEnabled()},
{"floating_toolbar_fullwidth", toolbar.fullwidth},
{"floating_toolbar_punctuation", toolbar.punctuation},
{"floating_toolbar_character_set", toolbar.character_set},
@@ -433,6 +434,8 @@ bool ApplyConfigUpdate(const json::object &data)
return SetConfiguredThemeVoice(json::value_to<std::string>(data.at("value")));
if (path == "general.floating_toolbar")
return SetConfiguredFloatingToolbarEnabled(json::value_to<bool>(data.at("value")));
if (path == "general.candidate_window_diagnostic_log")
return SetConfiguredCandidateWindowDiagnosticLogEnabled(json::value_to<bool>(data.at("value")));
if (path == "general.floating_toolbar_scale")
{
const json::value &value = data.at("value");
+40 -3
View File
@@ -18,6 +18,7 @@
#include "ipc/ipc.h"
#include "ipc/event_listener.h"
#include "ipc/candidate_ui_action_policy.h"
#include "log/candidate_diag_log.h"
#include "log/ftb_diag_log.h"
#include "settings/settings_launcher.h"
#include "utils/window_utils.h"
@@ -961,6 +962,11 @@ bool AreSmallWindowWebviewsReady()
return AreSmallWindowWebviewsReadyUnlocked();
}
bool IsCandidateWebviewReady()
{
return candidateNavigationReady && webviewCandWnd != nullptr && webviewControllerCandWnd != nullptr;
}
bool IsFloatingToolbarWebviewReady()
{
return floatingToolbarNavigationReady && webviewControllerFtbWnd != nullptr;
@@ -1085,6 +1091,18 @@ void UpdateSmallWindowWebviewVisibility(HWND hwnd, bool visible)
}
}
bool GetCandidateWebviewState(bool &isVisible, RECT &bounds)
{
if (!webviewControllerCandWnd)
return false;
BOOL visible = FALSE;
if (FAILED(webviewControllerCandWnd->get_IsVisible(&visible)) ||
FAILED(webviewControllerCandWnd->get_Bounds(&bounds)))
return false;
isVisible = visible != FALSE;
return true;
}
std::wstring ReadHtmlFile(const std::wstring &filePath)
{
std::wifstream file(filePath);
@@ -1606,6 +1624,8 @@ HRESULT OnControllerCreatedCandWnd( //
ICoreWebView2Controller *controller //
)
{
CAND_DIAG_LOGF(L"webview controller callback hr={:#x} controller_present={} hwnd_valid={}",
static_cast<unsigned>(result), controller != nullptr, IsWindow(hwnd) != FALSE);
if (!controller || FAILED(result))
{
OnSmallWindowControllerSettled(FAILED(result) ? result : E_FAIL);
@@ -1617,6 +1637,7 @@ HRESULT OnControllerCreatedCandWnd( //
if (!webviewCandWnd)
{
CAND_DIAG_LOGF(L"webview get_CoreWebView2 failed hr={:#x}", static_cast<unsigned>(getWebviewHr));
webviewControllerCandWnd.Reset();
OnSmallWindowControllerSettled(FAILED(getWebviewHr) ? getWebviewHr : E_FAIL);
return E_FAIL;
@@ -1696,6 +1717,8 @@ HRESULT OnControllerCreatedCandWnd( //
// Navigate to HTML
HRESULT hr = webviewCandWnd->NavigateToString(HTMLStringCandWnd.c_str());
CAND_DIAG_LOGF(L"webview NavigateToString hr={:#x} html_chars={} skin={}", static_cast<unsigned>(hr),
HTMLStringCandWnd.size(), string_to_wstring(preparedCandidateSkin));
if (SUCCEEDED(hr))
{
loadedCandidateSkin = preparedCandidateSkin;
@@ -1847,7 +1870,10 @@ HRESULT OnControllerCreatedCandWnd( //
COREWEBVIEW2_WEB_ERROR_STATUS errorStatus = COREWEBVIEW2_WEB_ERROR_STATUS_UNKNOWN;
const HRESULT successHr = args->get_IsSuccess(&success);
const HRESULT statusHr = args->get_WebErrorStatus(&errorStatus);
(void)0;
CAND_DIAG_LOGF(L"webview navigation completed success={} success_hr={:#x} status_hr={:#x} "
L"web_error={} logical_shown={}",
success != FALSE, static_cast<unsigned>(successHr), static_cast<unsigned>(statusHr),
static_cast<int>(errorStatus), ::is_global_wnd_cand_shown);
if (success)
{
NotifySmallWindowNavigationReady(candidateNavigationReady, L"candidate");
@@ -1882,7 +1908,8 @@ HRESULT OnControllerCreatedCandWnd( //
COREWEBVIEW2_PROCESS_FAILED_KIND kind =
COREWEBVIEW2_PROCESS_FAILED_KIND_BROWSER_PROCESS_EXITED;
const HRESULT hr = args->get_ProcessFailedKind(&kind);
(void)0;
CAND_DIAG_LOGF(L"webview process failed kind={} hr={:#x}",
static_cast<int>(kind), static_cast<unsigned>(hr));
return S_OK;
})
.Get(),
@@ -2817,6 +2844,15 @@ HRESULT OnControllerCreatedSettingsWnd( //
PostSettingsConfig();
}
}
else if (path == "general.candidate_window_diagnostic_log")
{
const bool value = json::value_to<bool>(data.at("value"));
if (SetConfiguredCandidateWindowDiagnosticLogEnabled(value))
{
CAND_DIAG_LOGF(L"diagnostic logging enabled from Settings");
PostSettingsConfig();
}
}
else if (path.rfind("tencent_tmt.", 0) == 0)
{
const std::string value = json::value_to<std::string>(data.at("value"));
@@ -3167,7 +3203,8 @@ void PostSettingsConfig()
{"smart_punctuation_repeat_to_chinese", GetConfiguredSmartPunctuationRepeatToChineseEnabled()},
{"paired_punctuation", GetConfiguredPairedPunctuationEnabled()}}},
{"general",
{{"floating_toolbar", GetConfiguredFloatingToolbarEnabled()},
{{"candidate_window_diagnostic_log", GetConfiguredCandidateWindowDiagnosticLogEnabled()},
{"floating_toolbar", GetConfiguredFloatingToolbarEnabled()},
{"floating_toolbar_fullwidth", toolbar.fullwidth},
{"floating_toolbar_punctuation", toolbar.punctuation},
{"floating_toolbar_character_set", toolbar.character_set},
+2
View File
@@ -99,6 +99,7 @@ bool EnsureSmallWindowsTopmost(const wchar_t *reason);
// after the WebViews report ready.
bool AreSmallWindowsTopmostApplied();
bool AreSmallWindowWebviewsReady();
bool IsCandidateWebviewReady();
// True once the floating toolbar's WebView2 has completed its first navigation
// and can actually paint. Its host must stay on-monitor and "visible" until
// then, otherwise WebView2 never finishes raster setup for it.
@@ -122,6 +123,7 @@ bool GetFloatingToolbarWebviewState(bool &isVisible, RECT &bounds);
// Same for the tray menu, whose blank-but-clickable failure mode needs exactly
// this to be separated from a host that was never uncloaked.
bool GetTrayMenuWebviewState(bool &isVisible, RECT &bounds);
bool GetCandidateWebviewState(bool &isVisible, RECT &bounds);
void LogSmallWindowReadyGate(const wchar_t *context);
// Lift the tray menu to the front of the small-window topmost band (e.g. after
// FTB was pinned last), renotifying its controller about the band change. Only
+45 -3
View File
@@ -22,6 +22,7 @@
#include "utils/ime_utils.h"
#include "window_hook.h"
#include "window/floating_toolbar_visibility_policy.h"
#include "log/candidate_diag_log.h"
#include "log/ftb_diag_log.h"
#include "voice-input/voice_input_service.h"
#include <windowsx.h>
@@ -652,6 +653,16 @@ std::wstring DescribeFloatingToolbarHostState()
}
} // namespace
std::wstring DescribeCandidateHostState()
{
bool webview_visible = false;
RECT webview_bounds{};
const bool has_controller = GetCandidateWebviewState(webview_visible, webview_bounds);
return fmt::format(L"logical_shown={} nav_ready={} {}", ::is_global_wnd_cand_shown,
IsCandidateWebviewReady(),
DescribeHostWindowState(::global_hwnd, has_controller, webview_visible, webview_bounds));
}
// External linkage, unlike its toolbar counterpart: the menu's own lifecycle
// events live in windows_webview2.cpp and are the ones worth recording.
std::wstring DescribeTrayMenuHostState()
@@ -1052,6 +1063,7 @@ int CreateCandidateWindow(HINSTANCE hInstance)
}
::global_hwnd = hwnd_cand;
CAND_DIAG_LOGF(L"candidate host created {}", DescribeCandidateHostState());
// A client can activate while the pipe server is already listening but this
// window does not exist yet, which is the race that leaves the toolbar
@@ -1205,6 +1217,11 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
if (message == WM_SHOWWINDOW)
{
UpdateSmallWindowWebviewVisibility(hwnd, wParam != FALSE);
if (hwnd == ::global_hwnd)
{
CAND_DIAG_LOGF(L"host WM_SHOWWINDOW showing={} reason={} {}", wParam != FALSE,
static_cast<unsigned long long>(lParam), DescribeCandidateHostState());
}
}
if (message == WM_APPLY_IME_CONFIG || message == WM_APPLY_IME_INPUT_SCHEME)
@@ -1239,6 +1256,14 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
LRESULT CALLBACK WndProcCandWindow(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_POWERBROADCAST || message == WM_DISPLAYCHANGE || message == WM_DWMCOMPOSITIONCHANGED ||
message == WM_SETTINGCHANGE)
{
CAND_DIAG_LOGF(L"system window event message={:#x} wparam={:#x} lparam={:#x} {}", message,
static_cast<unsigned long long>(wParam), static_cast<unsigned long long>(lParam),
DescribeCandidateHostState());
}
if (message == WM_IMEACTIVATE)
{
g_is_ime_active = true;
@@ -1262,6 +1287,9 @@ LRESULT CALLBACK WndProcCandWindow(HWND hwnd, UINT message, WPARAM wParam, LPARA
std::wstring str = preedit + L"," + Global::CandidateString;
(void)0;
LogSmallWindowReadyGate(L"show-candidate");
CAND_DIAG_LOGF(L"show message preedit_units={} payload_units={} caret=({},{}) {}", preedit.size(),
Global::CandidateString.size(), Global::Point[0], Global::Point[1],
DescribeCandidateHostState());
if (!EnsureSmallWindowsTopmost(L"show-candidate"))
{
(void)0;
@@ -1284,6 +1312,7 @@ LRESULT CALLBACK WndProcCandWindow(HWND hwnd, UINT message, WPARAM wParam, LPARA
if (message == WM_HIDE_MAIN_WINDOW)
{
CAND_DIAG_LOGF(L"hide message begin {}", DescribeCandidateHostState());
(void)0;
// Clear first so any FineTuneWindow callback already queued bails out.
::is_global_wnd_cand_shown = false;
@@ -1312,6 +1341,7 @@ LRESULT CALLBACK WndProcCandWindow(HWND hwnd, UINT message, WPARAM wParam, LPARA
}
SetHostWindowCloaked(hwnd, true);
UpdateHtmlContentWithJavaScript(webviewCandWnd, L"");
CAND_DIAG_LOGF(L"hide message end {}", DescribeCandidateHostState());
/* 候选词部分使用全角空格来占位 */
// std::wstring str = L" , , , , , , , , ";
// InflateCandWnd(str);
@@ -1321,6 +1351,8 @@ LRESULT CALLBACK WndProcCandWindow(HWND hwnd, UINT message, WPARAM wParam, LPARA
if (message == WM_MOVE_CANDIDATE_WINDOW)
{
CAND_DIAG_LOGF(L"move message caret=({},{}) {}", Global::Point[0], Global::Point[1],
DescribeCandidateHostState());
FineTuneWindow(hwnd);
return 0;
}
@@ -2448,9 +2480,12 @@ int FineTuneWindow(HWND hwnd)
{
(void)0;
LogSmallWindowReadyGate(L"fine-tune-no-webview");
CAND_DIAG_LOGF(L"fine-tune skipped: no webview {}", DescribeCandidateHostState());
return 0;
}
const uint64_t generation = ++g_candidate_finetune_generation;
CAND_DIAG_LOGF(L"fine-tune begin generation={} caret=({},{}) {}", generation, caretX, caretY,
DescribeCandidateHostState());
// Wrap/scroll budget = stable host size (half-screen DIP). Width wraps;
// height scrolls inside the card when fonts make the list taller than host.
const HalfScreenDipLimits measureLimits = QueryHalfScreenDipLimitsForPoint(caretPt);
@@ -2477,12 +2512,15 @@ int FineTuneWindow(HWND hwnd)
// measure callback was still pending — do not resurrect it.
if (!::is_global_wnd_cand_shown || caretY == Global::INVALID_Y)
{
(void)0;
CAND_DIAG_LOGF(L"fine-tune generation={} discarded: hidden={} invalid_caret={}", generation,
!::is_global_wnd_cand_shown, caretY == Global::INVALID_Y);
return;
}
// A newer show/update already queued another FineTune — ignore this one.
if (generation != g_candidate_finetune_generation.load())
{
CAND_DIAG_LOGF(L"fine-tune generation={} discarded: superseded_by={}", generation,
g_candidate_finetune_generation.load());
return;
}
@@ -2694,8 +2732,12 @@ int FineTuneWindow(HWND hwnd)
UpdateSmallWindowWebviewVisibility(hwnd, true);
RECT actualRect{};
GetWindowRect(hwnd, &actualRect);
(void)actualRect;
(void)0;
CAND_DIAG_LOGF(L"fine-tune generation={} completed size_dip=({:.1f},{:.1f}) "
L"margin=({},{}) rect=({},{},{}x{}) {}",
generation, finalSize.first, finalSize.second, Global::MarginLeft,
Global::MarginTop, actualRect.left, actualRect.top,
actualRect.right - actualRect.left, actualRect.bottom - actualRect.top,
DescribeCandidateHostState());
};
// Pass 2: remeasure the painted card after margins. Keep the stable