fix(m182): 非 Windows 圆角判据回答「由谁负责」,并给 macOS 直角三条源码级证据

用户第三轮 Mac 验收看到四角直角 + `source=square os=unknown`。原来非 Windows 一律
落到 `UnknownOs`,把「Windows 版本读不出来」与「这台根本不是 Windows」糊成同一格。

- `platform/host.rs`:新增 `HostOs`——平台族是一个**值**而不是散落的 `cfg`。
  这样三行真值表能在本机(只有 Windows)一次验完,`cfg` 只留在真要 FFI 的地方。
- `plan_corner` 拆成 `plan_corner_for(policy, host, os, backend)` + 保持原签名的薄包装
  (`app/update/appearance.rs` 在 scope 外,调用点一字未改)。
- `CornerSource` 加第三档 `Compositor`(跟随合成器,我们既不下发也不自绘);
  `CornerExclusion` 加 `borderless`(macOS)、`client-side`(Linux)、`unsupported`。
- 日志加 `host=`,非 Windows 的 `os=` 由 `unknown` 改 `n/a`(那栏是 Windows 版本,
  在 macOS/Linux 上不是「没读到」而是「跟本机无关」)。
- `platform/macos/mod.rs`:macOS 为什么是直角的**三环证据**(我们的 `decorations: false`
  → winit `Borderless`(无 `Titled`)→ 窗口服务器只裁 titled 窗;winit macOS 后端
  `cornerRadius` 命中 0),以及「迁移原生标题栏」的未实现配方与阻塞点(iced 0.14 未转发
  winit 的 `titlebar_buttons_hidden`,缩放键按不掉)。
- `platform/linux/mod.rs`:xdg-shell 无圆角概念、wl_surface 区域只能是矩形、XShape 只能
  切硬边 ⇒ CSD 窗没人替我们裁,D46 排除自绘 ⇒ 直角。没把 DWM 判据搬过去(单测钉着
  「非 Windows 不许出现 dwm」)。
- `design/shape.rs`:删掉「macOS 走 `layer.cornerRadius`」这句过期描述(D46 不许自绘裁切)。
- `dwm.rs` 的内联测试挪到 `dwm/tests.rs`(原文件 398 行,加第三档映射会顶破 400 行红线)。

验证:cargo fmt --check / clippy -D warnings / `cargo test -p unlucky-app-iced --lib`
684 passed;三次定向变异各自变红(macOS 行改回 Square/UnknownOs → 4 红;非 Windows
`os=` 改回 unknown → 1 红;Linux 原因码复制成 borderless → 4 红)。
Windows 侧无语义改动;`cargo test --lib m116 -- --ignored --nocapture` 复跑读回仍是
`pref=2 readback=? read_hr=0x80070057`(写入被接受、回读不可用),**不作为「圆角已生效」的证据**。
This commit is contained in:
AskaEth committed 2026-11-08 14:50:15 +08:00
1 parent 2a004a5754
commit 0451b3f972
11 files changed
+793 -192

No files matched your search

