|
| 1 | +# 1287: 移除颜色选择器中的颜色预览,试验性规避 issue #4325 崩溃 |
| 2 | + |
| 3 | +## 背景 |
| 4 | + |
| 5 | +issue #4325:渐变选择器中把前景色从黑改红后进程 SIGABRT(glibc 堆损坏, |
| 6 | +崩溃栈停在 `QPushButton::paintEvent → QCommonStyle::drawControl → |
| 7 | +QBrush::init → malloc → abort`,属「堆先坏、重绘踩雷」模式)。 |
| 8 | + |
| 9 | +复现步骤:插入高亮环境 → 焦点工具栏"文本背景色"按钮 → 下拉选"渐变" |
| 10 | +→ 渐变选择器中把前景色从黑改红 → 崩溃。 |
| 11 | + |
| 12 | +已有分析见 `da/1200/crash` 分支的 `devel/1200.md`: |
| 13 | + |
| 14 | +- 根因方向为 `refresh-now` 同步重建控件子树 + `doRefresh` 中 `delete qwid` |
| 15 | + 同步析构正在发射信号的 enum 控件(UAF); |
| 16 | +- 将两处 `delete qwid` 改 `deleteLater()` 后**崩溃依旧**(该分支上验证未生效); |
| 17 | +- issue 终极方案是 QML 重构渐变选择器及相关对话框,工作量大。 |
| 18 | + |
| 19 | +本任务采用**最小改动试验**:把颜色选择器界面里的颜色预览(`pattern-sample` |
| 20 | +画布)整个移除,验证崩溃是否随之消失,为后续路线提供判据。 |
| 21 | + |
| 22 | +## 对 1200.md 走读的文件归属修正 |
| 23 | + |
| 24 | +- 启动 init 为 `init-research.scm`(`STEM_INIT_FILE`,见 |
| 25 | + `src/Texmacs/Server/tm_server.cpp:60`)。`open-gradient-selector` 经 |
| 26 | + `lazy-define` 来自 `(generic pattern-selector)` |
| 27 | + (`TeXmacs/progs/init-research.scm:280-283`),即 |
| 28 | + **`TeXmacs/progs/generic/pattern-selector.scm`**; |
| 29 | +- `TeXmacs/progs/generic/pattern-tools.scm` 是"Alternative pattern selector", |
| 30 | + main 上无任何加载点(`developer-menu.scm:146-147` 的 `tm-tool` 引用已注释), |
| 31 | + 属死代码。1200.md 走读把链路归到 `pattern-tools.scm:67` 等,文件归属有误; |
| 32 | + 其机制描述(`refresh-now` → `windows_refresh` → 同步 emit → `doRefresh` |
| 33 | + 中 `delete qwid`)仍然成立,`pattern-selector.scm` 中对应位置: |
| 34 | + `set-color` :59-63、`pattern-sample` refreshable :445-471、 |
| 35 | + `pattern-options` refreshable :476-544、Foreground enum :415-432。 |
| 36 | + |
| 37 | +## 试验假设 |
| 38 | + |
| 39 | +改前景色的完整链路(均在 `pattern-selector.scm`): |
| 40 | + |
| 41 | +``` |
| 42 | +Foreground enum (QTMComboBox, currentIndexChanged) |
| 43 | +→ set-gradient-foreground (:219) |
| 44 | +→ set-color (:59) |
| 45 | +→ (refresh-now "pattern-sample") (:61) |
| 46 | +→ (refresh-now "pattern-options") (:62) |
| 47 | +→ windows_refresh (tm_window.cpp:842) |
| 48 | +→ QTMRefreshableWidget::doRefresh (QTMMenuHelper.cpp:964,main 上仍为 delete qwid) |
| 49 | +``` |
| 50 | + |
| 51 | +堆损坏的候选来源有二: |
| 52 | + |
| 53 | +- **A**:`doRefresh("pattern-options")` 同步 `delete` 正在发射信号的 enum |
| 54 | + 所在子树(1200.md 结论;但 `deleteLater` 修复未生效,存疑); |
| 55 | +- **B**:`pattern-sample` 画布(内嵌 `cell-background` 为 pattern 的 |
| 56 | + `texmacs-output` 表格)的刷新/渲染管线 |
| 57 | + (`build_effect` EFF_GRADIENT → `color_matrix` 等)在同步重建中损坏堆。 |
| 58 | + |
| 59 | +移除预览后的判据: |
| 60 | + |
| 61 | +- **崩溃消失** → B(或 B+A)为真,预览路径是损坏源,短期以移除预览兜底, |
| 62 | + QML 重构范围也可相应缩小; |
| 63 | +- **崩溃仍在** → A 确认,预览无辜,直接推进 QML 重构路线。 |
| 64 | + |
| 65 | +## 改动 |
| 66 | + |
| 67 | +`TeXmacs/progs/generic/pattern-selector.scm`: |
| 68 | + |
| 69 | +1. `(tm-widget ((pattern-selector u) cmd))` 中删除 |
| 70 | + `(vlist (refreshable "pattern-sample" ...))`(600x450 预览画布)及其后 |
| 71 | + 的 `// // //` 分隔; |
| 72 | +2. `set-color` 中删除 `(refresh-now "pattern-sample")` |
| 73 | + (预览移除后该名字已无对应 refreshable,`windows_refresh` 对无人认领的 |
| 74 | + kind 是无害 no-op,删除是为免留死代码)。 |
| 75 | + |
| 76 | +影响面:`open-pattern-selector` / `open-gradient-selector` / |
| 77 | +`open-background-picture-selector` 三个对话框共用 `(pattern-selector u)` |
| 78 | +界面,预览一并移除(三者崩溃机制相同)。`pattern-tools.scm` 为死代码, |
| 79 | +不动。 |
| 80 | + |
| 81 | +纯 scheme 改动,无需重编 C++,重启应用即生效。 |
| 82 | + |
| 83 | +## 验证(2026-09-09 已执行,自动化对照实验) |
| 84 | + |
| 85 | +环境:Xvfb :99(1600x1200,Xvfb/xdotool 经 `apt-get download` 解包到 |
| 86 | +`/tmp/xv-pkg` 运行,无需 root)。同一个 `moganstem` 二进制,仅切换 |
| 87 | +`TEXMACS_PATH` 指向基线树(`main@69af5c889` 的 worktree)或修复树 |
| 88 | +(本分支);scheme 侧 `delayed` 6s 调用 `open-gradient-selector` 打开 |
| 89 | +对话框,`xdotool` 依次点击「前景色」下拉框并选择「红色」,全程截图确认 |
| 90 | +控件位置与状态。 |
| 91 | + |
| 92 | +- **基线**:点击红色后进程立即死亡,stderr 报 |
| 93 | + `malloc(): unaligned tcache chunk detected` → `SIGABRT`,崩溃栈为 |
| 94 | + `abort ← malloc ← operator new ← QCss::StyleSelector::matchRule ← |
| 95 | + QWidget event/polish`——与 issue #4325 相同的「堆早已损坏、后续分配 |
| 96 | + 踩雷」模式,复现成立。 |
| 97 | +- **修复树**:同样的点击路径,前景色成功变为红色(截图确认下拉框显示 |
| 98 | + 「红色」,证明 enum 信号 → `set-gradient-foreground` → `set-color` → |
| 99 | + `refresh-now "pattern-options"` 全链路真实生效);随后连续切换 |
| 100 | + 蓝→红→绿→红共 5 次交互进程均存活,stderr 无任何 glibc 报错;点 |
| 101 | + 「确认」对话框正常关闭。Pattern 选择器(共用同一界面)打开、布局正常, |
| 102 | + 交互冒烟后进程存活。 |
| 103 | + |
| 104 | +### 结论 |
| 105 | + |
| 106 | +- 崩溃第一现场在 `pattern-sample` 预览画布的同步重建/渲染路径(假设 B), |
| 107 | + **移除预览后 issue #4325 复现路径不再崩溃**,本改动作为修复保留; |
| 108 | +- 1200.md 指认的 `doRefresh("pattern-options")` 同步 `delete` enum 子树 |
| 109 | + 并非本复现的第一现场——修复树中 options 面板的同步重建仍在发生, |
| 110 | + 但不再崩溃;这也解释了 `deleteLater` 修复未生效的原因(未动到真凶); |
| 111 | +- QML 重构仍是界面长期演进方向,但不再阻塞本崩溃修复。 |
| 112 | + |
| 113 | +## 后续 |
| 114 | + |
| 115 | +- 本分支合入后关闭 issue #4325 的崩溃(回复验证方法与结论); |
| 116 | +- 若后续恢复预览需求,应在 QML 重构版选择器中带预览,避开 Qt Widgets |
| 117 | + 的 refreshable 同步重建机制。 |
0 commit comments