Files
Unlucky-Launcher/crates/app-iced/src/platform/win32/transparency/exstyle.rs
T
AskaEth 5046f96f7e
CI / runtime smoke (macos arm64, probe) (push) Skipped
CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
CI / runtime smoke (ubuntu x64, xvfb) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好一个文件) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Unlucky Launcher v0.2.1
2026-11-06 20:47:19 +08:00

108 lines
5.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 取证用的**扩展样式探针**(M100 r1 P2-2):这个窗口是不是「点击穿透」。
//!
//! ## 本文件零 `unsafe`、零 `extern`
//!
//! 全仓 `unsafe` 只允许出现在 [`super`](`transparency.rs`)一个文件里,判据是
//! 「**带放行属性**的文件集合恰好等于它」。所以探针需要的 FFI 一律由 `transparency.rs` 提供
//! **安全包装**([`super::window_exists`] / [`super::raw_ex_style`]),本文件只做纯判断、
//! 三态映射与测试。
//!
//! ⚠️ **别把 FFI 写回来**(M100 r1 复审实测抓过一次):lint 级别沿模块树继承,所以在这里写
//! 一个 `unsafe` 块 + `extern` 声明也**编得过**,`unsafe-scope` 门禁也照样绿(它只数「带放行
//! 属性的文件」)——但「全仓唯一允许 unsafe 的文件」这个性质就破了。
//! 属性的作用是可审计性,不是给 CI 看的。
//!
//! ## 判据来源
//!
//! winit 0.30 在 Windows 把 `set_cursor_hittest(false)` 落成
//! `WS_EX_TRANSPARENT | WS_EX_LAYERED`(`window_state.rs`)。所以读回这一位,就是
//! 「取证窗口真的把命中测试关掉了」的 OS 侧证据——比「出图 hash 不变」有判别力得多
//! (实测:同 argv 开/关穿透,hash 完全相同)。
use super::raw_ex_style;
/// `WS_EX_TRANSPARENT`:**点击穿透**位(`0x0000_0020`)。
pub const WS_EX_TRANSPARENT: isize = 0x0000_0020;
/// 纯判据:扩展样式里 [`WS_EX_TRANSPARENT`] 在不在。
///
/// 单独拎成 `const fn` 是为了让「位判断写反」有断言盯着——`& WS_EX_TRANSPARENT != 0` 写成
/// `== 0` 是这类 getter 最经典的错,而且只会在「开/关恰好反向」时暴露(两边都"有输出")。
#[must_use]
pub const fn is_click_through_style(ex_style: isize) -> bool {
ex_style & WS_EX_TRANSPARENT != 0
}
/// 取证用:窗口**现在**是否点击穿透(`WS_EX_TRANSPARENT` 在扩展样式里)。
///
/// ## 为什么是三态而不是 `bool`
///
/// `None` = **没读到**(非 Windows / 句柄无效 / 句柄还没拿到)。这时调用方必须记 `unknown`,
/// **不许记成 `off`**——「拿不到」和「没有」是两件事,混为一谈就是假绿(本项目在别处栽过:
/// 扫描读不全时报「没有」)。所以这里宁可 `None`,也不用「读失败的 0」冒充「没这一位」。
///
/// ## 为什么本文件零 `unsafe`、零 `extern`
///
/// `unsafe` 全仓只在 [`super`](`transparency.rs`)那一个文件里。探针需要的 FFI
/// (`IsWindow` / `GetWindowLongPtrW`)都由 `super` 提供安全包装
/// ([`super::window_exists`] / [`super::raw_ex_style`]),本文件只做**纯判断**。
///
/// ## 用途
///
/// 出图时把它打进日志(`pages::harness` 的 `[caps] passthrough_ext=…`),于是「取证窗口到底
/// 有没有把指针输入关掉」每次运行都能自证,不依赖外部探针。
#[cfg(target_os = "windows")]
#[must_use]
pub fn click_through(hwnd: isize) -> Option<bool> {
if hwnd == 0 || !super::window_exists(hwnd) {
return None;
}
Some(is_click_through_style(raw_ex_style(hwnd)))
}
/// 非 Windows:**恒 `None`**。空实现不许冒充「没开穿透」(那就是把「不知道」写成「没有」)。
#[cfg(not(target_os = "windows"))]
#[must_use]
pub fn click_through(_hwnd: isize) -> Option<bool> {
None
}
#[cfg(test)]
mod tests {
use super::{click_through, is_click_through_style, WS_EX_TRANSPARENT};
use crate::platform::win32::transparency::WS_EX_LAYERED;
/// **抓得住**:`& WS_EX_TRANSPARENT != 0` 被写成 `== 0`(位判断反向)——这是这类 getter 最经典
/// 的错,而且只会在「开/关恰好互换」时暴露:日志里永远有值,只是意思反了。
/// **抓不住**:真实窗口上 winit 到底置没置这一位(那要出图日志里看 `passthrough_ext=on`)。
#[test]
fn the_click_through_bit_cannot_be_read_backwards() {
assert_eq!(WS_EX_TRANSPARENT, 0x0000_0020, "位值照 Win32 头文件");
assert!(is_click_through_style(WS_EX_TRANSPARENT));
assert!(
is_click_through_style(WS_EX_TRANSPARENT | WS_EX_LAYERED),
"winit 的鼠标穿透是两位一起置的,这种组合必须是 true"
);
assert!(
!is_click_through_style(WS_EX_LAYERED),
"只有分层位不算穿透——LWA_ALPHA 也加这一位,反向了会把正常窗口误判成穿透"
);
assert!(!is_click_through_style(0));
}
/// **抓得住**:「拿不到」被记成「没有」。句柄 0 / 无效句柄 / 非 Windows 必须是 `None`
/// (调用方据此写 `unknown`),不许用「读失败的 0」冒充「这一位没开」。
/// **抓不住**:一个**有效**窗口上的读数(那要真窗口,见 harness 的出图日志)。
#[test]
fn click_through_reports_unknown_instead_of_off_when_it_cannot_read() {
assert_eq!(click_through(0), None, "句柄 0 = 还没拿到窗口 ⇒ unknown");
assert_eq!(click_through(1), None, "无效句柄 ⇒ unknown(不许掉成 off)");
#[cfg(not(target_os = "windows"))]
assert_eq!(
click_through(0x1234),
None,
"非 Windows 的空实现必须恒 None,不许冒充 off"
);
}
}