From 53e185118d318c0e23f6de170b679a1dd4f05a67 Mon Sep 17 00:00:00 2001 From: Da Shen Date: Wed, 9 Sep 2026 00:26:52 +0800 Subject: [PATCH 1/4] =?UTF-8?q?[1287]=20=E6=96=B0=E5=A2=9E=20devel/1287.md?= =?UTF-8?q?=EF=BC=9A=E7=A7=BB=E9=99=A4=E9=A2=9C=E8=89=B2=E9=A2=84=E8=A7=88?= =?UTF-8?q?=E8=A7=84=E9=81=BF=20issue=20#4325=20=E5=B4=A9=E6=BA=83?= =?UTF-8?q?=E7=9A=84=E4=BB=BB=E5=8A=A1=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devel/1287.md | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 devel/1287.md diff --git a/devel/1287.md b/devel/1287.md new file mode 100644 index 0000000000..2d4c68a8e5 --- /dev/null +++ b/devel/1287.md @@ -0,0 +1,96 @@ +# 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++,重启应用即生效。 + +## 验证 + +1. 运行(开发构建): + `TEXMACS_PATH=/TeXmacs ./build/linux/x86_64/release/moganstem` +2. 回归复现:插入高亮环境 → 焦点工具栏"文本背景色" → 下拉选"渐变" + → 前景色黑改红 → 观察是否崩溃;多改几次(红/蓝/绿交替)。 +3. 基本回归:Pattern / Gradient / Picture 三个选择器打开、改 Width/Height、 + 改 Foreground/Background、Ok 应用到文档,确认除预览消失外布局与功能正常。 +4. 结论回填本文件,并在 issue #4325 上同步。 + +## 后续 + +- 崩溃消失:保留本改动,评估是否作为过渡方案合入; +- 崩溃仍在:还原预览(或保留亦可),按 issue 计划推进 QML 重构。 From e3af9a810df2fe8de6751bba7e36c7e99d4e6568 Mon Sep 17 00:00:00 2001 From: Da Shen Date: Wed, 9 Sep 2026 00:26:52 +0800 Subject: [PATCH 2/4] =?UTF-8?q?[1287]=20=E7=A7=BB=E9=99=A4=E9=A2=9C?= =?UTF-8?q?=E8=89=B2=E9=80=89=E6=8B=A9=E5=99=A8=E5=AF=B9=E8=AF=9D=E6=A1=86?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E9=A2=9C=E8=89=B2=E9=A2=84=E8=A7=88=EF=BC=8C?= =?UTF-8?q?=E8=AF=95=E9=AA=8C=E8=A7=84=E9=81=BF=E6=94=B9=E5=89=8D=E6=99=AF?= =?UTF-8?q?=E8=89=B2=E5=B4=A9=E6=BA=83=20(issue=20#4325)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pattern-selector 对话框改前景色时 set-color 同步 refresh-now 两个 refreshable,QTMRefreshableWidget::doRefresh 中 delete qwid 会在信号 发射途中析构 enum 所在子树(devel/1200.md,deleteLater 未根治)。 本改动移除 pattern-sample 预览画布及对应 refresh-now,作为最小改动 试验:若崩溃消失则损坏源与预览刷新/渲染路径相关;若仍在则确认 options 子树同步重建问题,按 issue 计划推进 QML 重构。 pattern / gradient / picture 三个选择器共用该界面,预览一并移除; pattern-tools.scm 在 main 上为死代码,不动。 --- TeXmacs/progs/generic/pattern-selector.scm | 34 +--------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/TeXmacs/progs/generic/pattern-selector.scm b/TeXmacs/progs/generic/pattern-selector.scm index c83c7e73b4..72fe4c6d94 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,38 +441,7 @@ ) ;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" + (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:") From 40e3f9d460663aa288c347ad3c61f86e3e8ce30b Mon Sep 17 00:00:00 2001 From: Da Shen Date: Wed, 9 Sep 2026 00:43:18 +0800 Subject: [PATCH 3/4] =?UTF-8?q?[1287]=20=E5=9B=9E=E5=A1=AB=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=8C=96=E5=AF=B9=E7=85=A7=E9=AA=8C=E8=AF=81=E7=BB=93?= =?UTF-8?q?=E8=AE=BA=EF=BC=9A=E7=A7=BB=E9=99=A4=E9=A2=84=E8=A7=88=E5=90=8E?= =?UTF-8?q?=20issue=20#4325=20=E5=A4=8D=E7=8E=B0=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Xvfb+xdotool 对照实验(同一二进制,仅切换 scheme 树): - 基线 main:改前景色为红色立即 SIGABRT(malloc(): unaligned tcache chunk detected),复现成立; - 修复树:同一操作路径不崩溃,前景色成功改红,连续 5 次换色与确认 按钮均正常,stderr 无 glibc 报错; - 结论:第一现场在 pattern-sample 预览画布的同步重建路径,options 面板的同步 delete 并非第一现场(亦解释 deleteLater 未生效)。 --- devel/1287.md | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/devel/1287.md b/devel/1287.md index 2d4c68a8e5..647e398d0f 100644 --- a/devel/1287.md +++ b/devel/1287.md @@ -80,17 +80,38 @@ Foreground enum (QTMComboBox, currentIndexChanged) 纯 scheme 改动,无需重编 C++,重启应用即生效。 -## 验证 - -1. 运行(开发构建): - `TEXMACS_PATH=/TeXmacs ./build/linux/x86_64/release/moganstem` -2. 回归复现:插入高亮环境 → 焦点工具栏"文本背景色" → 下拉选"渐变" - → 前景色黑改红 → 观察是否崩溃;多改几次(红/蓝/绿交替)。 -3. 基本回归:Pattern / Gradient / Picture 三个选择器打开、改 Width/Height、 - 改 Foreground/Background、Ok 应用到文档,确认除预览消失外布局与功能正常。 -4. 结论回填本文件,并在 issue #4325 上同步。 +## 验证(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 计划推进 QML 重构。 +- 本分支合入后关闭 issue #4325 的崩溃(回复验证方法与结论); +- 若后续恢复预览需求,应在 QML 重构版选择器中带预览,避开 Qt Widgets + 的 refreshable 同步重建机制。 From 0e909b14be55c15c47484a7b848b9f5c862c8754 Mon Sep 17 00:00:00 2001 From: Da Shen Date: Wed, 9 Sep 2026 08:52:20 +0800 Subject: [PATCH 4/4] =?UTF-8?q?[1287]=20gf=20fmt=20=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E5=8C=96=20pattern-selector.scm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TeXmacs/progs/generic/pattern-selector.scm | 144 ++++++++++----------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/TeXmacs/progs/generic/pattern-selector.scm b/TeXmacs/progs/generic/pattern-selector.scm index 72fe4c6d94..778a19c348 100644 --- a/TeXmacs/progs/generic/pattern-selector.scm +++ b/TeXmacs/progs/generic/pattern-selector.scm @@ -442,78 +442,78 @@ (tm-widget ((pattern-selector u) cmd) (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 + (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 >>>