use serde_json::{Value, json};
thread_local! {
static OBSERVATION_TARGET: std::cell::RefCell<Option<String>> =
const { std::cell::RefCell::new(None) };
}
pub fn set_observation_target(title: &str) {
OBSERVATION_TARGET.with(|t| *t.borrow_mut() = Some(title.to_string()));
}
pub fn failure_context() -> Value {
let foreground = foreground_window();
let title = foreground
.as_ref()
.map(|(t, _)| t.as_str())
.unwrap_or_default();
let target = OBSERVATION_TARGET
.with(|t| t.borrow().clone())
.unwrap_or_default();
json!({
"foreground_window": {
"title": title,
"class": foreground.as_ref().map(|(_, c)| c.as_str()).unwrap_or(""),
"flags": classify_title(title),
},
"observation_target": {
"title": target,
"flags": classify_title(&target),
},
})
}
fn classify_title(title: &str) -> Vec<&'static str> {
let mut flags = Vec::new();
if title.contains("兼容模式") || title.contains("Compatibility Mode") {
flags.push("compat_mode");
}
if title.starts_with('*') {
flags.push("dirty_buffer");
}
if flags.is_empty() {
flags.push("none");
}
flags
}
fn foreground_window() -> Option<(String, String)> {
use windows::Win32::UI::WindowsAndMessaging::GetForegroundWindow;
let hwnd = unsafe { GetForegroundWindow() };
if hwnd.0.is_null() {
return None;
}
let title = window_text(hwnd.0 as isize)?;
let class = window_class(hwnd.0 as isize)?;
Some((title, class))
}
fn window_text(hwnd: isize) -> Option<String> {
use windows::Win32::UI::WindowsAndMessaging::GetWindowTextLengthW;
let hwnd = windows::Win32::Foundation::HWND(hwnd as *mut _);
let len = unsafe { GetWindowTextLengthW(hwnd) };
if len <= 0 {
return Some(String::new());
}
let mut buf = vec![0u16; len as usize + 1];
let copied = unsafe { windows::Win32::UI::WindowsAndMessaging::GetWindowTextW(hwnd, &mut buf) };
if copied <= 0 {
return Some(String::new());
}
Some(String::from_utf16_lossy(&buf[..copied as usize]))
}
fn window_class(hwnd: isize) -> Option<String> {
let mut buf = [0u16; 256];
let copied = unsafe {
windows::Win32::UI::WindowsAndMessaging::GetClassNameW(
windows::Win32::Foundation::HWND(hwnd as *mut _),
&mut buf,
)
};
if copied <= 0 {
return None;
}
Some(String::from_utf16_lossy(&buf[..copied as usize]))
}