Commit Graph
6 Commits
Author SHA1 Message Date
AskaEth 0451b3f972 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`(写入被接受、回读不可用),**不作为「圆角已生效」的证据**。
2026-11-08 14:50:15 +08:00
AskaEth 8bfabafe8a fix(app-iced): M147 修掉 Linux 侧 clippy 的 14 条错与写死平台的 α 断言
两条红的成因都是「Windows-only 的东西没有平台门」:Linux + `--all-targets` + `-D warnings`
三件事凑齐时,Windows 专有的 DWM/穿透代码在 Linux 上变成未用导入 + 死代码。

clippy(14 条错,docker 内 exit 101 → 0):
- dwm.rs `set_preference` 的 `#[cfg(not(windows))]` 分支以 `return` 结尾 ⇒ needless_return;
  那个块本来就是该 cfg 下函数体的尾巴,去掉 `return`。
- dwm.rs 三常量 + 四读函数在非 Windows 上没有使用者(唯一调用方 `probe::run_one` 的
  Windows 版被门着,`transparency.rs` 只 re-export 三项)⇒ dead_code。用**模块级**
  `#![cfg_attr(not(target_os = "windows"), allow(dead_code))]`:
  · 逐项 `cfg(target_os = "windows")` 会把它们从 Linux 构建里删掉,本文件那几条与平台
    无关的单测(属性号照 Win32 头文件、无效句柄不许表现成成功)就连编译都过不了;
  · 逐项 `cfg_attr` 要多 7 行,本文件 394 行 → 会顶破 guards.yml 的 400 行红线(现 398 行)。
  代价写明在文件头:非 Windows 上本模块的 dead_code 不再报。
- exstyle.rs `use super::raw_ex_style`:唯一使用者 `click_through` 自己按平台门着 ⇒ 补 cfg。
- probe.rs 的 `use super::dwm::{…}`:`CornerOutcome` 与平台无关(`ProbeRow` 字段 + 非 Windows
  的 `empty_row`),其余九个符号只服务于 Windows 的 `run_one` ⇒ 拆成两条 use。
- window_alpha.rs `use …RawWindowHandle`:只有 `RawWindowHandle::Win32` 那条臂用它 ⇒ 补 cfg。

test(docker 内 622 passed / 1 failed → 623 passed / 0 failed):
`opaque_or_refused_alpha_never_touches_the_window_style` 里「无效句柄 ⇒ Failed」是按 Windows
语义写死的;非 Windows 走 `transparency::set_layered` 的空实现,如实返回 `Unsupported`。
按平台拆成两条断言(Windows 断 `Failed`,其它断 `Unsupported`),两边都不许表现成
`Applied`/`Opaque`。顺带说明:`dispatch` 的 `layered=false` 那半条断言与平台无关,保留在
两条之外照跑。

验证(本机 Windows + Docker Debian 12,命令与 ci.yml 逐字一致):
- docker `cargo clippy --workspace --all-targets -- -D warnings` → exit 0
- docker `cargo test -p unlucky-app-iced --lib` → 623 passed / 0 failed
- Windows `cargo check -p unlucky-app-iced --all-targets` → 无 error/warning
- Windows `cargo test -p unlucky-app-iced --lib` → 624 passed / 0 failed / 2 ignored
2026-11-06 20:06:56 +08:00
AskaEth 91f723a019 docs(app-iced): M116 r4 —— 日志/模板示例改用真进程实测值(frame=overlapped)
真进程两跑(`--software-renderer`,同一个 bin,只改整窗 α)拿到:
  α=1.0 → … frame=overlapped layered=off
  α<1   → … frame=overlapped layered=on suppressed_by=window_alpha
所以把两处仍拿 `frame=popup` 当示例的文档改成实测值:
- `pages/appearance/mod.rs` 的取证模板:`frame=overlapped layered=on … suppressed_by=window_alpha`,
  并注明真进程两条在 `DevDocs/artifacts/m116/README.md`、`frame=` 恒为 `overlapped`/`other`。
- `transparency/dwm.rs` 的 `style_summary` 示例:同上,并点明 `popup` 只属于带 owner 的弹出窗。

注释级改动,行为与断言零改动。
2026-11-05 06:35:20 +08:00
AskaEth abc51a957b fix(app-iced): M116 r4/r5 —— 产品窗口不是 WS_POPUP(真凶是整窗 α 的 layered)+ 裁定 A 落地
r4 复审 p1(证据链前提错误)+ 裁定 A(D47:整窗透明度优先于 DWM 圆角)。

## 事实订正(四处错前提 + 两处残留断言)
- `probe.rs`:删掉「winit 在 decorations=false 下给 WS_POPUP」与「popup+layered = 本产品
  窗口的完整形状」。产品样式按 winit `window_state.rs::to_window_styles` 的来源写成
  `PRODUCT_STYLE`(`WS_CAPTION|WS_BORDER|WS_CLIPSIBLINGS|WS_SYSMENU|WS_THICKFRAME|
  WS_MINIMIZEBOX|WS_MAXIMIZEBOX` = `0x04CF0000`,**不含 WS_POPUP**),并新增
  `product` / `product+alpha` 两行(五行实验)。裸 popup 行改名为 `probe:popup*`,
  明确标注「对照,不是产品形状」。
