Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 73 additions & 105 deletions TeXmacs/progs/generic/pattern-selector.scm
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@

(define (set-color col)
(set! global-pattern-color col)
(refresh-now "pattern-sample")
(refresh-now "pattern-options")
) ;define

Expand Down Expand Up @@ -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 >>>
Expand Down
117 changes: 117 additions & 0 deletions devel/1287.md
Original file line number Diff line number Diff line change
@@ -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 同步重建机制。
Loading