diff --git a/TeXmacs/progs/generic/pattern-selector.scm b/TeXmacs/progs/generic/pattern-selector.scm index c83c7e73b4..778a19c348 100644 --- a/TeXmacs/progs/generic/pattern-selector.scm +++ b/TeXmacs/progs/generic/pattern-selector.scm @@ -58,7 +58,6 @@ (define (set-color col) (set! global-pattern-color col) - (refresh-now "pattern-sample") (refresh-now "pattern-options") ) ;define @@ -442,110 +441,79 @@ ) ;tm-widget (tm-widget ((pattern-selector u) cmd) - (padded (hlist (vlist (refreshable "pattern-sample" - (resize "600px" - "450px" - (texmacs-output `(document (block (tformat (cwith - "1" "1" - "1" "1" - "cell-width" - "596guipx") - (cwith "1" "1" - "1" "1" - "cell-height" - "446guipx") - (cwith "1" "1" - "1" "1" - "cell-vmode" - "exact") - (cwith ,"1" - ,"1" - ,"1" - ,"1" - ,"cell-background" - ,(get-color)) - (table (row (cell "")))))) - '(style (tuple "generic")) - ) ;texmacs-output - ) ;resize - ) ;refreshable - ) ;vlist - // - // - // - (explicit-buttons (vlist (refreshable "pattern-options" - (assuming (and (not global-picture?) (not global-gradient?)) - (aligned (item (text "Name:") (link pattern-name-selector)) - (item (text "Width:") - (hlist (enum (set-width answer) - (list (get-width) "100%" "100@" "1cm" "") - (get-width) - "15em" - ) ;enum - >> - ) ;hlist - ) ;item - (item (text "Height:") - (hlist (enum (set-height answer) - (list (get-height) "100%" "100@" "1cm" "") - (get-height) - "15em" - ) ;enum - >> - ) ;hlist - ) ;item - (item (text "Recolor:") (link pattern-recolor-options)) - (item (text "Skin:") (link pattern-skin-options)) - ;; TODO: the blur effect changes the image size; - ;; one needs a blur that wraps around torically - ;; (item (text "Blur:") - ;; (link pattern-blur-options)) - ) ;aligned - ) ;assuming - (assuming global-gradient? - (aligned (item (text "Name:") (link pattern-name-selector)) - (item (text "Width:") - (hlist (enum (set-width answer) - (list (get-width) "100%" "100@" "1cm" "") - (get-width) - "15em" - ) ;enum - >> - ) ;hlist - ) ;item - (item (text "Height:") - (hlist (enum (set-height answer) - (list (get-height) "100%" "100@" "1cm" "") - (get-height) - "15em" - ) ;enum - >> - ) ;hlist - ) ;item - (item (text "Foreground:") (link pattern-foreground-options)) - (item (text "Background:") (link pattern-background-options)) - ) ;aligned - ) ;assuming - (assuming global-picture? - (aligned (item (text "Name:") (link pattern-name-selector)) - (item (text "Size:") - (hlist (enum (set-size answer) - (list "Fit" "Fit to width" "Fit to height") - (get-size) - "15em" - ) ;enum - >> - ) ;hlist - ) ;item - (item (text "Recolor:") (link pattern-recolor-options)) - (item (text "Skin:") (link pattern-skin-options)) - ) ;aligned - ) ;assuming - ) ;refreshable - ====== - (glue #f #t 0 0) - ) ;vlist - ) ;explicit-buttons + (padded (hlist (explicit-buttons (vlist (refreshable "pattern-options" + (assuming (and (not global-picture?) (not global-gradient?)) + (aligned (item (text "Name:") (link pattern-name-selector)) + (item (text "Width:") + (hlist (enum (set-width answer) + (list (get-width) "100%" "100@" "1cm" "") + (get-width) + "15em" + ) ;enum + >> + ) ;hlist + ) ;item + (item (text "Height:") + (hlist (enum (set-height answer) + (list (get-height) "100%" "100@" "1cm" "") + (get-height) + "15em" + ) ;enum + >> + ) ;hlist + ) ;item + (item (text "Recolor:") (link pattern-recolor-options)) + (item (text "Skin:") (link pattern-skin-options)) + ;; TODO: the blur effect changes the image size; + ;; one needs a blur that wraps around torically + ;; (item (text "Blur:") + ;; (link pattern-blur-options)) + ) ;aligned + ) ;assuming + (assuming global-gradient? + (aligned (item (text "Name:") (link pattern-name-selector)) + (item (text "Width:") + (hlist (enum (set-width answer) + (list (get-width) "100%" "100@" "1cm" "") + (get-width) + "15em" + ) ;enum + >> + ) ;hlist + ) ;item + (item (text "Height:") + (hlist (enum (set-height answer) + (list (get-height) "100%" "100@" "1cm" "") + (get-height) + "15em" + ) ;enum + >> + ) ;hlist + ) ;item + (item (text "Foreground:") (link pattern-foreground-options)) + (item (text "Background:") (link pattern-background-options)) + ) ;aligned + ) ;assuming + (assuming global-picture? + (aligned (item (text "Name:") (link pattern-name-selector)) + (item (text "Size:") + (hlist (enum (set-size answer) + (list "Fit" "Fit to width" "Fit to height") + (get-size) + "15em" + ) ;enum + >> + ) ;hlist + ) ;item + (item (text "Recolor:") (link pattern-recolor-options)) + (item (text "Skin:") (link pattern-skin-options)) + ) ;aligned + ) ;assuming + ) ;refreshable + ====== + (glue #f #t 0 0) + ) ;vlist + ) ;explicit-buttons ) ;hlist ====== (explicit-buttons (hlist >>> diff --git a/devel/1287.md b/devel/1287.md new file mode 100644 index 0000000000..647e398d0f --- /dev/null +++ b/devel/1287.md @@ -0,0 +1,117 @@ +# 1287: 移除颜色选择器中的颜色预览,试验性规避 issue #4325 崩溃 + +## 背景 + +issue #4325:渐变选择器中把前景色从黑改红后进程 SIGABRT(glibc 堆损坏, +崩溃栈停在 `QPushButton::paintEvent → QCommonStyle::drawControl → +QBrush::init → malloc → abort`,属「堆先坏、重绘踩雷」模式)。 + +复现步骤:插入高亮环境 → 焦点工具栏"文本背景色"按钮 → 下拉选"渐变" +→ 渐变选择器中把前景色从黑改红 → 崩溃。 + +已有分析见 `da/1200/crash` 分支的 `devel/1200.md`: + +- 根因方向为 `refresh-now` 同步重建控件子树 + `doRefresh` 中 `delete qwid` + 同步析构正在发射信号的 enum 控件(UAF); +- 将两处 `delete qwid` 改 `deleteLater()` 后**崩溃依旧**(该分支上验证未生效); +- issue 终极方案是 QML 重构渐变选择器及相关对话框,工作量大。 + +本任务采用**最小改动试验**:把颜色选择器界面里的颜色预览(`pattern-sample` +画布)整个移除,验证崩溃是否随之消失,为后续路线提供判据。 + +## 对 1200.md 走读的文件归属修正 + +- 启动 init 为 `init-research.scm`(`STEM_INIT_FILE`,见 + `src/Texmacs/Server/tm_server.cpp:60`)。`open-gradient-selector` 经 + `lazy-define` 来自 `(generic pattern-selector)` + (`TeXmacs/progs/init-research.scm:280-283`),即 + **`TeXmacs/progs/generic/pattern-selector.scm`**; +- `TeXmacs/progs/generic/pattern-tools.scm` 是"Alternative pattern selector", + main 上无任何加载点(`developer-menu.scm:146-147` 的 `tm-tool` 引用已注释), + 属死代码。1200.md 走读把链路归到 `pattern-tools.scm:67` 等,文件归属有误; + 其机制描述(`refresh-now` → `windows_refresh` → 同步 emit → `doRefresh` + 中 `delete qwid`)仍然成立,`pattern-selector.scm` 中对应位置: + `set-color` :59-63、`pattern-sample` refreshable :445-471、 + `pattern-options` refreshable :476-544、Foreground enum :415-432。 + +## 试验假设 + +改前景色的完整链路(均在 `pattern-selector.scm`): + +``` +Foreground enum (QTMComboBox, currentIndexChanged) +→ set-gradient-foreground (:219) +→ set-color (:59) +→ (refresh-now "pattern-sample") (:61) +→ (refresh-now "pattern-options") (:62) +→ windows_refresh (tm_window.cpp:842) +→ QTMRefreshableWidget::doRefresh (QTMMenuHelper.cpp:964,main 上仍为 delete qwid) +``` + +堆损坏的候选来源有二: + +- **A**:`doRefresh("pattern-options")` 同步 `delete` 正在发射信号的 enum + 所在子树(1200.md 结论;但 `deleteLater` 修复未生效,存疑); +- **B**:`pattern-sample` 画布(内嵌 `cell-background` 为 pattern 的 + `texmacs-output` 表格)的刷新/渲染管线 + (`build_effect` EFF_GRADIENT → `color_matrix` 等)在同步重建中损坏堆。 + +移除预览后的判据: + +- **崩溃消失** → B(或 B+A)为真,预览路径是损坏源,短期以移除预览兜底, + QML 重构范围也可相应缩小; +- **崩溃仍在** → A 确认,预览无辜,直接推进 QML 重构路线。 + +## 改动 + +`TeXmacs/progs/generic/pattern-selector.scm`: + +1. `(tm-widget ((pattern-selector u) cmd))` 中删除 + `(vlist (refreshable "pattern-sample" ...))`(600x450 预览画布)及其后 + 的 `// // //` 分隔; +2. `set-color` 中删除 `(refresh-now "pattern-sample")` + (预览移除后该名字已无对应 refreshable,`windows_refresh` 对无人认领的 + kind 是无害 no-op,删除是为免留死代码)。 + +影响面:`open-pattern-selector` / `open-gradient-selector` / +`open-background-picture-selector` 三个对话框共用 `(pattern-selector u)` +界面,预览一并移除(三者崩溃机制相同)。`pattern-tools.scm` 为死代码, +不动。 + +纯 scheme 改动,无需重编 C++,重启应用即生效。 + +## 验证(2026-09-09 已执行,自动化对照实验) + +环境:Xvfb :99(1600x1200,Xvfb/xdotool 经 `apt-get download` 解包到 +`/tmp/xv-pkg` 运行,无需 root)。同一个 `moganstem` 二进制,仅切换 +`TEXMACS_PATH` 指向基线树(`main@69af5c889` 的 worktree)或修复树 +(本分支);scheme 侧 `delayed` 6s 调用 `open-gradient-selector` 打开 +对话框,`xdotool` 依次点击「前景色」下拉框并选择「红色」,全程截图确认 +控件位置与状态。 + +- **基线**:点击红色后进程立即死亡,stderr 报 + `malloc(): unaligned tcache chunk detected` → `SIGABRT`,崩溃栈为 + `abort ← malloc ← operator new ← QCss::StyleSelector::matchRule ← + QWidget event/polish`——与 issue #4325 相同的「堆早已损坏、后续分配 + 踩雷」模式,复现成立。 +- **修复树**:同样的点击路径,前景色成功变为红色(截图确认下拉框显示 + 「红色」,证明 enum 信号 → `set-gradient-foreground` → `set-color` → + `refresh-now "pattern-options"` 全链路真实生效);随后连续切换 + 蓝→红→绿→红共 5 次交互进程均存活,stderr 无任何 glibc 报错;点 + 「确认」对话框正常关闭。Pattern 选择器(共用同一界面)打开、布局正常, + 交互冒烟后进程存活。 + +### 结论 + +- 崩溃第一现场在 `pattern-sample` 预览画布的同步重建/渲染路径(假设 B), + **移除预览后 issue #4325 复现路径不再崩溃**,本改动作为修复保留; +- 1200.md 指认的 `doRefresh("pattern-options")` 同步 `delete` enum 子树 + 并非本复现的第一现场——修复树中 options 面板的同步重建仍在发生, + 但不再崩溃;这也解释了 `deleteLater` 修复未生效的原因(未动到真凶); +- QML 重构仍是界面长期演进方向,但不再阻塞本崩溃修复。 + +## 后续 + +- 本分支合入后关闭 issue #4325 的崩溃(回复验证方法与结论); +- 若后续恢复预览需求,应在 QML 重构版选择器中带预览,避开 Qt Widgets + 的 refreshable 同步重建机制。