菊花链布局持久化机制 —— 意图 v2(2026-10-30 落地)
结论一句话:场景身份只认纯连接构成(内屏开关 + EDID 集合),值存全量用户设置;意图记忆是唯一权威,未见构成兜底平铺、60 秒学会;规则表 TOML 已删除;bpc 按屏持久化替代全局 6bpc 一刀切。
1. 背景:为什么重写
- KScreen 原生短板:一个 EDID 集合存一个文件,未知集合走默认模板(全堆 0,0 重叠、无状态输出保持 disabled),没有任何“来就点亮排开”的兜底;身份靠 EDID,撞车(同型号两块屏)靠连接器名,而 MST 重编号天天变。
- v1 凶案(2026-10-30 21:55):家里三屏(eDP + P27h-28 + MST 竖屏),replay 按裸 mode 尺寸比对——竖屏当时报 2560x1440(旋转未应用),规则要 1440x2560,失配;MST 抖动剩 1 外屏瞬间
single-2560x1440-right命中并应用,plan 里没有竖屏 → 竖屏一直黑。是自家机制把屏按黑的,比原生更狠。 - 用户裁定:① 内屏 1440x900 是人工定义,显示设置不擅自改;② 合盖拔线睡现状保留;③ key 里掺分辨率是错的(值当键用);④ 全局 6bpc 太粗暴,bpc 也要按构成持久化。
2. 方案定稿(用户逐条确认)
- key = 纯连接构成:
edp:on|edidA,edidB,…(EDID multiset 排序,同 EDID 双屏出现两次)。不含分辨率/位置/旋转/mode——那些是值。 - value = 全量用户设置:每块屏
{enabled, x, y, w, h, mode, rotation, bpc}。切换构成整套自动加载。 - 键语义(用户原话“满分”):
- 显示设置里关闭某屏 = 同 key 不同值(还连着,EDID 可读),60 秒 settle 自动更新记忆;
- 关掉某屏电源 = 不同 key(EDID 消失,构成真少了一块),各记各的,上电回来自动恢复;
- eDP 开关进 key(场景 2/3、4/5 就靠内屏开关区分);
- MST 残留连接器(有连接无 EDID)用连接器名回退顶上,保证不丢输出。
- 到场流程:
构成变化 → 查意图 ├─ 命中且已符合 → 不动 ├─ 命中但不符 → 按意图摆(enable/mode/position/rotation)+ 按 EDID 恢复 bpc └─ 未见过 → 兜底平铺(学 Windows extend:全点亮横向排开) → 用户随手摆 → 60s settle 无人改动 → 记为该构成意图 - 只在未锁屏且 DPMS 为 On 时动手;同构成只重放一次;应用成功立刻
guard accept;收尾重映射内屏触摸。
3. 实现位置(/etc/nixos,世代 replay-intent-v2-edid-key-no-rules,提交 f8e75b0)
modules/system/service/kscreen-layout-replay.py(重写核心,691→约640 行,净删):xrandr_detail():xrandr --verbose一次解析出每输出 EDID md5 + max bpc(kscreen-doctor 给不出 EDID);read_outputs():补edid/mode(当前 * 项)/bpc三字段;scene_key():构成签名(eDP 按名,外部按 EDID);signature()保留为别名;find_intent():精确命中,无尺寸回退(删掉就是为了不再误伤);map_intent():按 EDID 分组、组内按连接器名排序一一对应;集合对不上返回 None;save_intent():新文件~/.local/state/kscreen-layout-replay/intent-v2.json(旧 intent.json 留作考古);apply_intent():enable → mode → position → rotation 全量恢复(mode 缺失则跳过该步);apply_tile():兜底平铺(eDP 开着纳入首位、关着不动它);apply_bpc()+bdf_for_edid():sysfs 按 EDID 反查 PCI BDF,sudo -n gpu bpc <BDF> <X名> <值>(wheel 免密),单块失败只记日志;- 删除:
load_rules/pick_rule/build_plan/desired_equals_current/apply_plan/internal_enabled及 TOML 引用。
modules/system/service/kscreen-layout-replay.nix:删environment.etc."kscreen-layout-rules.toml"安装行,注释同步。modules/system/service/kwin-data/kscreen-layout-rules.toml:git rm删除(含 single-2560x1440-right 等 6 条规则)。- bpc 落点复用现状:
gpu bpc(写 debugfs 各 DP 口max_bpc+ 运行时 modeset)与 pin 目录/var/lib/amdgpu-bpc/<EDID稳定id>(手动 pin 仍优先于意图值)。
4. 身份同源证据
md5(/sys/class/drm/card1-eDP-1/edid) = 61bb4eaf… == KScreen 场景文件里 eDP 的 id,逐字一致。EDID md5 就是 KScreen 的 output id,运行时身份与 KScreen 记忆同源,不猜连接器名偏移。
5. 验证记录
- 单元测试(伪造 5 场景数据):重编号同 key、旋转态同 key、同 EDID 双屏映射不错位、四键互异、EDID 对齐——全过。
- 真机 22:32:未知构成触发两次平铺(竖屏晚到,第二次才纳入),竖屏点亮于 +4000+0,guard 同步接受 3 输出基准;
status正确显示构成签名 + 意图未命中。 - 实测 eDP 当前
max bpc=16、外屏 6——意图会如实记录,不做归一化;相等则跳过,回写只发生在真差异时。
6. 五场景清单(各学一次即收敛)
- 笔记本不外接:内屏开(10%);
- 家里:内屏开居上低分辨率 + 主屏居右 + 竖屏居左(45%);
- 家里:内屏关 + 主屏居右 + 竖屏居左(极少);
- 单位:内屏开居下 + 主屏居中 + 两横屏居左右(极少);
- 单位:内屏关 + 主屏居中 + 两横屏居左右(45%)。
7. 待实测(截至 2026-10-30 22:40)
- 家里布局 2 摆好静置 60s → 查 intent-v2.json 落盘;
- 切场景时 apply_bpc(主屏闪一下正常);
- 单位两组场景到单位学;
- 关电源键切换(MST 下游消失 vs 残留)。
台账索引:hermes-nixos-workspace docs/kscreen-layout-flap.md §13(凶案根因)§14(v2 落地)。对称机制(插电自动唤醒)另见 docs/unplug-typec-clamshell-suspend-20261030.md:固件无 AC 唤醒源,待一次“睡着插线”实测验证 USB 插入唤醒。