- `corner.rs`:平台矩阵第 ③ 条改成「调整带只给带 WS_CAPTION|WS_THICKFRAME 的窗口;
  裸 popup 假形状上的 `+0,+0` 不能解释产品」;补第 ④ 条「整窗 α 会关掉圆角(裁定 A)」
  并说明日志里 `pref=` 有值即证明请求确实发出。
- `app/update/appearance.rs` 与 `transparency/{dwm.rs,transparency.rs}` 的注释:把「第一嫌疑是
  无边框 WS_POPUP」改成「整窗 α 引入的 WS_EX_LAYERED」;`frame=` 的真进程取值写明是
  `overlapped`(关 min/max 则 `other`),**永远不会是 popup**。
- 实测复核(`m116_corner_controlled_experiment`,本机 Server 2025 / RDP 断开):
  `product` 行 `Δframe = +0,-7`(与 `decorated` 同格),`product+alpha` 也是 `+0,-7`
  ⇒ 分层位不改变那条带子,`Δframe` 不能解释圆角失效。

## 裁定 A 落地
- 日志新增中立观测栏 `suppressed_by=window_alpha`(`format_line` 多一个参数;进 `CornerApplied`
  指纹,α 1.0↔<1 各触发一次重下/一行日志)。只陈述「分层位来自我们的整窗 α」。
- 设置页:外观说明句写明「开启整窗透明度时 Windows 不会给窗口圆角,回到 100% 自动恢复」;
  新增 `SettingsAppearanceCornerSuspended` 一档,仅在「跟随系统 + α<1」时作为**当前状态提示**
  出现;档位本身照常显示用户选的值(不偷偷改成「直角」)。中英双份 + golden。
- 未动:per-pixel α、D36 的 LWA_ALPHA 实现、α 与圆角的优先级。

## 顺带
- `presenter/prefs.rs` 的 `AppearanceBase` 文档「三个键/只读」→「四个键」(r4 P3)。
- 证据目录(DevDocs/artifacts/m116/{README.md,probe-table.md})按上面重写:五行真值表原样贴出、
  产品行标明「跑出来的不是手写的」、旧的「无边框 ⇒ 没有边框带」结论改为「只在假形状上成立,
  产品不用这个形状」。

门禁:check 0 / fmt --check 0 / `cargo test -p unlucky-app-iced --lib` 0(448 passed, 0 failed,
2 ignored)。ignore 探针实跑:`PROBE_RC=0`,两行产品读数见报告。
2026-11-05 06:35:19 +08:00
AskaEth 41746d1612 fix(app-iced): M116 r1 复审 —— 口径降级为「不承诺」、修掉伪造读数与两处错注释、圆角下发去重
四条 P1(全是「口径与证据不符」,行为未变):

1. 把「Win11 一定有系统圆角、边缘平滑」从事实降级:`corner.rs` 的平台矩阵改成
   「实测 / 解释 / 推断 / 未验」四列,并明写本机只测到「写入被接受 / 回读 E_INVALIDARG /
   无边框窗口没有 DWM 调整带」;设置页与 golden 的说明句改成「我们**请求**系统圆角,
   是否圆、边缘什么样由合成器决定」,不再承诺结果。
2. 删掉证据目录里那条伪造读数 `readback=2`:本机三种样式回读全是 `E_INVALIDARG`,
   该值物理上产不出来,也不可能由 `format_line` 产出。改成显式的「合成示例(非实测)」,
   并把每一栏标注成真实/示例来源。probe-table 的「没有一条是推断」标题一并拆开,
   结论 4/5 明确标为**解释**(并写明 Δframe 之差是样式位的同义反复,不能反推 DWM 决策)。
3. `keys.rs` 的「旧读新」语义按实际链路改写:1.4.0 读 1.5.0 文件走
   `SchemaCompat::check_url` → `ReadOnlyPreserveSchema` → `SchemaCompat::access` →
   `FileAccess::ReadOnly`,**整份配置只读**(值照读、`store.set` 报 `SettingError::ReadOnly`、
   `save` 跳过),不是「只是少一档」。
4. 两处错注释订正:
   - `hresult_ok` 的理由改成 `S_FALSE` 的通用语义,并明写 `0x7FF8FFA9` **不是** DWM 的返回值
     (本机实测是 `E_INVALIDARG 0x80070057`),那是我早期用 `unsigned_abs()` 打印 HRESULT 的产物;
     对应单测也把该常量标为「合成值,只用来钉规则」。
   - `DWMWA_VISIBLE_FRAME_BORDER_THICKNESS` 的文档改成「本机三样式三态实测全 1,不能当判据,
     仅作诊断读数」。

两条裁定:
- CPU 档在 Win11 仍走 DWM(tower 裁定保留):理由写进 `plan_corner` 文档——D36 约束的是我们自己的
  渲染路径,DWM 圆角是合成器侧的,不增加我方负担;设置页说明同步加了「软件渲染也一样」。
