wiki: v1.4.4 校准轮——01 演进史/分支表,06 三处失效 file:line 引用更新
docs: 「修改信息」用户覆盖层;widget 连接词「USB」;选项文案「默认」 - 16 §7.9(新):覆盖表存储/应用点/Wired 来源/音量页键/对话框/验收判据 - 05:rename_device → set_device_info 命令表(身份键口径 + 广播语义) - 15 §4.6:连接类型第四词「USB」只来自用户覆盖(tooltip/卡片仍「有线」); 两条取数路径必须同调覆盖层 - 16 §7.9 下拉空值选项文案「自动探测」改「默认」(= 未指定,走自动派生)
docs(wiki 07/13): wcslen(null) 根因闭环 + §23 已闸清单修正(主仓 beta.6 审查收尾) §16.1 根因闭环:6 场取证指纹、CI 产物逐字节解析(IAT 定名 wcslen、.pdata 定函数边界、vtable thunk 定调用方式)、「详细日志才崩」的惰性宏机制实证、 教训四条(null 闸唯一实现点 / 惰性宏参数当一等公民审 / 自家 release 二进制 无需 PDB 也可钉死崩溃函数 / 本机与用户机 ucrtbase 版本差异的边界)。 §23 已闸清单:修正「用户操作类投递关机时不可能触发」的错误假设——秒级 窗口内泵仍在消化消息,widget 点击/托盘菜单/电量循环/hotkey/updater 补闸; 真正不可达的只有前端命令类。
docs(wiki 07): §16 原生崩溃自取证判据与活体验证(对应主仓 8220a8b / v1.4.4-beta.5)
docs(07/13): 关机/注销竞态 panic 定性与退出投递闸纪律(对应主仓 5b8b072) - 07 §15:本机 25 场 panic_*.txt 定性——与用户闪退不同因的排除证据 (钩子 v1.4.0-beta.2 入包 ⇒ 用户机无 panic 文件是硬证据)、根因 (tao WM_ENDSESSION 只标 Destroyed 不停泵)、方法论教训(取证前 先验证取证工具的版本边界) - 13 §23:退出开始后后台线程不得向主线程泵投递——两条防线(闸门 + 哨兵窗口)、三个置位点、已闸清单、watchdog 闸在循环级的理由
docs(07/12/13): 原生层闪退复盘与 COM 通知回调生命周期纪律(对应主仓 f3d02ed) 07 §14:v1.4.4-beta.2 用户机 16 场异常死亡完整复盘——A/B 两型死亡签名、 三处根因(TrySuspend 同步释放押注 Evergreen 实测配对 / audio_notify 注销-释放竞态 / ensure_mta 污染 tokio 共享池)、取证锚链(issued→ completed→档位行 flush 落盘)与四条教训。 12 §10:P0-3 从「潜在缺陷」改判「已闭合」——实测配对依赖本身被 解除(进程同寿命持有),历史正控依据保留。 13 §22:新增纪律「COM 通知回调注销-释放路径禁止」——判据、三段 可复核依据(含 IMMNotificationClient 不 AddRef 的文档原文)、唯一 实现点与共享线程池附则。
docs(15): 位置口径同步六档下拉改造(默认 auto_right、镜像扫描、drag 收编) 主仓 de53b61 的 Wiki 配套:§5 现行口径补六档与迁移说明; §5.2 优先级措辞改贴靠档/drag 档;§8.6.11 现行判据补 auto_right 镜像臂(offset = max_offset)。演进史与退役留档不动。
docs: T3-3 来源标注取消沿革入 16 §11;15 档位表字号随 12 DIP 校准(对应主仓 de53b61)
docs: 图标缩放契约校准为面积平均/双三次,尺寸锚点随 256 母图改口径(15 §4.4/§4.5,对应主仓 de53b61)
docs: 图标描边相位判据(15 §4.4,对应主仓 de53b61)
docs: 设备 tooltip 信息行与两排共享布局核心判据(15 §4.6,对应主仓 de53b61) tooltip = 设备名 + 两排信息带标签版本;与 widget 行同走 item_row_contents 单一布局核心(RowContent 四态),⛔ 各自拼分支;连接类型词映射 蓝牙/2.4G/未知(N/A 只在连接类型出现);换行走 wrap_lines 硬换行。
docs: 徽章框/角部抗锯齿/墨界居中判据,改写 §4.6(对应主仓 de53b61) 徽章框判据追加二轮两机制:环带覆盖率 8/轴采样(封面角仍 4/轴)与 环带 alpha γ=1.6 幂次压缩(带墨净 -25%,判据与逐像素依据在条目内); 框内居中改按墨的上下界(mask_ink_rows + badge_text_y),旧行盒顶对齐 口径删除(数字墨带压下半部,用户实测偏下 ~4px)。
docs: 两排文本布局与连接类型派生判据(15 §4.6、16 §7.8,对应主仓 de53b61)
docs: 任务栏常驻开关判据与 blank 帧命中面纪律(15 §7) - 15 §7:「常驻显示未连接的设备」(默认关)只改过滤层不动挂载层—— 取数时不入选、空列表走透明帧、连上由事件驱动自动出现;⛔ 拆窗不可行 (refresh_async 未挂载早退 ⇒ 快照唯一生产者断供,永远重挂不回来); ⛔ blank 帧 = 无命中面(清 LAST_ITEM_RECTS + 销毁 tooltip:两者来自 上一张实帧,隐藏态可持续数小时,残留会让 hover / 滚轮 / tip 打到 看不见的条目上) 对应主仓 de53b61(常驻开关 + blank 帧清命中面)。
docs: 同步手机钉到任务栏批次的判据、判活、事件表与踩坑(03/07/11/15/16) - 15 §7:手机图标判据 = 容器服务行扫描(110A AudioSource 专属,真机实测 只在手机容器出现;⛔ AVRCP 四台全有)+ 组名行名兜底;连接态只认链路 证据——判活 = 转发会话峰值电表(唯一流量信号,推流 peak=0.10 / 无声=0), 见流即闩锁、drop_conn 唯一清点;五个被证伪的信号全数记录;采样线程 纪律(电表采样经常驻 STA 工作线程,MTA 控制器线程直调会逐秒刷公寓 冲突告警);a2dp-changed 接入任务栏事件表(force=true,读 10s 缓存 = 图标晚亮/晚灰) - 15 §8.5.3d:WheelTarget 第三枚 Relay——按名现查不缓存、绑定守卫四条件、 枚举失败降级、空名不匹配 - 07 §13:hover 每秒闪烁踩坑——判重必须挡在 emit 之前(回调里塞 return 只拦字段写入)+ appendChild 移动节点清 :hover(顺序比对后才移动); Armed 提示持久化连带修正 - 16 §4.6:服务行在场只证配对(两轮证伪:配对即常驻 / 离场变幻影均不 成立)+ 捕获端点同容器(Get-PnpDevice 实测 8 行) - 03 §a2dp_sink:转发会话已被任务栏消费;音量能力边界(手机音量 PC 不可见不可控,实测+四源) - 11:飞智 V5 Pro 特别版类型白名单 + 真机验证状态 + 校验和/档位口径 对应主仓 e79cfe0(特别版白名单)/ cb36ea1(钉住+图标+音量+滚轮)/ 08a5db6(连接态显示全链路)。
docs(01): release.yml 描述补便携版 zip(对应主仓 7bf80e9)
docs(11): 飞智手柄小节(Vader 4/5 Pro)+ 引言计数补 AULA/飞智 - 新增「飞智手柄(专用单设备接收器)」小节:两代协议 Fact 级转录 (V4 Pro 旧协议 0xEC 回显 / V5 Pro V2 GET_INFO),对应主仓 78c2bf4 - V4 Pro 标注实机验证状态与 DInput 模式前提(FN+A,XInput 模式读不到) - V5 Pro 标注协议来源与「尚未本机实机验证」证据边界;临界档复查、 充电照报档位等行为语义一句话带出,判据全文以代码内 ⛔/⭐ 为准 - 引言「全部设备」计数补 AULA/飞智;维护说明 drivers/razer/ 改 drivers/ 各品牌目录(AULA/飞智/罗技域早已不归 razer 目录)
docs(07): 响度均衡核心矛盾改判——引擎重载假设(Falcosc 脚本源码比对) 读 Falcosc/enable-loudness-equalisation 源码后改判:该脚本读写只用扁平 FxProperties、apply 靠 Restart-Service audiosrv,动摇「Win11 只认实例存储」 旧结论;上轮耳朵验收失败的根因更可能是引擎未重载而非 store 位置。 - §12 标题/状态行改判,指向 §12.4 - §12.1 补脚本两类值:5 个增强注册槽({d04e05a6})+ 2 个 flag(开关与本仓 COM 写入逐字节相同;release time 坐实偏移 8) - §12.2 修正「即时生效」为「写入即生效不成立」 - §12.4 改判引擎重载假设,标注证据边界(第三方报告,非本机实测) - §12.5 增补 FxProperties 缺失弱信号,隐藏逻辑决策不变 - §12.6 续做顺序重排:重载实验(端点禁用/切默认/audiosrv 当 ground truth) 优先,注册槽写入可行性其次,提权实例存储降为兜底 影响边界:仅 wiki 07 §12,主仓代码零改动。脚本源码行号引用(:106-109/ :110-111/:138)已人工核对。
wiki: v1.4.3 发布校准轮——演进史补 v1.4.3、分支表刷新 - 01 §5:补 v1.4.3 演进史条目(蓝牙音频接收、打断机制、device_mac 匹配) - 01 §6:分支表现状行刷新(2026-11-11,main = 24a537a 发布提交) - 抽查 3 处 file:line 引用(common.js:111 / settings.js:878 / popup-audio.js:83): 全部行号漂移但符号均存活,按 §2 惰性约定不追改;本轮新增的行号引用 (04 事件表 a2dp-changed 两处)经 grep 核实准确 - 本版 commits 的模式 A 义务已随 1113058 落地(03/04/05/13),无漏更 - README ↔ Wiki 入口链接互通、Home/_Sidebar 导航一致(无新页面)
docs(03/04/05/13): A2DP Sink 落地同步 同步主仓 628f01a(feat: 蓝牙音频接收)触发的模式 A 义务: - 03:新增 §a2dp_sink——单连接模型(TraceLogging provider 一次注册)、 复用纪律、武装仅首次 + 退避上限、100ms 轮询打断(pending 回灌)、 SetupDi 端点判活与 device_mac、init 同步探测 - 05:+5 命令(状态/枚举/连接/断开/popup 卡连接)与 a2dp-changed 事件, device_mac 为前端匹配唯一依据 - 04:common.js 共享 A2DP_ACTIVE_STATES、popup 会话标签改判与按钮路由判据、 事件总表补 a2dp-changed - 13 §19.3:登记表锚点归属细则——判据写进函数体首行,doc 注释会被 归属到上一个符号(a2dp_sink 实测)
wiki: v1.4.2 发布校准轮——演进史补行、分支表刷新、补 §8.6.2b 播放/暂停 poll-for-effect 漏更
docs(07): 补 §12.1「ACL 陷阱有两层」——提权也可能写不进 FxProperties **为什么改** 上条提交把 ACL 结论写成「非管理员不能写」,实测发现只对了一半:管理员同样 可能失败。按 `.NET` 惯用写法给的恢复命令在两种身份下都报错,排查后发现真正 的原因与身份无关。 **根因** `MMDevices\Audio\Render` 的 ACL 里 `BUILTIN\Administrators` 只有 `SetValue, ReadKey`,**不含 `KEY_CREATE_SUB_KEY`**。访问掩码按位合并后整体 判定,多要一位拿不到就整包拒绝: - `OpenSubKey($path, $true)` → 要 `KEY_WRITE`(= SET_VALUE|CREATE_SUB_KEY) ⇒ 普通用户与管理员【都】失败 - `reg add /v <已有值>` → 只要 `KEY_SET_VALUE` ⇒ 管理员下成功 ⚠️ `OpenSubKey($path, $true)` 看起来只是「要写权限」,实则要 `KEY_WRITE`, 是最容易踩的写法。 **顺带修正一处措辞** 原文「实测确认它在本机非管理员下必然失败」易被读成「脚本思路有误」。改为 「注册表直写路径非提权不可行」,并指向真正免提权的 §12.2 COM 路径——本次 恢复属性正是走它完成的(`SetPropertyValue` 非管理员返回 S_OK)。 **影响边界** 仅文档。§12.6 的续做入口与结论不变。 **验证结论** `git diff --cached --stat` = 1 文件 +30/-0;§12.1 结构复核为 `### 12.1` → `#### ⛔ ACL 陷阱有两层`。
docs(07): 归档响度均衡研究——扁平属性写得进但 Win11 不读,标未结 **为什么改** 用户需求是「给扬声器加音量均衡开关」。实现完成后机械验证全绿(写入字节、 状态回读、菜单位置、5 台设备往返),但用户耳朵验收**无变化**。因暂时无法 定论,按「不把半成品留在主干」的惯例把代码回退,研究过程存档待续。 **怎么改** 07 页新增 §12,六小节:属性位置与两条实现路径 / IPolicyConfig 免提权方案 / PROPVARIANT 偏移坑 / Win11 只认实例存储的未结矛盾 / 设备支持性无法判别 / 续做入口;Home.md 导航同步 §12 指针。 **踩过的坑(判据要点)** ① **ACL 不可推断,必须实测**:`Get-Acl` 显示 `BUILTIN\Users` 有 `SetValue` 且 `ContainerInherit`,看着能写;实则 ACL 里另有显式 DENY,`RegOpenKeyExW` 四个 访问掩码全返回 ACCESS_DENIED。差点据此做出「非管理员可写」的错误结论。 ② **PROPVARIANT 联合体在偏移 8**:按「头 + 立刻跟联合体」直觉写会让布尔值落到 偏移 4 → 属性能读到、真值恒假,且**不报任何错**。自检单测当场转红才拦住。 ③ **自造判据的下场**:§12.5 记录的设备支持性判据来自第三方工具的单台设备样本, 被我当成通用规则写进了上一轮结论,后经 22 台全量扫描证伪。已在页面中明确记 「该判据错误」并说明来源,避免下一个人重复踩。 **影响边界** 仅文档。主干代码已回退到 c67f261,工作区无残留。**系统侧有一处未还原**:DUNU DTC100pro 的响度属性被写成 ON,需要提权才能改回(已告知用户命令)。 **验证结论** 文档改动,`git diff --stat` = 2 文件 +118/-1;Home.md 表格 19 行均为 5 列, 结构未被破坏。主仓 `git status` 干净。
docs(08): Release Notes 规范收成指针,消除与 AGENTS 的双源 为什么改:Wiki 08「Release Notes 写作规范」与 AGENTS 同名章节近逐字重复, 且已实际漂移(Wiki 版多出「一切用户无感知的变化归 🧹 内部优化」一条)—— 正是本仓「勿双源」纪律要防的形态。 怎么改:本节收成指针(正本 = AGENTS「Release Notes 风格规范」),独有的一条 回填 AGENTS;AGENTS 同批把「宁详勿略」废弃为「只写四类 + 判据全文进 Wiki + 一个提交一个主题」,type 修为十选一、scope 改按受影响模块命名。 影响边界:只动本页一段;其余 Wiki 页零变化。AGENTS 侧的完整改动见主仓 `docs(agents)` 提交。doc-table-audit 硬失败 0。
docs(15): §8.6.11 新增「自动」位置,并修 §8.6.9 重号与错位 用户要求:给「任务栏窗口位置」加「自适应」档,用 `EchoMusic` 的思路实现避让。 ## 新增 §8.6.11「自适应」位置:问无障碍层要矩形,不问像素 用对照表把**与 §8 已退役两代的分界**钉死(数据来源 / 坐标系 / 失败时行为), ⚠️ 并显式写明:退役那条「⛔ 不得重开像素扫描」**依然有效**,本节不许被引用为 「解禁」。附三层结构图、四条降级条件、空隙求解三步、宽度两档口径。 ### ⛔⛔ 「有新图标不会自动避让」:两个独立缺陷(用户报,EchoMusic 不会) 用户报「当有新的应用图标时,组件不会自动避让,EchoMusic 的可以」。 **不是一个 bug,是两个**,且第二个放大第一个。 **缺陷一:槽位重算搭了 30s 的车。** `find_widget_slot()` 只在 `fetch_into_snapshot` 里跑,那挂在 `plan.refresh` = `REFRESH_EVERY_TICKS(15) × MAINTENANCE_TICK_SECS(2s)` = **30s**(15 是**刻意** 设的,为了不每 2s 跑 WMI)⇒ 探针 2s 采到了,组件最长 **30s** 才动。 `EchoMusic` 消费端 **2s** —— 差距就在这。 修法**不是**调小 `REFRESH_EVERY_TICKS`,而是加一条**只读 u64 的快节拍**: 维护线程每 2s 比对 `taskbar_layout::generation()`,变了就置 `SLOT_STALE` + `request_repaint()`;重算放**绘制路径**。 ⛔⛔ **重算必须留在主线程**:窗口在 `apply_from_config` 里于主线程创建 ⇒ 绘制与 `fetch_into_snapshot` 同线程 ⇒ `SLOT_*` 单写者。否则多写者会读到 「新的 `X` + 旧的 `W`」**撕裂组合** ⇒ 窗口正好压在图标上(缺陷隐蔽复发)。 **缺陷二:UIA 间歇性漏报单个元素(实测丢失率 ~40%)。** 同一块 `789..844` 在 10 次采样里丢 4 次;压力测试里单次漏 **3 块**。元素一丢 ⇒ 求解器认为那儿是 空的 ⇒ 组件滑回去压住图标 ⇒ 下轮又让开 ⇒ **持续抖动**。 修法 = **方向不对称的迟滞并集**(`merge_observed`):多一块**立即**生效(用户 要的响应速度);少一块等 `OCCUPIED_GRACE_MS = 6s` 才移除。 ⭐ 6s 依据:丢失率 40% ⇒ 连续 3 次全漏 ≈ 0.4³ = **6.4%**;3 × 2s = 6s。 ⚠️ 方向不可反(用在「新增」上会让避让变慢,用户更在意)。 ⚠️ 代价:关图标后 ~7s 才移回 —— 只影响观感,不产生重叠。 ⚠️ `reliable` 闸门**管不住**:它只判「枚举截断(>512)」与「结果为空」, 而漏报是**少几个元素**、枚举完整 ⇒ 判据只能放在并集层。 ### ⚠️ 审计抓到本次改动引入的自伤(已修,值得留档) 第一次跑逐帧重叠审计就抓到:`窗口 0..342 压住 [(0,69) 开始按钮, (72,347) 搜索框]`。 根因:`offset = 0` 成立靠的是「空隙边界**已含** `find_free_interval` 的间距」; 而**兜底槽 = 整条任务栏**,左缘是**裸的 x = 0** ⇒ 兜底时直接贴上开始按钮 (发生在启动首帧 —— `DESIRED_W` 还是 0、自适应必然兜底那一刻)。 ⇒ 加 `effective_position`:**兜底时贴靠语义退回 `center`**。 ⭐ 所以这条口径是**两段**的,别只记「adaptive 贴左」那一半。 ### 验收(65s 压力测试:4 应用逐个开→逐个关,逐帧比对窗口跨度与占用并集) ``` 审计:13 帧 adaptive 定位 | 重叠 0 帧 占用区更新 10 次 | 迟滞兜住漏报 1 次(并集=27 本次采样=24 兜住漏报=3) 开 notepad → +4s 组件 754 → 809(一个按钮宽 55px),此后 14s 纹丝不动 关 notepad → +7s 移回 754 ``` ### ⭐ 封面圆角:按底衬比例折算 + 抗锯齿(用户两轮反馈) ① 用户要「圆角与底衬一致」;② 又报「**圆角太大,且锯齿感很强**」。 ② 说明 ① 的直接实现(照抄绝对半径)错了:底衬 `r=8 / h=50` = 16%,封面边长 40 照抄 8px 就是 20%。改为按**比例**折算 `cover_radius(cw, m) = cw × m.radius / m.h` ⇒ 运行时 `cr = 6`,且随内容缩放档自动跟随。 抗锯齿:`inside_rounded_rect` 只判内/外 ⇒ 角上是阶梯。`rounded_rect_coverage` 把坐标/边长/半径**整体乘 S** 后喂给**生产用的那个谓词**做 4×4 超采样。 ⚠️⛔⛔ **`S` 必须 ≥ 8**(实测踩过):采样点是整数坐标,像素内四等分点只有 8 倍坐标下才落在整数;4 倍会让采样整体偏左上、最外角拿到 95/255 而非 0。 ⚠️ 覆盖率粒度由**样本数**(16)定,⛔ 与 S 无关。 ### ⛔⛔ 作废上一轮自己写下的验收结论(§4.7.0 已同步改写) 上一轮据抓图报「左缘逐行推进 8 行 ⇒ r=8,是圆角特征」,**那是错的**: 测到的是**封面本身的图案**。这张专辑封面**四角恰好是白的**,与任务栏底色 (217,217,217)几乎一致 ⇒ 遮罩边界在 RGB 上根本不可见;改前改后抓图完全一样, 而运行时半径确实从 8 变成了 6。 ⇒ **抓图能证明「有没有过渡带」,不能证明「半径是几」。** 半径改用运行时日志自证(临时打一行、读完即删):`cw=40 chh=40 cr=6 m.radius=8 m.h=50`。 ⚠️ 顺带证实旧记录「分层窗抓不到」不准确:`PrintWindow(hdc, PW_RENDERFULLCONTENT)` 能抓到合成内容,此前用的 `BitBlt` 屏幕 DC 才抓不到。 ### ⚠️ 判据自身踩的坑(判别力为零的那版) ⛔ 写成「`cov==255` ⇒ 硬判据为真」是**错的**并已转红:覆盖率采样像素**面积**, 硬判据取的是**那个像素坐标点**,`(3,0)` 即反例(cov=255、判据为假)—— 两者本就 差半像素,写成等价恒红。改为只钉两个**单向**约束(不许有洞 / 不许外扩超一个 采样带),容差**四向**(`(3,0)` 的硬判据真值在其**右侧**邻居)。 ⚠️⛔⛔ **注入本身也踩了坑**:调用点判据的 needle 字面量与生产处**完全相同**, 最初用 `str.replace`(**全局**)注入 ⇒ **判据自己的 needle 被一起改成新串**, 转而去生产处找它 ⇒ **注入后全绿,假阴性**。正解:**只替换第一处**。 定位手段:判据里插 `panic!` 打印 `src[..fn_start].matches(needle).count()` ——切片计数 0 却报绿才看出 needle 被改。已写入 Wiki 14 §6.1。 ⚠️ **仍未验证、需人工**:圆角观感。底衬只在悬停时画,底衬一亮封面白色角才有 对比;本机 `SetCursorPos` 返回 0(非交互 window station),自动做不了。 ### ⛔ 间距取整必须**下取整**(26px → 25px) `dip_of` 的 `round` 把 12.5 进位成 13 ⇒ 总距 26px,而原生实测 25px(本机 9 对 相邻图标全是 25)⇒ 改用整数除法(floor)⇒ 12 ⇒ **25px,完全一致**。 ⛔⛔ **不许改 `Metrics::dip_of` 的取整**:它服务全部内容度量,把 0.5 的偏差摊到 每一项上是净损失。⚠️ 两方向都 `margin ≥ 0`,安全性不变,差别只在「对齐实测值」。 ⚠️ 注入验证时我写过「③ 会独立转红」——**实测不成立**(① 先响)。已改成实测结论。 ### ⛔⛔ 「不是实时的」:只能走事件驱动,轮询物理上做不到 | 一次探针 | 实测 | |---|---| | 全量(枚举 + 23 元素 × 3 属性)| **79~161ms** | | 只「枚举 + 取长度」 | **34~57ms(占 43%)** | | ⛔ 复用 UIA 会话后 | **29~35ms** | 枚举本身占大头 ⇒ **不存在便宜的「变化信号」可轮询**(500ms 一次 = 12~34% 的核) ⇒ 改用 **UIA 结构变更事件**,提供方主动推、空闲零成本。 ⚠️ 先否掉一条看似合理的方案:「枚举任务栏后代 HWND 集合变化」当廉价信号 —— **实测不成立**:任务栏只有 11 个后代,开/关应用**0 新增 0 消失**(Win11 应用按钮 是 `MSTaskListWClass` 里 XAML 画的,不是 HWND)。 **三个承重点**:① 不抽消息队列 = 事件永不回调;② 兜底周期不可省(事件覆盖不到 「按钮整体平移」);③ ⛔⛔ **`ChildrenInvalidated` 必须排除** —— 本机实测 XAML **空闲时也持续**发事件(15s 内 +59 次),认它 ⇒ 探针被灌满且**避让一点不快**。 ⚠️ 留 `event_hits()`:XAML 上事件会不会触发事先无法确定;本机实测**会**(+5~14/次)。 ⚠️ **一条「像自激振荡、其实不是」的记录**:CPU 一度 10.7%,第一反应是 「组件移动 → 结构变化 → 事件 → 探针 → 移动」;**实测 15s 内移动 0 次**。 分档后真相是该链路只占 **+1.4%**(关 0.94% / 开 2.34%)。⇒ **先分档归因**。 **验收**:开 notepad 端到端 **0.39s**(事件→探针完成 31~35ms);关 6.36s (6s 迟滞,设计如此);稳态探针 0.8/s、29~35ms、移动 0 次。 ### ⭐⭐ 迟滞改成**按证据判活** ⇒ 关闭方向 6.36s → **0.25s** 原来 6s 是**按时间猜**。**先量清「漏报」是什么**(这一步否掉原假设):相邻两次 明细的差异不是「同一按钮随机丢」,而是**按钮动画平移的中间位置** (`336..376 → 336..377`、`365..405` 这种根本不是静止按钮位置的矩形)。 ⇒ 6s 真正在挡的是「枚举到中间态」。 ⇒ 改成:本次没枚举到就**单独问它一次**(`CurrentBoundingRectangle`)—— 答「还在」⇒ 保留并**采用它现在挪到哪儿了**;答「没了/空矩形/报错」⇒ **本轮就弃**。 ⚠️ 结构变化:`Entry` 带 COM 元素 ⇒ 非 `Send` ⇒ 进不了 `static CACHE` ⇒ 元素只活在 **探针线程的局部 `tracked`**,`CACHE` 仍是**纯矩形**(保住了「主线程纯内存读」)。 ⛔ 唯一保留的按时间判定:`collect_classic_children` 那三块无 UIA 节点可问 ⇒ `MERGE_FALLBACK_GRACE_MS = 2s`。 **验收**:开 0.41~0.60s(不变)/关 **0.25s**(原 6.36s);压力测试 39 帧 **重叠 0 帧**;CPU 关 0.94% / 开 1.61%;日志实证 `判活后=23 本次采到=22 问出还活着=1`。 ⚠️ 这个改动**动了迟滞机制本身** ⇒ 必须重跑逐帧重叠审计确认没把抖动放回来。 ### ⛔⛔ 间距:连着被判两次,两个错各有一个可测量的量 「间距还是有点大」→ 改 0 → 「间距不可能为0」。 **⛔ 推理错误:槽位相接 ≠ 底衬相接。** UIA 量到的槽位首尾相接(间隙 0),但原生 底衬/图标画在槽位**内部** ⇒ 底衬之间本来就有缝。 | 量 | 实测(125% DPI) | DIP | |---|---|---| | 应用按钮槽位节距 | 55px | 44 | | 应用图标宽度 | 30px | 24 | | ⇒ 图标每侧内缩 | **13px** | **10** | | ⇒ **图标↔图标间距** | **25px** | **20** | 测法(可复用):图标区与**同列正上方**求 RGB 最大偏差 —— 逐列同上下对比天然抵消 半透明底色渐变;拿「空白处」当底色会全判成非底色(实测踩到)。 ⇒ `margin + 图标内缩 = 图标间距` ⇒ **`margin = 10 DIP`**(实测落在 799 → 802)。 **⛔⛔ 换算走任务栏 DPI,⛔ 不许 `m.dip()`。** 本机 `taskbar_content_scale = "smaller"` ⇒ `content_dpi = 120/1.25 = 96` ⇒ `m.dip(10) == 10`(不是 12.5)⇒ 离图标 23px,**且不报错**。 ⭐ 原生那套度量是 Windows 自己画的,不认识我们的内容缩放设置。 ⚠️ 实测确认「原生不给应用按钮画常驻底衬」(顶行 y=2~4 全片无变化,hover 态与邻居 逐行差仅 +1~+3/255)⇒ **对齐参照只能是图标,不是底衬**。 ### ⛔ 空隙内**贴左缘**,⛔ 不许居中(初版写错、真机证伪后改的) 初版把 `adaptive` 归入 `center`,注释里的依据是「缝隙通常刚好等于窗口宽」。 **真机事实正相反**:实测空隙 `x=869 w=1005`,音乐面板只有 `w=506` —— **空隙是组件的 2 倍**。居中 ⇒ 组件被丢在空地正中、两侧各空 250px, 用户报「距离应用图标太远,之间有大片空白」。 ``` 修复前 应用图标 ──── 329px 空白 ──── [组件 506] ──── 250px ──── 右侧小组件 修复后 应用图标 ── 20px ── [组件 306] ──────────── 784px ──── 右侧小组件 ``` 根因不是算错,是**依据本身错了**:按「空隙≈组件宽」设计,而空隙只在任务栏 排满时才≈组件宽,空任务栏上它是组件的数倍。 - ⛔ `align_in_slot` 给 `adaptive` 单独一档 `offset = 0`,**不并入 `center`**。 - ⛔ **不许再叠加 `edge_margin`**:空隙边界已含 `find_free_interval` 的间距, 再加等于翻倍(实测 20 → 40px)。 - ⭐ 判据必须用**宽空隙**的数字钉:宽度恰好等于空隙时三种取法结果相同, 那正是当初误判的来源。 **⚠️ 两个「不处理就永远不生效」的坑**(本机实测,最重要的留档): 1. **UIA 会把容器也返回** —— 实测抓到 `789..2124`(中段)与 `2085..2560` (托盘)两块大矩形。容器一进占用列表就「到处都被占」⇒ 每次都退回, 而日志只说「空隙放不下」,**看不出真因**。⇒ `keep_innermost` 只留最内层; ⛔ 必须用「完整包含」而非「相交」(相邻按钮边界相接,实测 `404..459` / `459..514`)。 2. **widget 自己是任务栏的子窗口**(`SetParent` 嵌进去的)⇒ 不按 pid 排除就会 **躲自己**。⚠️ `EchoMusic` 无此问题(它的窗是独立顶层窗)。 另记真机验收数字(`命中空隙 x=814 w=1100` / 窗口实坐标 `1223..1464` ⊂ 空隙 `814..1914` / 切回 `center` 命中 0 次)与三处可观测性判据(首次决策必须记日志、 降级日志写明是哪一条、占用区明细进 verbose)。 ## 顺带修两处既有文档缺陷(都是本仓上一批引入的) · **§8.6.9 重号**:上一批给「点封面激活」新起了一节也叫 §8.6.9,而原 §8.6.9(「面板不是独立窗口 / 设备清单」)已存在 ⇒ 同一编号出现两次。 · **该节还错位**:它被插在 **§8.5.3c 与 §8.5.4 之间**(属 §8.5 的地盘)却编号 §8.6.x。 ⇒ 一并处理:把「点封面激活」**移到 §8.6 末尾并重编号为 §8.6.10**, §8.5 序列恢复连续、§8.6 无重号。同步改三处引用(`media_activate.rs`、 Wiki 03)⇒ 现在 `§8.6.9` 唯一指向原「设备清单」节。 ## §8.1 补一处分界说明 第 ① 代退役方案「枚举任务栏子窗矩形」与新版**表面相似、实质不同**:第①代把 子窗矩形**直接**当占用区(于是任务栏容器自己就占满整条 ⇒ 无处可放),新版 **先做「只留最内层」过滤**再求空隙 —— 这正是它没重蹈覆辙的原因。 ⚠️ 不加这一句,读者会以为 §8.6.11 就是第①代的重开。 ## Wiki 03 登记新模块 补 `taskbar_layout.rs`(633 行):职责、四个 ⛔(后台线程 + 缓存 / 任何不确定 都退回 / UIA 返 0 不等于空 / 两个必踩的坑),并指向 §8.6.11。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs(15): 记封面按下观感已由真机人工确认(v1.4.1 验收) §8.6.9 的「可行判据」段补一句闭环:**按下时封面颜色保持、只变暗**,真机人工确认。 为什么要专门记这一笔:这一格**机械判据做不到** —— widget 是 `WS_EX_LAYERED` + `UpdateLayeredWindow` 的分层窗,屏幕 DC 抓不到(`CAPTUREBLT` 也只抓到纯白),所以本节的判据全部是**跑合成公式的数字**,它们保证「公式对」, 但保证不了「看着对」。⇒ 本节原先只有一句「未经像素级截图验收」,缺口一直敞着。 ⭐ 由此记下两者的分工(对将来同类改动也适用): · 合成数值判据 ⇒ 保证「公式对」(可注入、可证伪); · 真机人工确认 ⇒ 保证「看着对」(机械判据覆盖不到)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs(03): 校准 32 处模块行数,补登 media_activate.rs 与 taskbar_music.rs 发 v1.4.1 的正式版校准轮(AGENTS.md:模块增删 → 03 · Rust 后端模块)。 ## 行数校准 32 处 模块清单里的行数**早已全部过时**(`taskbar_widget` 标 5110、实际 9369, 差得近一倍),会让读者对模块规模判断失准。逐个按 `readlines()` 重新计数校正 (⛔ 不用 `split('\n')`:那会把末尾换行算成一行,**整体偏大 1**)。 ## 补登两个未登记的模块 · **`taskbar_music.rs`(1187 行)**—— 音乐控制组件的数据层,SMTC 封装。 此前只在 [15 章](15-任务栏内嵌与自绘) 有正文,模块清单里没有 ⇒ 从模块表查不到。 含两条容易踩空的判据:⛔ 循环退出条件只能是「通道断开」(否则约 90 秒后订阅被 drop、快照清空,面板静默退成设备组且日志无异常);⭐⭐ SMTC 只给 AUMID 不给 PID, 音量与封面激活都靠 `audio.rs` 的匹配口径架桥。 · **`media_activate.rs`(274 行)**—— 点封面激活对应应用,本版新增。 ⛔ 全程只做 Win32 快调用、不做 COM 枚举、不持锁:它跑在**窗口线程的点击路径**上, 一次音频会话枚举(实测百毫秒级)就会让 widget 卡顿 ⇒ pid 由后台线程预算好。 ## 影响边界 纯文档。`doc-table-audit` 对全 Wiki 114 个表格块 0 问题。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs(13): 新增 §19.2.1「必要」的判准,并把它标为 §19.4.1 的上游 用户要求:之后的注释尽量精简,只写必要的 ⇒ 判据全文入 Wiki(AGENTS.md 只留 索引与摘要,避免两处标准并存)。 新增 **§19.2.1**,把「多余」从自觉变成**可判定**: · ⭐⭐ **三问**(全为「否」就删掉整段):① 删掉它有人会写出错代码吗;② 判据/依据/ 位置缺一不可吗;③ 它是这一处的信息吗(判据只写在唯一实现点)。 · ⛔ **四种最该删的形态**:旧实现对比、需求原文引述、**不可复核的旧数据** (即便自称「不引用」也要删)、以及整段演进史。附真实例子与删除理由。 · ⚠️ **反例,防止误删**:`tb_top > 200` 这类魔数要保留它**在本机失效的数字** (`tb_top = 1380`),否则「为什么不能用魔数」就不可复核。 · ⭐ **验收手段**:两版剥成「非空代码行」逐字比对 ⇒ 行为零变化;含两个已踩的坑 (空行位置漂移、CRLF/LF)。 · ⭐ **查冗余的六个角度** + 两个别误判的例外(`⇒` 不是冗余;指向 cargo registry 的 `file:NNN` 不是失效)。 · 记三轮实测的**终局**:结构层 / 叙事层 / 引用层三个角度都扫过并逐条人工判读 ⇒ **注释已无可删**,后续若再有冗余应是具体某处的新问题,而非全仓性的一批。 同时把既有 **§19.4.1** 标为 §19.2.1 的配套:「§19.2.1 决定哪些注释该删,§19.4.1 保证删完没坏」⇒ 删与不删共用同一套验收,不留两套并行标准。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs(15): §8.6.9 点封面激活的身份链与三段递进,附预乘变暗的坑 用户口径(2026-10-07):「点击音乐封面可以打开对应的音乐软件」。 ## 新增 §8.6.9 画出身份链(SMTC 的 AUMID → `media_session_of` 顺带回 pid → 快照 → 窗口线程的纯 Win32 激活),用表格列三段递进(可见窗口 / 按 exe 拉起 / `shell:AppsFolder`)各自覆盖的场景,并记录本机实测的取证过程: · **pid 17976 有 7 个顶层窗口、可见性全为 0** ⇒ 第一段必然落空, `IsWindowVisible` 过滤是对的;靠第二段拉起成功,**3 秒后**再点变成 `激活结果=Window` ⇒ 它自己把 UI 拉出来后第一段直接命中,两段互相印证。 另记四条判据:⛔ 浏览器跳过「按 exe 重启」(会开空白新窗);⚠️ 最小化必须先 `SW_RESTORE`;⛔ `ShellExecuteW` 成功判据是 `> 32` 而非「非零」;⛔「找不到窗口」 与「切不动」必须分开。 **⚠️ 已知缺口单列**:托盘气泡无法通用触发(`Shell_NotifyIconGetRect` + `NIN_SELECT` 所需的 id 由应用自己注册、第三方查不到 ⇒ 无第三方通用解)。 ## 附:预乘位图「变暗」的坑与三条失明判据 封面按下变灰曾只压 alpha、RGB 不动 ⇒ 真机表现为**封面颜色偏移**(蓝绿变 别的颜色)。**预乘空间里「变暗」= alpha 与 RGB 一起缩放**;同一个文件里三键 与设备角标本来就是这么写的,只有新增的封面这段抄漏了。 **⚠️ 三条「想当然的判据」全部失明**(都已实测撞到,值得留档): | 想当然的判据 | 为什么不行 | |---|---| | 比截图的色相 | ① 分层窗**根本抓不到**(`WS_EX_LAYERED` + `UpdateLayeredWindow`,`CAPTUREBLT` 也只抓到纯白)⇒ 像素判据做不出来;② 即便能抓,**覆盖度一变 R:G:B 比值必变**(底衬对三分量贡献相同)⇒ 结构性失明 | | 判据里重算一遍公式 | 对「调用点用错写法」完全失明;且 `contains` 会命中**判据自己的字面量** ⇒ 注入回 bug 仍全绿 | | 「按下总亮度必须低于松开」 | 本例正确解总亮度 376 **高于**松开态 310 —— 覆盖度降了、底衬透得更多 ⇒ 更亮是**物理事实**,不是 bug | **可行的判据**:用同一批合成函数跑真实数字,断言「降低覆盖度时结果必须落在 『正常合成』与『纯底衬』之间」——正确解三分量全在区间内,错误解 G/B 冲出 上限。另配一条**钉调用点**的判据(读源文件并**先把判据自身从待检文本里剔掉**)。 另记封面命中判据的同类盲区:默认布局里封面与三键不重叠 ⇒「先判三键」与 「先判封面」结果相同 ⇒ 判据对优先级**完全失明**(把封面判据挪到三键之前 仍全绿)。⇒ 判据补了一组**故意重叠**的布局,重叠点必须归第一键;注入实测 随即转红。这类「判据对要钉的性质失明」的教训值得留档。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs(15): §8.5.3b/§8.5.3c 记滚轮两面板分流与 SMTC↔音频会话的唯一桥 用户口径(2026-10-06):「hover 在音乐控制组件上滚动滚轮调整音乐会话音量, 与弹出窗口『应用音量』卡片音量条一致」+「tooltip 第三行显示当前应用的音量」。 新增三节: · **§8.5.3b 两个面板的滚轮:同一条通道、两枚枚举**。列两个面板的命中下标 空间 / 目标身份 / 写入对象对照表;记录当年那条「音乐面板一律不受理」的闸是 **过度拒绝**(真正的病根是下标空间别名,见 §8.6.4),本轮改为按面板分流, 且分流必须在取命中下标之前。 · **§8.5.3c SMTC 会话 → 音频会话:唯一的桥是「AUMID 含 exe 名」**。记平台 事实(SMTC 不给 PID 也不给应用名)、两类实测 AUMID 形态、三条收紧规则 (只认包含方向 / <3 字符拒绝 / 不要加「.exe 必需」这条伪证据)、 以及**刻意不做运行期记忆**的理由(记错即固化、记忆键无法校验重名)。 附 `ck/AF-Media-Bar` 的 `MediaSourceProcessResolver` 作为同源旁证。 · **音乐 tooltip 第三行 = 会话音量**:记「滚轮唯一可见反馈」这个事实 (实测音量 0.26→0.28 一路在涨、界面毫无变化 ⇒ 用户报「没反应」), 以及为什么音量**存快照**(COM 阻塞不进绘制线程)、为什么变化只能靠轮询 (会话音量不触发任何 SMTC 事件)、为什么滚轮后要立即乐观更新。 **⚠️ 已知覆盖缺口**单列一节写明(未修):AUMID 与 exe 名毫无字面关系的应用 匹配不上 ⇒ 滚轮不动 + tooltip 显示 `音量 —`;本机实测能工作是侥幸,根治需要 Win32 侧读应用自有 AUMID(只在进程内、外部不可得)。写明以免当成 bug 反复排查。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>