排查日期:2026-08-11/12 · 系统:deepin 25(ostree 原子系统,X11 会话,dde-shell 2.0.52,bloom 图标主题) 结论状态:根因已在源码级确认,解决方案已按根因对应设计。
任务栏固定应用的身份(id)存储完全正确,错乱只发生在显示层:
dde-shell 任务管理器的 DockGlobalElementModel 把固定(驻留)条目以行号(row index)绑定到启动器应用模型上。冷启动时,它在 AM(Application Manager)尚未扫描完应用列表时就对行号做了快照,而代码只补偿了 rowsRemoved、没有补偿 rowsInserted——后续插入的应用使已快照的行号漂移,固定槽位从此读到的是恰好占据该行号的其他应用的名称和图标(例如 org.deepin.dde-shell 占位条目、dummyapp-wpsoffice 占位图标)。因为槽位的 id 始终正确,所以点击仍能打开正确的应用;当应用真正运行(条目被换绑到"活动窗口模型")后,显示便自愈。
DockGlobalElementModel
rowsRemoved
rowsInserted
org.deepin.dde-shell
dummyapp-wpsoffice
这解释了全部观察到的现象:每次重启必现、重新固定当时正常但重启后复发、错乱对象每次可能不同。
用户报告:任务栏中间几个固定应用图标,重启后与实际应用对不上;手动重新固定后当时正常,重启即复发。
实测(两次截图 + DBus 交互验证):
dde-file-manager
microsoft-edge
deepin-terminal
/usr/share/icons/hicolor/scalable/apps/dummyapp-wpsoffice.svg
DISPLAY=:0
dde-shell
dde-shell@DDE.service
org.desktopspec.ApplicationManager1.service
/usr/share/applications
/var/lib/linglong/entries/apps/share/applications
逐层排除了各候选存储位置后,用 dde-dconfig 直接读到生效值:
dde-dconfig
$ dde-dconfig get -a org.deepin.dde.shell -r org.deepin.ds.dock.taskmanager -k dockedElements ["desktop/dde-file-manager", "desktop/microsoft-edge", "desktop/deepin-terminal"] $ dde-dconfig get ... -k dockedElements -m isDefaultValue false # ← 用户自定义值,且 id 正确无误
结论:固定配置写入、持久化都没有问题。(物理存储由系统服务 dde-dconfig-daemon 管理。)
dde-dconfig-daemon
id="microsoft-edge"
icon="microsoft-edge"
name="Microsoft Edge"
isDocked=true
/usr/share/applications/org.deepin.dde-shell.desktop
NoDisplay=true
Exec=/bin/false
Icon=dde-shell
application-default-icon
wmctrl
microsoft_2dedge
microsoft-edge → Name "Microsoft Edge" Icon "microsoft-edge" ✅ com.microsoft.Edge → Name "Microsoft Edge" Icon "microsoft-edge" ✅(双 id 并存) org.deepin.dde-shell → Name "Deepin Desktop Shell" Icon "dde-shell" ← 错乱时被"借位"的条目
23:35:19 dde-application-manager 开始扫描桌面文件(数百个,持续数秒) 23:35:20 AM 仍在填充中(日志:couldn't find app "dde-shell@DDE") 23:35:21 dde-shell(dock)初始化 ← 此刻任务管理器对固定条目做行号快照
dock 初始化与 AM 扫描高度重叠,竞态窗口在本次开机日志中直接可见。
涉及代码:linuxdeepin/dde-shell 仓库 panels/dock/taskmanager/(已对照本机 2.0.52 二进制符号,结构与行为一致)。
linuxdeepin/dde-shell
panels/dock/taskmanager/
TaskManager::init()(taskmanager.cpp)取启动器(dde-apps)的 appModel 构造全局模型:
TaskManager::init()
appModel
m_dockGlobalElementModel = new DockGlobalElementModel(model, m_activeAppModel, this);
DockGlobalElementModel::loadDockedElements()(dockglobalelementmodel.cpp)把每个固定条目存为三元组:
DockGlobalElementModel::loadDockedElements()
m_data.append(std::make_tuple(id, m_appsModel, row)); // row = 该 id 当前在 appsModel 里的行号
显示时 data() 对 NameRole / IconNameRole 等一律按存储的行号回读:
data()
default: { if (model) return model->index(row, 0).data(role); }
构造函数里对 m_appsModel 只连接了 rowsRemoved 做行号平移;对 m_activeAppModel 才有 rowsInserted 处理。m_appsModel 的 rowsInserted / dataChanged / modelReset 均无处理。
m_appsModel
m_activeAppModel
dataChanged
modelReset
冷启动时 AM 正在向 appModel 持续插入应用(3.5),loadDockedElements() 快照的行号随后整体漂移:
loadDockedElements()
ItemIdRole
requestNewInstance()
dde-am
isDocked
m_dockedElements
即:模型里"身份按 id、显示按行号",行号漂移后两者分离——这正是"图标和应用对不上,但点击又是对的"这一核心体验的来源。
(id, m_activeAppModel, i)
IconNameRole
NameRole
~/.config/dconf/user
entries/apps/share/applications
建议向 linuxdeepin/dde-shell 提交 issue/PR,要点:
appModelReadyChanged
反馈渠道:GitHub linuxdeepin/dde-shell issue 或官方论坛 https://bbs.deepin.org.cn。
原理:竞态只发生在冷启动快照时刻。待 AM 扫描稳定后重启一次 dde-shell@DDE.service,任务管理器会在稳定的应用模型上重新快照行号,显示即全部正确。该操作只重建 dock/通知面板,不影响任何应用窗口和系统设置,可随时撤销。
等待策略采用轮询判定而非固定时长的 sleep,分两个阶段:
AM 迟迟不就绪(计数为 0)时不会误判稳定。
已部署两个文件:
~/.local/bin/fix-dock-icons.sh(已 chmod +x):
~/.local/bin/fix-dock-icons.sh
chmod +x
#!/bin/bash # 日志:journalctl -b -t fix-dock-icons # 注意:log 只能写 stderr 和 journal——wait_stable 经 $() 调用,stdout 会被捕获为返回值。 log() { echo "fix-dock-icons: $*" >&2 logger -t fix-dock-icons -- "$*" 2>/dev/null || true } # 仅统计 AM 的直接子对象(应用条目 + JobManager1/MimeManager1 两个常量), # 排除应用运行后挂在其下的实例子对象(路径多一段),避免应用启动干扰计数。 am_count() { busctl --user tree org.desktopspec.ApplicationManager1 2>/dev/null | grep -c '/org/desktopspec/ApplicationManager1/[^/]*$' } # 等待应用数连续 $1 秒稳定($2 秒硬上限),输出最终计数 wait_stable() { local win=$1 cap=$2 last=-1 stable=0 waited=0 n while [ "$stable" -lt "$win" ] && [ "$waited" -lt "$cap" ]; do n=$(am_count) if [ "$n" != "$last" ]; then log "应用数变化: $last -> $n"; fi if [ "$n" -gt 0 ] && [ "$n" = "$last" ]; then stable=$((stable+1)); else stable=0; fi last=$n; waited=$((waited+1)); sleep 1 done echo "$last" } # 重建 dock 并如实记录成败(依赖全局 restarts/baseline 仅用于日志) restart_dock() { if systemctl --user restart dde-shell@DDE.service; then log "第 $restarts 次重建 dock(baseline=$baseline)" else log "警告:第 $restarts 次重建 dock 失败(systemctl 退出码 $?)" fi } # 单实例保护:快速重复登录或手动误执行时直接退出 exec 9>"${XDG_RUNTIME_DIR:-/tmp}/fix-dock-icons.lock" flock -n 9 || { log "已有实例在运行,退出"; exit 0; } log "启动:开始等待 AM 扫描稳定(3s 窗口 / 20s 上限)" baseline=$(wait_stable 3 20) restarts=1 restart_dock watch=0 while [ "$watch" -lt 10 ]; do sleep 1; watch=$((watch+1)) n=$(am_count) if [ "$n" -gt 0 ] && [ "$n" != "$baseline" ]; then if [ "$restarts" -lt 3 ]; then log "监视期发现应用数变化 $baseline -> $n,判定阶段1误判,重新等待稳定" baseline=$(wait_stable 3 20) restarts=$((restarts+1)) restart_dock watch=0 else log "应用数仍变化($baseline -> $n)但已达重启上限,停止监视" break fi fi done log "监视期结束,退出(共重建 $restarts 次,最终应用数=$baseline)"
已通过的测试(桩替换 busctl/systemctl/logger + 直连真实 AM 端到端演练):正常场景恰重建 1 次;"扫描中途停顿→误判→监视期发现计数变化→自愈重建"场景恰重建 2 次;单实例保护生效(持锁时拒绝运行);重建命令失败时日志如实告警。日志全量落 journal(journalctl -b -t fix-dock-icons 查看)。
journalctl -b -t fix-dock-icons
历轮检视修复的实现缺陷汇总:① 初版误用 busctl call(缺方法名报错退化为固定延迟,见上节注意);② 旧计数口径把应用实例/任务子对象计入(应用一启动计数就变,干扰稳定判定、误触自愈),已改为仅统计 AM 直接子对象;③ 日志若写 stdout 会被 baseline=$(wait_stable ...) 的命令替换捕获、污染数值比较,log 强制只写 stderr+journal;④ 重建命令失败原本静默,已加成败日志;⑤ 补单实例保护(flock)。
busctl call
baseline=$(wait_stable ...)
重启后验证方法:登录后执行 journalctl -b -t fix-dock-icons 应看到"启动→第 1 次重建 dock→监视期结束"的完整记录(典型 6–8 秒内触发重建);systemctl --user show dde-shell@DDE.service -p ActiveEnterTimestamp 可确认 dock 实际重建时刻;任务栏固定图标应全部正确显示。
systemctl --user show dde-shell@DDE.service -p ActiveEnterTimestamp
验证结果(2026-08-12 深夜,连续两次重启):✅ 通过。两次均恰重建 1 次、无误判无重试;重建时刻与 ActiveEnterTimestamp 精确吻合(23:56:04 / 23:58:05,即登录后 4–5 秒);重建后全部固定图标正确(DBus 热状态复核通过)。已知固有形态:登录后前 4–5 秒图标仍错乱(竞态发生在 dock 首次快照、脚本须等 AM 连续 3 秒稳定才能重建),随后 dock 重建闪一下即恢复——该窗口是"事后重建"思路的下限。备选"事前延迟 dock 首次启动"方案(ExecStartPre 等 AM 稳定,图标开机即对、无闪烁,代价是面板晚 4–5 秒出现 + 需调大单元 TimeoutStartSec)已评估并记录,用户选择保持现状。
ActiveEnterTimestamp
注意:查询命令是 busctl --user tree ... | grep -c(统计 AM 下应用对象数,本机当前 130)。本报告早期版本误用 busctl --user call <服务> <对象> <接口>——call 必须带第 4 个参数(方法名),缺参会报 "Too few arguments",被 2>/dev/null 吞掉后计数恒为 0,恰好等于 last 初值 0,会退化成约 9 秒后无条件重启。已实测验证并修正(last 初始化为 -1、要求 n>0,双重防巧合)。
busctl --user tree ... | grep -c
busctl --user call <服务> <对象> <接口>
call
2>/dev/null
last
n>0
~/.config/autostart/deepin-dock-icon-fix.desktop:
~/.config/autostart/deepin-dock-icon-fix.desktop
[Desktop Entry] Type=Application Name=Dock Icon Race Workaround Comment=待应用管理器扫描稳定后重建 dock,规避冷启动固定图标错位 Exec=/home/meteor/.local/bin/fix-dock-icons.sh NoDisplay=true X-GNOME-Autostart-enabled=true
(Exec 用绝对路径:desktop entry 不展开 ~/$HOME。)
~
$HOME
# 固定列表生效值与来源 dde-dconfig get -a org.deepin.dde.shell -r org.deepin.ds.dock.taskmanager -k dockedElements dde-dconfig get -a org.deepin.dde.shell -r org.deepin.ds.dock.taskmanager -k dockedElements -m isDefaultValue # 任务管理器条目与属性 busctl --user tree org.deepin.ds.Dock.TaskManager busctl --user get-property org.deepin.ds.Dock.TaskManager \ /org/deepin/ds/Dock/TaskManager/Item/AppItem/microsoft_2dedge \ org.deepin.ds.Dock.TaskManager.Item icon # AM 对应用的解析 busctl --user get-property org.desktopspec.ApplicationManager1 \ /org/desktopspec/ApplicationManager1/microsoft_2dedge \ org.desktopspec.ApplicationManager1.Application ID Name Icons # dock 进程归属 dbus-send --session --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus \ org.freedesktop.DBus.GetConnectionUnixProcessID string:org.deepin.dde.Dock1
本报告由逐层排查(截图取证 → 配置核查 → DBus 实证 → 日志时序 → 源码比对)得出,全程未修改任何系统设置。
No replies yet
Featured Collection
Popular Events
一、摘要(TL;DR)
任务栏固定应用的身份(id)存储完全正确,错乱只发生在显示层:
dde-shell 任务管理器的
DockGlobalElementModel把固定(驻留)条目以行号(row index)绑定到启动器应用模型上。冷启动时,它在 AM(Application Manager)尚未扫描完应用列表时就对行号做了快照,而代码只补偿了rowsRemoved、没有补偿rowsInserted——后续插入的应用使已快照的行号漂移,固定槽位从此读到的是恰好占据该行号的其他应用的名称和图标(例如org.deepin.dde-shell占位条目、dummyapp-wpsoffice占位图标)。因为槽位的 id 始终正确,所以点击仍能打开正确的应用;当应用真正运行(条目被换绑到"活动窗口模型")后,显示便自愈。这解释了全部观察到的现象:每次重启必现、重新固定当时正常但重启后复发、错乱对象每次可能不同。
二、现象记录
用户报告:任务栏中间几个固定应用图标,重启后与实际应用对不上;手动重新固定后当时正常,重启即复发。
实测(两次截图 + DBus 交互验证):
dde-file-managermicrosoft-edge(用户固定的 Edge)deepin-terminal/usr/share/icons/hicolor/scalable/apps/dummyapp-wpsoffice.svg(WPS 未安装占位图标)三、排查与证据链
3.1 环境
DISPLAY=:0),新版 dock 由dde-shell承载(非旧版 dde-dock)dde-shell@DDE.service(PID 3884),AM 为org.desktopspec.ApplicationManager1.service(PID 3136)/usr/share/applications)+ 玲珑(/var/lib/linglong/entries/apps/share/applications)3.2 固定列表的持久化——存储完全正常
逐层排除了各候选存储位置后,用
dde-dconfig直接读到生效值:结论:固定配置写入、持久化都没有问题。(物理存储由系统服务
dde-dconfig-daemon管理。)3.3 运行时实证(DBus)
id="microsoft-edge",icon="microsoft-edge",name="Microsoft Edge",isDocked=true—— 身份与驻留状态均正确。/usr/share/applications/org.deepin.dde-shell.desktop(NoDisplay=true、Exec=/bin/false、Icon=dde-shell)的名称;dde-shell图标在主题中不存在,于是渲染为回退图标application-default-icon(深色 "i" 方块)。wmctrl出现 Edge 窗口,TaskManager 新增microsoft_2dedge条目)。3.4 AM 热状态解析全部正确
3.5 开机时序——竞态窗口客观存在
dock 初始化与 AM 扫描高度重叠,竞态窗口在本次开机日志中直接可见。
四、根因(源码级确认)
涉及代码:
linuxdeepin/dde-shell仓库panels/dock/taskmanager/(已对照本机 2.0.52 二进制符号,结构与行为一致)。4.1 绑定机制
TaskManager::init()(taskmanager.cpp)取启动器(dde-apps)的appModel构造全局模型:DockGlobalElementModel::loadDockedElements()(dockglobalelementmodel.cpp)把每个固定条目存为三元组:显示时
data()对 NameRole / IconNameRole 等一律按存储的行号回读:4.2 缺陷:只补偿删除、不补偿插入
构造函数里对
m_appsModel只连接了rowsRemoved做行号平移;对m_activeAppModel才有rowsInserted处理。m_appsModel的rowsInserted/dataChanged/modelReset均无处理。冷启动时 AM 正在向 appModel 持续插入应用(3.5),
loadDockedElements()快照的行号随后整体漂移:microsoft-edge的快照行 → 漂移后指向org.deepin.dde-shell占位条目 → 名称显示 "Deepin Desktop Shell",图标dde-shell缺失 → 回退图标。dummyapp-wpsoffice→ 显示"WPS 下载"占位图标。4.3 为什么身份仍然正确
ItemIdRole返回的是三元组里的 id(不是行号),点击启动走requestNewInstance()→dde-am→ 始终打开正确应用;isDocked按 id 查m_dockedElements,也正确。即:模型里"身份按 id、显示按行号",行号漂移后两者分离——这正是"图标和应用对不上,但点击又是对的"这一核心体验的来源。
4.4 为什么会自愈、为什么重新固定无效、为什么每次受害者不同
(id, m_activeAppModel, i)(活动窗口模型的行号有插入/删除双向补偿),显示随之恢复正确。(附带小瑕疵:换绑时发出的dataChanged未包含IconNameRole/NameRole,所以有时要等后续状态变化才真正刷新——本次排查中终端图标就是在交互后恢复的。)4.5 已排除的备选假设
~/.config/dconf/user无任何 dock 键;新 dock 不读旧列表(仅迁移一次)entries/apps/share/applications,AM 注册正常五、解决方案
方案 A(根治,需上游修复):修正 DockGlobalElementModel 的行号维护
建议向
linuxdeepin/dde-shell提交 issue/PR,要点:m_appsModel增加rowsInserted(及modelReset)处理——按 id 重新 match 行号而非盲目做索引加减(启动器模型可能排序,插入位置任意);dataChanged的角色列表补上IconNameRole、NameRole;loadDockedElements()在appModelReadyChanged之后对既有条目强制重绑行号。反馈渠道:GitHub
linuxdeepin/dde-shellissue 或官方论坛 https://bbs.deepin.org.cn。方案 B(用户侧兜底,已部署):登录后待 AM 扫描稳定,在热状态重建一次 dock
原理:竞态只发生在冷启动快照时刻。待 AM 扫描稳定后重启一次
dde-shell@DDE.service,任务管理器会在稳定的应用模型上重新快照行号,显示即全部正确。该操作只重建 dock/通知面板,不影响任何应用窗口和系统设置,可随时撤销。等待策略采用轮询判定而非固定时长的 sleep,分两个阶段:
AM 迟迟不就绪(计数为 0)时不会误判稳定。
已部署两个文件:
~/.local/bin/fix-dock-icons.sh(已chmod +x):已通过的测试(桩替换 busctl/systemctl/logger + 直连真实 AM 端到端演练):正常场景恰重建 1 次;"扫描中途停顿→误判→监视期发现计数变化→自愈重建"场景恰重建 2 次;单实例保护生效(持锁时拒绝运行);重建命令失败时日志如实告警。日志全量落 journal(
journalctl -b -t fix-dock-icons查看)。历轮检视修复的实现缺陷汇总:① 初版误用
busctl call(缺方法名报错退化为固定延迟,见上节注意);② 旧计数口径把应用实例/任务子对象计入(应用一启动计数就变,干扰稳定判定、误触自愈),已改为仅统计 AM 直接子对象;③ 日志若写 stdout 会被baseline=$(wait_stable ...)的命令替换捕获、污染数值比较,log 强制只写 stderr+journal;④ 重建命令失败原本静默,已加成败日志;⑤ 补单实例保护(flock)。重启后验证方法:登录后执行
journalctl -b -t fix-dock-icons应看到"启动→第 1 次重建 dock→监视期结束"的完整记录(典型 6–8 秒内触发重建);systemctl --user show dde-shell@DDE.service -p ActiveEnterTimestamp可确认 dock 实际重建时刻;任务栏固定图标应全部正确显示。验证结果(2026-08-12 深夜,连续两次重启):✅ 通过。两次均恰重建 1 次、无误判无重试;重建时刻与
ActiveEnterTimestamp精确吻合(23:56:04 / 23:58:05,即登录后 4–5 秒);重建后全部固定图标正确(DBus 热状态复核通过)。已知固有形态:登录后前 4–5 秒图标仍错乱(竞态发生在 dock 首次快照、脚本须等 AM 连续 3 秒稳定才能重建),随后 dock 重建闪一下即恢复——该窗口是"事后重建"思路的下限。备选"事前延迟 dock 首次启动"方案(ExecStartPre 等 AM 稳定,图标开机即对、无闪烁,代价是面板晚 4–5 秒出现 + 需调大单元 TimeoutStartSec)已评估并记录,用户选择保持现状。~/.config/autostart/deepin-dock-icon-fix.desktop:(Exec 用绝对路径:desktop entry 不展开
~/$HOME。)dde-shell@DDE.service(3.1)。不建议的做法
/usr/share/applications下的占位 desktop 文件:ostree 只读且会被更新覆盖,且与根因无关;附录 A:关键验证命令(可复现)
本报告由逐层排查(截图取证 → 配置核查 → DBus 实证 → 日志时序 → 源码比对)得出,全程未修改任何系统设置。