- 「圆角窗口上拖边缩放仍命中」实测未做(RDP 断开、无指针注入手段):mission 里以 note 记为 dropped
  (store 的 task_drop 对已完成任务不生效,工具层面无法取消勾选,已在 note 里写清)。

六条 P2:
1. probe-table 拆「实测 / 解释 / 未验」并删掉绝对化标题。
2. CPU 档裁定见上(任务书由 tower 侧调整)。
3. 拖边缩放任务记账更正见上。
4. **圆角下发去重**:新增 `corner::{CornerApplied, needs_redispatch}`(指纹 = 策略 / 来源 /
   原因码 / 有没有句柄,不含只有真下发才拿得到的读数),`App::apply_window_corner` 改成
   `&mut self` 并在判定未变时**直接返回**(不动窗口、不打日志);`App` 新增 `corner_applied` 字段。
   新增测试 `the_same_policy_is_dispatched_only_once`:连续 20 次同策略只下发/记 1 次,
   换档与「首次拿到句柄」必须重下。
5. `dwm_get_rect` 的 `hr == 0` 改成 `hresult_ok(hr)`,「判据只有一处」名副其实。
6. 报告口径修正(诊断已产出、UI 接线待做——`SettingsBridge` 未暴露 doc,接线要改 presenter 的
   `settings_bridge.rs`,不在本 mission scope),本轮不动代码。

门禁:fmt --check exit 0;clippy(5 个 crate)--all-targets -D warnings 零 warning;
lib 测试 app-iced 419(+1 去重测试)/ core 266 / design 163 / i18n 24 / presenter 129 全绿;
改动文件仍全部 ≤400 行(最大 keys.rs 398)。
2026-11-05 06:35:17 +08:00
AskaEth 88dbf6d955 feat(app-iced): M116 窗口外框圆角策略(跟随系统/直角)+ DWM 读写的受控对照实验
用户问「为什么窗口没有圆角」。核实:组件层圆角齐,但窗口外框**零处理**——代码里没有任何
Dwm* / SetWindowRgn。本 mission 先做实测,再按平台分开落策略。

判据(纯函数、可真值表):
- `platform::plan_corner(策略 × 系统版本 × 渲染档)` → `source=dwm|square` + `excluded_reason`。
  Win11 / Server 2025(build ≥ 22000)→ DWM;Win10 及更早 → 直角(不做 SetWindowRgn 毛边默认);
  探测不到版本(macOS / X11 / Wayland)→ 直角 + `unknown-os`(「不承诺」而不是「做不到」)。
  CPU 档在 Win11 上照样用 DWM(D36 限制的是我们自己的合成技巧,不是系统合成器)。

落点:`platform/win32/transparency.rs` 仍是全仓唯一 unsafe 文件,新增 DWM 的 FFI 与安全包装;
`alpha.rs` / `dwm.rs` / `probe.rs` 三个新子模块零 unsafe、零 extern(沿 exstyle.rs 的既有形状)。

实测(本机 Server 2025 build 26100、RDP 会话断开、同进程三窗口对照):
- `DwmSetWindowAttribute(DWMWA_WINDOW_CORNER_PREFERENCE)` 在 WS_OVERLAPPEDWINDOW / WS_POPUP /
  WS_POPUP|WS_EX_LAYERED(α=0.8) 上**全部成功** ⇒ 无边框与整窗 α 不会让属性设不上;
- `DwmGetWindowAttribute` 同一属性三种样式**全部 E_INVALIDARG** ⇒ 本机不支持回读,与样式无关;
- `DWMWA_EXTENDED_FRAME_BOUNDS − GetWindowRect`:有边框 = +0,−7,popup = +0,+0
  ⇒ DWM 只给有边框窗口加不可见调整带,我们的窗口没有可画的圆角区域(结构性解释)。
- 像素(角上透明/方块)**未验**:RDP 断连导致 CopyFromScreen / gdigrab 全废。

同时修掉一个自己踩到的判据错误:`i32::unsigned_abs()` 不能用来打印 HRESULT
(E_INVALIDARG 0x80070057 会被印成 0x7FF8FFA9)。现在 HRESULT 用 `hr as u32` + 严重性位,
GetLastError 的 DWORD 语义保持 `unsigned_abs` 并写死注释(两者不同源,别统一)。

配置:新键 `appearance.window_corner ∈ {system, square}`(出厂 system),schema 1.5.0 只推 minor、
不写迁移:老文件缺键 = 取默认 = 跟随系统;旧版本读新文件时未知键被登记并原样写回(`RenderMode::Full`),
只在显式 `store.set` 该键时才 `UnknownKey`。

设置 UI:外观组只留两档(跟随系统 / 直角)+ 一句按平台说清的说明,文案走 i18n 中英。
日志:`[corner] <ctx> corner=policy=… source=… os=… excluded_reason=… radius=system pref=… readback=… frame=… layered=…`
(形状由 `corner::format_line` 的格式化单测钉住,用探针窗口真实读数构造)。

证据:DevDocs/artifacts/m116/{README.md,probe-table.md}
2026-11-05 06:35:03 +08:00