+93
View File
@@ -0,0 +1,93 @@
//! macOS 的窗口外框(M182):**本轮不实现**,这里只有结论、证据与后续配方。
//!
//! 用户第三轮 Mac 真机验收的现象:四角是直角,日志 `[corner] … source=square os=unknown`。
//! 「macOS 不支持圆角」是**错的**——macOS 的窗口四角本来就有系统圆角,只是我们交上去的窗口
//! 不在会被裁的那一类里。三环链条如下,每一环都能在本机复核(读锁文件里的依赖源码即可):
//!
//! ## 第 1 环:我们交的是什么窗
//!
//! `crates/app-iced/src/app/boot/diagnostics.rs` 的 `window_settings()` 给的是
//! `decorations: false`(M93 自绘三键)。那是**唯一产地**,`[window]` / `[caps]` 两行
//! 验收日志也从它取字段。
//!
//! ## 第 2 环:winit 把 `decorations: false` 翻成什么
//!
//! winit 0.30.13 `src/platform_impl/macos/window_delegate.rs:540-552`:
//!
//! ```text
//! let mut masks = if (!attrs.decorations && screen.is_none()) || titlebar_hidden {
//! // Resizable without a titlebar or borders
//! NSWindowStyleMask::Borderless | NSWindowStyleMask::Resizable | NSWindowStyleMask::Miniaturizable
//! } else {
//! // default case, resizable window with titlebar and titlebar buttons
//! NSWindowStyleMask::Closable | Miniaturizable | Resizable | Titled
//! };
//! ```
//!
//! ⇒ `decorations: false` 拿到的是 **`Borderless`**,**没有 `Titled`**。
//!
//! ## 第 3 环:窗口服务器只给 `Titled` 窗做圆角裁剪
//!
//! 这是 AppKit 的标准窗口行为(A 级外部事实:标准窗口的圆角由窗口服务器连同阴影一起画,
//! 无边框窗口不参与)。本仓库**没有**任何一行代码能影响它:winit 的 macOS 后端里
//! `cornerRadius` / `CornerRadius` 命中数为 **0**(`src/platform_impl/macos/` 内四命中全是
//! 「屏幕/视图左上角」的注释),也就是说圆角从来没有一行业是我们调的。
//!
//! 反向印证:用户第三轮实测(本机窗口就是 borderless)拍到四角直角,与第 3 环一致。
//!
//! 判据一侧的落点:`plan_corner_for(MacOs, …)` 现在返回
//! `source=compositor excluded_reason=borderless`(见 [`crate::platform::win32::corner`]),
//! 而不是原先含糊的 `source=square os=unknown`。
//!
//! ## 为什么**不**用 objc 给 `contentView.layer` 设 `cornerRadius`(M182 任务书原路线,已否)
//!
//! 1. **违反 D46 第 4 条**「macOS / X11 / Wayland 按合成器默认、**不主动自绘**」:
//! 把内容层裁成圆角就是自绘裁切,同一份决策日志里已经否决过这条(还附带
//! 「会把标题栏三键与边缘缩放热区一起裁掉」的问题)。
//! 2. **撞 `unsafe` 铁律**:根 `Cargo.toml` 的 `[workspace.lints.rust] unsafe_code = "deny"`,
//! 且 `guards.yml` 的 `unsafe-scope` 作业要求「带 `allow(unsafe_code)` 的文件集合**恰好等于**
//! `crates/app-iced/src/platform/win32/transparency.rs`」——新增一个 objc FFI 文件会让该作业
//! 直接红,而那个工作流不在 M182 的改动范围内。
//! 3. **拿不到 `NSWindow`**:`app/window_alpha.rs` 的 `probe_hwnd` 在非 Windows 恒返回 `None`,
//! 于是 `App::apply_window_corner` 走 `NoWindow` 分支——macOS 上那条下发链**从来没被调用过**。
//! 要接线得改 `app/window_alpha.rs` + `app/update.rs`(M182 范围之外,且 M168/M178 正在改
//! `app/**`)。
//! 4. **本机编不了**:macOS 分支要靠 macOS 工具链,Windows 上连类型检查都跑不到。
//!
//! ## 后续口径(**未实现**,需另立 mission + Mac 真机第 4 轮验收)
//!
//! 目标:把外框交还给窗口服务器当标准窗口,由它按自己的规则圆角(= 用户定的
//! 「其他系统按照窗口合成器的默认规则来」):
//!
//! ```text
//! decorations: true,
//! platform_specific: { title_hidden: true, titlebar_transparent: true, fullsize_content_view: true }
//! ```
//!
//! - 三个字段 iced 0.14 已暴露(`iced_core-0.14.0/src/window/settings/macos.rs`),
//! iced_winit 1:1 转发(`iced_winit-0.14.1/src/conversion.rs:125-137` 的
//! `#[cfg(target_os = "macos")]` 块);
//! `FullSizeContentView` 让内容铺满整窗(不留原生标题栏高度),窗口服务器把整窗
//! ——**连同我们的背景图**——裁成圆角,不会出现「外框圆了、里面还是方块」。
//! - **必须同时隐藏我们自绘的三键**(`crates/app-iced/src/shell/mod.rs`),否则屏幕上会同时存在
//! 左上角原生三色灯与右上角自绘三键两套控件——那不是「跟随合成器」,是两套控件打架。
//! - **已知阻塞点(真做之前必须先解掉)**:winit 有 `with_titlebar_buttons_hidden`
//! (`src/platform/macos.rs:290`,能一次藏掉全部三色灯),但 **iced 0.14 的
//! `PlatformSpecific` 没有转发它**;而 `WindowExtMacOS` 也没有运行期隐藏按钮的接口
//! (只有 `set_has_shadow` / `set_document_edited` 等)。三条出路:① 给 iced 上游提转发
//! (最干净,但要等版本);② 仅用 `closeable: false, minimizable: false` 藏掉关闭/最小化
//! 两键——**缩放键藏不掉**,会留一个孤零零的绿灯;③ 拿到 `NSWindow` 后手动 `setHidden:`
//! (回到 objc + `unsafe`,见上面的第 2、3 条)。
//! - **验收日志会一起变**:`renderer::acceptance::visual` 从 `decorations` 推
//! `controls=self-drawn|native`,一旦改成 `true`,那行会写 `controls=native`,而我们当时
//! 仍在自绘三键(除非同一次改动把三键撤掉)⇒ 那条改动必须**同一个 mission 内**一起做。
//! - **验证路径**:本机(Windows)拿不到任何 macOS 像素;只能靠
//! `ci.yml` 的 `runtime-smoke-macos` 作业(dispatch 触发、`continue-on-error`)看**编不编得过**,
//! 视觉效果必须等**第 4 轮 Mac 真机验收图**或用户手动确认。
//!
//! ## 本轮明确未验
//!
//! - macOS 圆的半径、平滑度、与系统其它窗口是否一致(无真机);
//! - 高 DPI(Retina)下系统圆角的视觉半径(由系统决定,我们读不到,也不需要缩放——这一档
//! 本来就「不归我们管」,见 [`crate::platform::win32::corner`] 的 `radius=system`);
//! - 原生三色灯的最终观感与「是否保留自绘三键」的产品取舍。