<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Planet deepin | 简体中文</title><link>https://planet.deepin.org/</link><description>Planet deepin | 简体中文</description><image><link>https://planet.deepin.org/</link><title>Planet deepin</title><url>https://planet.deepin.org/img/planet.png</url><height>48</height><width>48</width></image><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>2026-06-05T00:00:00+00:00</lastBuildDate><atom:link href="https://planet.deepin.org/index.xml" rel="self" type="application/rss+xml"/><item><title>deepin-ports SIG 周报 (2026-06-05)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260605-weekly/</link><pubDate>Fri, 05 Jun 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260605-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>已推进新版 aic8800 合入仓库，以便 7.0 以上内核能够使用。&lt;/li>&lt;li>已将 SC8280XP 相关固件推送进 linux-firmware。&lt;/li>&lt;li>将 q6a 镜像重命名为 qcom，适配 Radxa 旗下所有 qcom 开发板。&lt;/li>&lt;li>为 deepin-ports-image 增加了 next 仓库 profile。&lt;/li>&lt;li>发布了一版 qcom 镜像，基于 deepin 25.1.1。&lt;/li>&lt;/ul>&lt;h3 id="其它">其它&lt;/h3>&lt;ul>&lt;li>参加了 Radxa &amp;#x26; 高通开发者大会，在会上作 deepin on arm64 (qcom) 设备展示。
&lt;ul>&lt;li>撰写并发布了公众号新闻：&lt;a href="https://mp.weixin.qq.com/s/kGKetWMrAVMZUFwlCh4fiA" rel="nofollow">https://mp.weixin.qq.com/s/kGKetWMrAVMZUFwlCh4fiA&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 能够尽快获得通过，以便正式确定技术规范。&lt;/li>&lt;li>进行一些基础库的重构，以便利用 RVA23 提升性能。&lt;/li>&lt;li>准备在 deepin-ports-image 中增加单独的 RVA23 仓库选项。&lt;/li>&lt;li>继续升级 rust 工具链至 1.88+。&lt;/li>&lt;li>升级 mesa 26.1.0。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配">关键设备适配&lt;/h3>&lt;ul>&lt;li>在 K3 上测试使用 RVA23 仓库并进行 benchmark。&lt;/li>&lt;li>修改 deepin-ports-kernel 的 workflow 以支持使用 GCC 15+ 构建。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;li>deepin-ports-image 重构&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-05-22)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260522-weekly/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260522-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="关键设备适配-进展预估-60">关键设备适配 &lt;span>进展预估 60%&lt;/span>&lt;/h3>&lt;ul>&lt;li>已与厂商联系并针对发布的最新内核打包了一版 K3 镜像。&lt;/li>&lt;li>厂商在使用新内核后反映 K3 GPU 已能正常使用。&lt;/li>&lt;li>已发布 K3 的安装指南。&lt;/li>&lt;/ul>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>发布了两版 K1 镜像。&lt;/li>&lt;li>K1 镜像测试报告不通过，修复缺失包的问题后重新打包镜像。&lt;/li>&lt;li>接收来自开发板漂流计划的两份 K1 的测试报告。&lt;/li>&lt;li>适配各开发板的 ffmpeg patch 版本因为上游安全更新，正在重新打包。&lt;/li>&lt;li>已打包来自 Radxa 的 qcom 7.0 内核，适用于 Radxa Q6A 和 Q8B。&lt;/li>&lt;/ul>&lt;h3 id="其它">其它&lt;/h3>&lt;ul>&lt;li>向 Open Build Service 提交了允许在 scmsync 模式下 trigger service 的 PR。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 能够尽快获得通过，以便正式确定技术规范。&lt;/li>&lt;li>进行一些基础库的重构，以便利用 RVA23 提升性能。&lt;/li>&lt;li>准备在 deepin-ports-image 中增加单独的 RVA23 仓库选项。&lt;/li>&lt;li>继续升级 rust 工具链至 1.88+。&lt;/li>&lt;li>升级 mesa 26.1.0。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配">关键设备适配&lt;/h3>&lt;ul>&lt;li>在 K3 上测试使用 RVA23 仓库并进行 benchmark。&lt;/li>&lt;li>修改 deepin-ports-kernel 的 workflow 以支持使用 GCC 15+ 构建。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;li>deepin-ports-image 重构&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-05-15)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260515-weekly/</link><pubDate>Fri, 15 May 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260515-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>更新了 k1 的 deepin 25.1 镜像。&lt;/li>&lt;/ul>&lt;h3 id="其它">其它&lt;/h3>&lt;ul>&lt;li>根据 TC 会议精神，提出两个需求，已被采纳并加入需求列表：
&lt;ul>&lt;li>deepin live 模式下应当写入 sources.list 以实现 live 模式下安装软件包&lt;/li>&lt;li>deepin live 模式下应当在桌面显示水印，提示用户当前为 live 模式&lt;/li>&lt;/ul>&lt;/li>&lt;li>开发板漂流计划：已寄出一块 K1 开发板。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 能够尽快获得通过，以便正式确定技术规范。&lt;/li>&lt;li>进行一些基础库的重构，以便利用 RVA23 提升性能。&lt;/li>&lt;li>准备在 deepin-ports-image 中增加单独的 RVA23 仓库选项。&lt;/li>&lt;li>继续升级 rust 工具链至 1.88+。&lt;/li>&lt;li>升级 mesa 26.1.0。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配">关键设备适配&lt;/h3>&lt;ul>&lt;li>在 K3 上尝试使用 Imagination 开源驱动方案结合 Zink 启动桌面。&lt;/li>&lt;li>在 K3 上测试使用 RVA23 仓库并进行 benchmark。&lt;/li>&lt;li>修改 deepin-ports-kernel 的 workflow 以支持使用 GCC 15+ 构建。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;li>deepin-ports-image 重构&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-05-08)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260508-weekly/</link><pubDate>Fri, 08 May 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260508-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="rva23-仓库-进展预估-55">RVA23 仓库 &lt;span>进展预估 55%&lt;/span>&lt;/h3>&lt;ul>&lt;li>已在新设备（amd64 + riscv64）上基于 deepin 25.1 叠加 RVA23 仓库并升级验证了仓库的可用性，无存在冲突的软件包。&lt;/li>&lt;li>已验证 mesa 26.1.0 可在现有 RVA23 仓库基线上构建。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配-进展预估-50">关键设备适配 &lt;span>进展预估 50%&lt;/span>&lt;/h3>&lt;ul>&lt;li>观测到上游已正式发布了 K3 的 SDK，已在 deepin-ports-kernel 中增加了对 K3 6.18 内核和 u-boot 的构建。&lt;/li>&lt;li>已与厂商沟通，预计下周进行会议讨论 GPU 适配事宜。&lt;/li>&lt;/ul>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>为用户在浏览器端直接创建可一次写入的镜像，已发布 wasm-imager: 一个基于 WebAssembly 的镜像创建工具。&lt;/li>&lt;li>基于以上工具，在 deepin-ports SIG 网站上增加了镜像创建页面。该功能目前尚在测试阶段，待进一步验证。&lt;/li>&lt;/ul>&lt;h3 id="其它">其它&lt;/h3>&lt;ul>&lt;li>已收到 Radxa/高通 有关开发者大会的邀请，将进行展位的准备工作。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 能够尽快获得通过，以便正式确定技术规范。&lt;/li>&lt;li>进行一些基础库的重构，以便利用 RVA23 提升性能。&lt;/li>&lt;li>准备在 deepin-ports-image 中增加单独的 RVA23 仓库选项。&lt;/li>&lt;li>继续升级 rust 工具链至 1.88+。&lt;/li>&lt;li>升级 mesa 26.1.0。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配">关键设备适配&lt;/h3>&lt;ul>&lt;li>在 K3 上尝试使用 Imagination 开源驱动方案结合 Zink 启动桌面。&lt;/li>&lt;li>在 K3 上测试使用 RVA23 仓库并进行 benchmark。&lt;/li>&lt;li>修改 deepin-ports-kernel 的 workflow 以支持使用 GCC 15+ 构建。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;li>deepin-ports-image 重构&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-04-30)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260430-weekly/</link><pubDate>Thu, 30 Apr 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260430-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="rva23-仓库-进展预估-55">RVA23 仓库 &lt;span>进展预估 55%&lt;/span>&lt;/h3>&lt;ul>&lt;li>尝试在 deepin next / RVA23 仓库推进 gcc 16。&lt;/li>&lt;li>在 deepin next / RVA23 仓库完成 rust 1.82 的升级，四大架构均完成构建。&lt;/li>&lt;li>在 deepin next / RVA23 仓库推进 mesa 26 及其依赖 (meson/vulkan/libdrm) 升级，四大架构均完成构建。&lt;/li>&lt;/ul>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>修复了 deepin-ports-kernel 的构建 workflow。&lt;/li>&lt;li>推送了 generic-revyos-617y 内核到 deepin-ports 内核仓库。&lt;/li>&lt;/ul>&lt;h3 id="其它">其它&lt;/h3>&lt;ul>&lt;li>参与 RISC-V Meetup Wuhan 并作有关 RVA23/next 仓库进展和 deepin 开发板漂流计划的报告。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 下周内获得通过，以便正式确定技术规范。&lt;/li>&lt;li>进行一些基础库的重构，以便利用 RVA23 提升性能。&lt;/li>&lt;li>准备在 deepin-ports-image 中增加单独的 RVA23 仓库选项，并在 k3 上测试 RVA23 仓库。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;li>deepin-ports-image 重构&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-04-17)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260417-weekly/</link><pubDate>Fri, 17 Apr 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260417-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="rva23-仓库-进展预估-50">RVA23 仓库 &lt;span>进展预估 50%&lt;/span>&lt;/h3>&lt;ul>&lt;li>尝试在 deepin next / RVA23 仓库推进 mesa 26 并推进部分包的升级。
&lt;ul>&lt;li>rust-bindgen-cli&lt;/li>&lt;li>directx-headers&lt;/li>&lt;li>libdrm&lt;/li>&lt;li>meson&lt;/li>&lt;li>spirv-tools&lt;/li>&lt;li>spirv-llvm-translator-21&lt;/li>&lt;/ul>&lt;/li>&lt;li>尝试在 deepin next / RVA23 仓库推进 llvm 22。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配-进展预估-40">关键设备适配 &lt;span>进展预估 40%&lt;/span>&lt;/h3>&lt;ul>&lt;li>已与厂商讨论 GPU 适配相关事项。如果未能适配闭源驱动可能考虑开源 zink 方案或先使用软件渲染。&lt;/li>&lt;li>从上游重新拉取、打包和更新了 K3 的内核。&lt;/li>&lt;/ul>&lt;h3 id="其它">其它&lt;/h3>&lt;ul>&lt;li>已定于在下周于武汉举行的 RISC-V Meetup，deepin-ports 将作 deepin next 相关分享，并进行开发板漂流活动宣讲。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 下周内获得通过，以便正式确定技术规范。&lt;/li>&lt;li>进行一些基础库的重构，以便利用 RVA23 提升性能。&lt;/li>&lt;li>准备在 deepin-ports-image 中增加单独的 RVA23 仓库选项，并在 k3 上测试 RVA23 仓库。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;li>deepin-ports-image 重构&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-04-10)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260410-weekly/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260410-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="rva23-仓库-进展预估-50">RVA23 仓库 &lt;span>进展预估 50%&lt;/span>&lt;/h3>&lt;p>由于构建机器进行搬迁，尚未连接构建系统，本周暂无进展。&lt;/p>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>天气炎热，已将 4 台 SG2042 机器搬迁至有空调的调试机房，网络尚在配置中。&lt;/li>&lt;li>在调试间重新安装和配置 SG2044 机器。&lt;/li>&lt;/ul>&lt;h3 id="其它">其它&lt;/h3>&lt;ul>&lt;li>在 TC 会议上跟进了 ISO Live 体验相关的 RFC 事项。&lt;/li>&lt;li>预计在 4 月末将在武汉举行 RISC-V Meetup，已联系运营进行合作，届时将同步开展开发板漂流计划的宣讲和第一阶段的分发。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 下周内获得通过，以便正式确定技术规范。&lt;/li>&lt;li>进行一些基础库的重构，以便利用 RVA23 提升性能。&lt;/li>&lt;li>准备在 deepin-ports-image 中增加单独的 RVA23 仓库选项，并在 k3 上测试 RVA23 仓库。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配-部分已推迟">关键设备适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>等待厂商配合修复 GPU 问题。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;li>deepin-ports-image 重构&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-04-02)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260402-weekly/</link><pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260402-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="rva23-仓库-进展预估-50">RVA23 仓库 &lt;span>进展预估 50%&lt;/span>&lt;/h3>&lt;ul>&lt;li>&lt;code class="">llvm-toolchain-21&lt;/code> 在 &lt;code class="">loong64&lt;/code> 上已打包完成，基础仓库就绪。&lt;/li>&lt;li>仓库已在以下地址发布：
&lt;pre class="language-text" code="deb [trusted=yes] https://ci.deepin.com/repo/obs/deepin:/CI:/ports:/rva23/testing/ ./
" language="text">&lt;code __ignoremap="">deb [trusted=yes] https://ci.deepin.com/repo/obs/deepin:/CI:/ports:/rva23/testing/ ./
&lt;/code>&lt;/pre>&lt;/li>&lt;/ul>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>deepin-ports SIG 页面前端：
&lt;ul>&lt;li>更新了依赖，解决构建失败问题，暂时 pin vue 版本至 3.5.30 直到 vuetify 发布新版本。&lt;/li>&lt;/ul>&lt;/li>&lt;li>deepin-ports-kernel 刷新上游版本：
&lt;ul>&lt;li>弃用闭源补丁流程，现在 deepin-ports 仅支持完全开源的内核代码。&lt;/li>&lt;li>弃用树莓派 (bcm2711/bcm2712) 的 6.12 版本内核，保留 6.18 版本。&lt;/li>&lt;li>弃用 DC ROMA 1 的 5.15 定制内核。&lt;/li>&lt;li>更新 CIX P1 的内核至 2025Q4 SDK。&lt;/li>&lt;li>弃用 SG2042 的 6.6 内核。&lt;/li>&lt;li>将来自 collabora 的 RK3588 内核更名为 &lt;code class="">rk3588-collabora-619y&lt;/code>。&lt;/li>&lt;/ul>&lt;/li>&lt;li>deepin-ports 仓库：
&lt;ul>&lt;li>针对 A7A 的相关变动，更新了 A733 的内核，后续镜像将提供两种规格 A7A 的支持。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 下周内获得通过，以便正式确定技术规范。&lt;/li>&lt;li>进行一些基础库的重构，以便利用 RVA23 提升性能。&lt;/li>&lt;li>准备在 deepin-ports-image 中增加单独的 RVA23 仓库选项，并在 k3 上测试 RVA23 仓库。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配-部分已推迟">关键设备适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>等待厂商配合修复 GPU 问题。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;li>deepin-ports-image 重构&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-03-27)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260327-weekly/</link><pubDate>Fri, 27 Mar 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260327-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="rva23-仓库-进展预估-45">RVA23 仓库 &lt;span>进展预估 45%&lt;/span>&lt;/h3>&lt;p>开始建立 deepin next 仓库，初始软件包主要为工具链。为保证隔离，仓库将独立于 deepin 主线和 deepin-ports 仓库。&lt;/p>&lt;ul>&lt;li>已在 SG2044 (模拟 RVA23) 设备上测试上述仓库的基础可用性。&lt;/li>&lt;li>已在 ci.deepin.com 建立 deepin next 仓库，支持四大架构。目前依然使用 deepin-ports-keyring 签名。后续会将此仓库转移到可公开访问的路径下。&lt;/li>&lt;li>loong64 因为构建机内存不足，本周依然在重新尝试打包 &lt;code class="">llvm-toolchain-21&lt;/code>。&lt;/li>&lt;/ul>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>已安排一台空闲的 DP1000 设备协助玲珑进行 RISC-V 架构软件包仓库初始化。&lt;/li>&lt;li>已与运营开始协商规划 deepin 开发板漂流计划，预计下周形成初步方案。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 下周内获得通过，以便正式确定技术规范。&lt;/li>&lt;li>进行一些基础库的重构，以便利用 RVA23 提升性能。&lt;/li>&lt;li>准备在 deepin-ports-image 中增加单独的 RVA23 仓库选项，并在 k3 上测试 RVA23 仓库。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配-部分已推迟">关键设备适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>等待厂商配合修复 GPU 问题。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;li>deepin-ports-image 重构&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-03-20)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260320-weekly/</link><pubDate>Fri, 20 Mar 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260320-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="rva23-仓库-进展预估-45">RVA23 仓库 &lt;span>进展预估 45%&lt;/span>&lt;/h3>&lt;p>RVA23 基础工具链基线提升已完成，开始进行部分包的性能优化。&lt;/p>&lt;ul>&lt;li>已修改 &lt;code class="">dpkg&lt;/code> 为 riscv64 默认使用 rva23u64 基线。&lt;/li>&lt;li>已修改 &lt;code class="">gcc-15&lt;/code> 使用 rva23u64 ISA string 作为构建 flag。&lt;/li>&lt;li>已修改 &lt;code class="">llvm-toolchain-21&lt;/code> 启用 rva23 基线。&lt;/li>&lt;li>已基于 rva23 基线重构 &lt;code class="">glibc&lt;/code>。&lt;/li>&lt;li>除 loong64 因为构建机内存不足暂无法打包 &lt;code class="">llvm-toolchain-21&lt;/code>，其余所有升级均在四大架构全部构建完毕。&lt;/li>&lt;/ul>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>已为 Radxa A7A 的两种音频芯片（AC101/AC101B）修改 u-boot 和内核，用户可以自行根据手中的设备进行选择。
&lt;ul>&lt;li>以上修改已在 Radxa 寄送的 A7A 设备上测试完毕。相关 PR 已合入，对应产物等待推进仓库。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 下周内获得通过，以便正式确定技术规范。&lt;/li>&lt;li>创建 RVA23 (next) 仓库并合入已构建出的软件包，然后继续进行 RVA23 基线提升工作。&lt;/li>&lt;li>准备在 deepin-ports-image 中增加单独的 RVA23 仓库选项，并在 k3 上测试 RVA23 仓库。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配-部分已推迟">关键设备适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>等待厂商配合修复 GPU 问题。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;li>deepin-ports-image 重构&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-03-06)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260306-weekly/</link><pubDate>Fri, 06 Mar 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260306-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="rva23-仓库-进展预估-45">RVA23 仓库 &lt;span>进展预估 45%&lt;/span>&lt;/h3>&lt;p>本周对工具链的工作进行收尾测试，初步开始 RVA23 基线的打包验证。&lt;/p>&lt;ul>&lt;li>四个架构上的 native 工具链均已打包完成，处于就绪状态。&lt;/li>&lt;li>交叉工具链在 amd64/arm64 上打包完成，在 loong64/riscv64 上的打包修复作为后续处理事项。&lt;/li>&lt;li>已将整个 rva23 项目设置为仅在 RVA23 profile 的 SG2044 上打包。&lt;/li>&lt;li>已更新 dpkg 调整 RVA23 编译参数，并两次构建成功，初步验证了基线提升的效果。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配-进展预估-40">关键设备适配 &lt;span>进展预估 40%&lt;/span>&lt;/h3>&lt;p>针对 SpacemiT K3 平台，本周完成了图形化验证，驱动等待厂商更新。&lt;/p>&lt;ul>&lt;li>从厂商处获取了电源适配器，从而允许 typec DP 输出以调试图形界面。&lt;/li>&lt;li>成功进入 deepin 安装界面，安装成功后成功进入完整的 deepin 桌面。&lt;/li>&lt;li>GPU 驱动存在问题，已打包初步镜像供厂商调试使用。&lt;/li>&lt;/ul>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>收到用户关于 A733 A7A 设备的反馈，已查明是不同型号设备的硬件规格不一致导致的。
&lt;ul>&lt;li>已收到来自 radxa 的 A7A 设备，但目前没有正常工作的 SD 读卡器。任务暂时延后。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;blockquote>&lt;p>下周参加 FOSSAsia，顺延到下下周。&lt;/p>&lt;/blockquote>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 下周内获得通过，以便正式确定技术规范。&lt;/li>&lt;li>创建 RVA23 (next) 仓库并合入已构建出的软件包，然后继续进行 RVA23 基线提升工作。&lt;/li>&lt;li>准备在 deepin-ports-image 中增加单独的 RVA23 仓库选项，并在 k3 上测试 RVA23 仓库。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配-部分已推迟">关键设备适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>等待厂商配合修复 GPU 问题。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;li>deepin-ports-image 重构&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-02-27)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260227-weekly/</link><pubDate>Fri, 27 Feb 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260227-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="rva23-仓库-进展预估-40">RVA23 仓库 &lt;span>进展预估 40%&lt;/span>&lt;/h3>&lt;p>本周投入了大量精力进行工具链的升级和版本对齐，同时还打包了交叉编译的相关基础工具链。&lt;/p>&lt;ul>&lt;li>SG2044 已成功部署基于 RVA23 模拟的 OpenSBI 固件，并顺利接入构建系统，作为 deepin 社区第一台完全符合 RVA23 标准的构建机，大幅提升了 RVA23 软件包的编译效率。&lt;/li>&lt;li>跟进 GCC 上游，向 GCC-15 Backport 了几个 RVA23U64 Profile 的支持补丁。完成了针对 &lt;code class="">-march=rva23u64&lt;/code> 编译选项的构建验证。&lt;/li>&lt;li>交叉工具链方面，完成 gcc-15-cross / cross-toolchain-base 的升级：
&lt;ul>&lt;li>给 cross-toolchain-base 的交叉编译增加了 loong64 的支持&lt;/li>&lt;li>给 binutils 的交叉编译增加了 loong64 和 riscv64 的支持&lt;/li>&lt;li>针对交叉工具链不一致导致的构建失败问题，升级对齐 debian 小版本：
&lt;ul>&lt;li>glibc 2.42-12&lt;/li>&lt;li>linux 6.18.9&lt;/li>&lt;li>gcc 15.2.0-13&lt;/li>&lt;li>binutils 2.46-1&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;li>gcc-15 新版依赖 python-check-jsonschema 已打包&lt;/li>&lt;li>至此，面向 RVA23 的完整工具链已基本就绪，下周起将正式进入系统基线的切换与调整阶段&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配-进展预估-40">关键设备适配 &lt;span>进展预估 40%&lt;/span>&lt;/h3>&lt;p>针对 SpacemiT K3 平台，本周与厂商进行了密切的联合调试，从 bootloader 到操作系统的全链路均打通。&lt;/p>&lt;ul>&lt;li>bootloader 方面：
&lt;ul>&lt;li>完成 K3 U-Boot 的测试运行，针对运行中发现的缺陷已整理 patch 并提交给厂商。&lt;/li>&lt;li>u-boot + opensbi 自动持续构建已提交 deepin-ports-kernel 的 k3 分支，将在厂商公开发布代码后合并。&lt;/li>&lt;li>esos 已完成打包，针对打包时遇到的问题，已整理 patch 并提交给厂商。&lt;/li>&lt;li>spacemit-flash-dtbs 软件包已同步升级至 k3 最新版。&lt;/li>&lt;li>bootloader 阶段工作基本收尾，目前正在编写面向用户的安装指南。&lt;/li>&lt;/ul>&lt;/li>&lt;li>内核方面：
&lt;ul>&lt;li>K3 需要的 GPU 等固件已打包并推送至 deepin-ports 仓库。&lt;/li>&lt;li>通过厂商协助，实现使用 deepin gcc-15 工具链打包 K3 内核，自动构建代码已提交 deepin-ports-kernel 的 k3 分支，将在厂商公开发布代码后合并。&lt;/li>&lt;li>编译完成的内核包已推送至 deepin-ports 仓库。&lt;/li>&lt;/ul>&lt;/li>&lt;li>系统方面：
&lt;ul>&lt;li>在 deepin-ports-profiles 和 deepin-ports-image 中加入了 K3 的初步支持。&lt;/li>&lt;li>系统已成功引导至 CLI 命令行界面。&lt;/li>&lt;li>由于缺乏 Type-C DP+PD 扩展坞，使用 x11vnc 绕过物理输出进行了验证，成功驱动 K3 的 GPU 并运行 glmark2-es2 图形性能测试，证明硬件加速能力已就绪。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>收到用户关于 A733 A7A 设备的反馈，已查明是不同型号设备的硬件规格不一致导致的。
&lt;ul>&lt;li>u-boot 现已对 A7A 提供两种二进制，分别指向 ac101/ac101b 两个 dtb 文件，用户可根据情况刷入。&lt;/li>&lt;li>kernel 现已对 A7A 同时提供 ac101/ac101b 两种 dtb 文件，可根据 u-boot 选择。&lt;/li>&lt;li>已对安装文档进行修改已引导用户选择合适的 u-boot。&lt;/li>&lt;li>已向 radxa 要求 A7A 设备以供测试。&lt;/li>&lt;/ul>&lt;/li>&lt;li>已对 imggpu 的闭源二进制包进行了重命名，后缀现在为 &lt;code class="">&amp;#123;ddk版本}-&amp;#123;bvnc}&lt;/code>&lt;ul>&lt;li>同时对 profile 包中的依赖和配置也进行了修改。&lt;/li>&lt;/ul>&lt;/li>&lt;li>在 deepin CICD 的 k8s 集群下搭建了监控平台，并给大部分较强算力的 rv 机器配置了监控，降低宕机排查时间。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 下周内获得通过，以便正式确定技术规范。&lt;/li>&lt;li>创建 RVA23 (next) 仓库并合入已构建出的软件包，然后继续进行 RVA23 基线提升工作。&lt;/li>&lt;li>准备在 deepin-ports-image 中增加单独的 RVA23 仓库选项，并在 k3 上测试 RVA23 仓库。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配-部分已推迟">关键设备适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>预计下周完成对 SpacemiT K3 的初步适配，至少支持 cli 系统的启动。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;li>deepin-ports-image 重构&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-02-13)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260213-weekly/</link><pubDate>Fri, 13 Feb 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260213-weekly/</guid><description>&lt;blockquote>&lt;p>祝大家春节玉快，阖家欢落！&lt;/p>&lt;/blockquote>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="关键设备适配">关键设备适配&lt;/h3>&lt;ul>&lt;li>已获取 K3 及代码，工作流将在厂商同步释放 SDK 时发布。&lt;/li>&lt;/ul>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>增加 apple-asahi-617y 内核。&lt;/li>&lt;li>增加 rk3566-mainline u-boot。&lt;/li>&lt;li>radxa-overlays-dkms。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配">RISC-V 转译应用适配&lt;/h3>&lt;ul>&lt;li>box64 0.4.0 已合并。实测可在 RISC-V 设备上成功运行 Linux 版 steam。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 下周内获得通过，以便正式确定技术规范。&lt;/li>&lt;li>基于已有的高版本工具链，尝试开启 RVA23 优化的 Bootstrap，以便后续评估性能收益。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配-部分已推迟">关键设备适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>预计下周正式启动针对 SpacemiT K3 芯片/设备的适配工作，评估内核与引导支持情况，具体进度以厂商通知为准。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-02-06)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260206-weekly/</link><pubDate>Fri, 06 Feb 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260206-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="rva23-仓库-进展预估-32">RVA23 仓库 &lt;span>进展预估 32%&lt;/span>&lt;/h3>&lt;p>本周完成了四大架构基础仓库的构建。&lt;/p>&lt;ul>&lt;li>已在 rva23 仓库修复了 loong64 的部分包的构建
&lt;ul>&lt;li>rebuild 了 libffi (loong64 下未以 -fPIC 构建)&lt;/li>&lt;li>完成 llvm-21 的 stage1 和完全体构建&lt;/li>&lt;li>为 loong64 升级了 llvm-defaults 以使用 llvm-21&lt;/li>&lt;/ul>&lt;/li>&lt;li>RVA23 仓库的全部工具链已就位。RVA 基线升级等待符合 RVA23 的构建机就位中。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配">关键设备适配&lt;/h3>&lt;ul>&lt;li>已向进迭时空请求 K3 设备及三件套（内核、驱动、bootloader）代码。等待后续中。&lt;/li>&lt;/ul>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>更新了 A733 (&lt;code class="">a733-radxa-515y&lt;/code>) 的内核并解决了构建问题。&lt;/li>&lt;li>有用户报告 A733 的音频芯片在不同批次间型号不一致，可能需要引入 dtbo，预计下周会给出解决方案。&lt;/li>&lt;/ul>&lt;h3 id="基础设施与文档建设">基础设施与文档建设&lt;/h3>&lt;ul>&lt;li>接收一台 Radxa Zero 3W，下周将开始适配。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配">RISC-V 转译应用适配&lt;/h3>&lt;ul>&lt;li>提交了 box64 0.4.0 升级的 PR，预计下周展开测试。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库-部分已推迟">RVA23 仓库 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 下周内获得通过，以便正式确定技术规范。&lt;/li>&lt;li>基于已有的高版本工具链，尝试开启 RVA23 优化的 Bootstrap，以便后续评估性能收益。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配-部分已推迟">关键设备适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>预计下周正式启动针对 SpacemiT K3 芯片/设备的适配工作，评估内核与引导支持情况，具体进度以厂商通知为准。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-01-30)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260130-weekly/</link><pubDate>Fri, 30 Jan 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260130-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="rva23-仓库-进展预估-30">RVA23 仓库 &lt;span>进展预估 30%&lt;/span>&lt;/h3>&lt;p>本周继续进行 RVA23 仓库的工具链版本方面的收尾工作，同时递交 RFC 草稿。&lt;/p>&lt;ul>&lt;li>已向 deepin RFC 仓库递交 RFC 18: 「建立 deepin RVA23 优化/next 仓库」。&lt;/li>&lt;li>已针对 RFC 收到的部分反馈做出了多轮修改和问题回应。&lt;/li>&lt;li>已在 rva23 仓库、在四大架构完成以下操作：
&lt;ul>&lt;li>llvm 21 stage1 和完整版本升级构建完成（loong64 架构除外）&lt;/li>&lt;li>llvm-defaults 已升级为使用 llvm-21（loong64 架构除外）&lt;/li>&lt;li>wasi-libc 已完成更新&lt;/li>&lt;/ul>&lt;/li>&lt;li>目前工具链升级工作已收尾，预计下周可开始进入到 RVA23 的基线调整阶段。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配">关键设备适配&lt;/h3>&lt;ul>&lt;li>观察到进迭时空 K3 已发布，正在联系沟通适配相关事宜以获得相关代码。&lt;/li>&lt;/ul>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>为 &lt;code class="">linux-firmware&lt;/code> backport Radxa Q6A 的相关固件以解决音频问题，已合并推送。&lt;/li>&lt;/ul>&lt;h3 id="基础设施与文档建设">基础设施与文档建设&lt;/h3>&lt;ul>&lt;li>针对离线的双路 SG2042 高性能构建节点询问了管理方，并进行了系统的重新安装。&lt;/li>&lt;li>已将该节点重新纳入构建集群。&lt;/li>&lt;/ul>&lt;h3 id="其它">其它&lt;/h3>&lt;ul>&lt;li>已获得 UOS AI 代码访问权限，通过重新编译、修改使 UOS AI 成功在 RISC-V 设备上运行本地模型。&lt;/li>&lt;li>已提交 llama.cpp 相关构建参数至 UOS AI 项目组，要求修复 &lt;code class="">riscv64&lt;/code> 架构的构建。&lt;/li>&lt;li>已对 UOS AI 相关组件包进行审计，针对一些本应标记为 &lt;code class="">all&lt;/code> 架构的软件包，向 UOS AI 项目组提出整改。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库">RVA23 仓库&lt;/h3>&lt;ul>&lt;li>争取 RVA23 RFC 下周内获得通过，以便正式确定技术规范。&lt;/li>&lt;li>基于已有的高版本工具链，尝试开启 RVA23 优化的 Bootstrap，以便后续评估性能收益。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配-部分已推迟">关键设备适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>预计下周正式启动针对 SpacemiT K3 芯片/设备的适配工作，评估内核与引导支持情况，具体进度以厂商通知为准。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 和 box64 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2026-01-23)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260123-weekly/</link><pubDate>Fri, 23 Jan 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260123-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="rva23-仓库-进展预估-25">RVA23 仓库 &lt;span>进展预估 25%&lt;/span>&lt;/h3>&lt;p>本周主要集中在下一代 RISC-V 架构标准（RVA23）的底层工具链构建与版本对齐。&lt;/p>&lt;ul>&lt;li>已与相关研发人员完成协调，初步确定了 RVA23 桌面系统的基线版本策略，准备与其它厂商达成一致。&lt;/li>&lt;li>已在 rva23 仓库、在四大架构完成以下操作：
&lt;ul>&lt;li>binutils 2.45 升级构建完成。&lt;/li>&lt;li>gcc-15 stage1 stage2 和完整版本升级构建完成。&lt;/li>&lt;li>gcc-defaults 已升级为使用 gcc-15。&lt;/li>&lt;li>glibc 2.42 升级构建完成。&lt;/li>&lt;li>dpkg 1.23.4 升级构建完成。&lt;/li>&lt;/ul>&lt;/li>&lt;li>目前工具链升级工作已接近尾声，即将开始 llvm-toolchain-21 升级工作，工具链升级工作即将收尾并进入到 RVA23 基线调整阶段。&lt;/li>&lt;/ul>&lt;h3 id="deepin-ports-维护">deepin-ports 维护&lt;/h3>&lt;ul>&lt;li>为 &lt;code class="">alsa-ucm-conf&lt;/code> 提交来自 radxa 的 patch，目前 PR 正等待上机验证，预计将解决音频配置问题。&lt;/li>&lt;li>在内核构建工作流中升级了 Q6A 的内核到 6.18，相关产物已推送至 deepin-ports 仓库。&lt;/li>&lt;li>发布了搭载 deepin 25.0.10 和 6.18 内核的 Q6A 镜像。&lt;/li>&lt;li>deepin 25.0.10 发布版镜像已集成 RISC-V 64位 ISO，未来将实现与主线同步发布。&lt;/li>&lt;li>deepin 25.0.10 发布镜像已在 DP1000 台式机上进行测试并成功完成安装和运行。&lt;/li>&lt;/ul>&lt;h3 id="基础设施与文档建设">基础设施与文档建设&lt;/h3>&lt;ul>&lt;li>修复并重新上线了 4 台此前离线的 SG2042 构建机器，恢复了集群算力。&lt;/li>&lt;li>已修复 sig-deepin-ports 的页面前端构建问题。&lt;/li>&lt;li>已在 sig-deepin-ports 的页面标注和区分 ISO 通用镜像的下载与设备镜像的下载。&lt;/li>&lt;li>已在 sig-deepin-ports 的页面醒目展示社区支持声明和默认密码信息。&lt;/li>&lt;/ul>&lt;h3 id="其它">其它&lt;/h3>&lt;ul>&lt;li>已配合运营完成 openRuyi 社区相关页面的文档提供与校对。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库">RVA23 仓库&lt;/h3>&lt;ul>&lt;li>预计下周完成全部工具链版本升级的构建和验证。&lt;/li>&lt;li>在确认能够运行 RVA23 指令集的实体机就位后，正式启动 RVA23 的软件包基线提升工作。&lt;/li>&lt;/ul>&lt;h3 id="关键设备适配">关键设备适配&lt;/h3>&lt;ul>&lt;li>预计下周正式启动针对 SpacemiT K3 芯片/设备的适配工作，评估内核与引导支持情况，具体进度以厂商通知为准。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-部分已推迟">RISC-V 转译应用适配 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 和 box64 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="其它-部分已推迟">其它 &lt;span>部分已推迟&lt;/span>&lt;/h3>&lt;ul>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>dde-shell 调试简明指南</title><link>https://deepin-community.github.io/sig-dde/posts/dde-shell-debugging-quick-guide/?utm_source=atom_feed</link><pubDate>Mon, 19 Jan 2026 09:20:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde/posts/dde-shell-debugging-quick-guide/</guid><description>&lt;p>桌面环境组件的开发调试相比应用组件总会有或多或少的不同，本文就旨在提供一个快速简明的描述，给期望开发或调试 dde-shell 以及其插件的开发者一个参考，以便快速上手。&lt;/p>
&lt;h2 id="环境推荐">环境推荐&lt;/h2>
&lt;p>你实际上可以使用任意自己喜欢的 IDE/开发环境，方便起见，此处以下列环境作为基础。若你使用其他开发工具或环境，则需自己参照实际情况进行调整。&lt;/p>
&lt;ul>
&lt;li>IDE: Visual Studio Code 或其衍生版本
&lt;ul>
&lt;li>推荐安装的插件
&lt;ul>
&lt;li>&lt;a href="https://marketplace.visualstudio.com/items?itemName=webfreak.debug">Native Debug (webfreak.debug)&lt;/a> (用于使用 gdb 启动调试)&lt;/li>
&lt;li>&lt;a href="https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd">clangd (llvm-vs-code-extensions.vscode-clangd)&lt;/a> (用于提供 C++ LSP)&lt;/li>
&lt;li>&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools">CMake Tools (ms-vscode.cmake-tools)&lt;/a> (用于初始的配置和构建)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>推荐使用 VSCode 或衍生版本是因为 VSCode 目前事实上是主流 IDE。后附的三个插件均是 VSCode 官方仓库以及 &lt;a href="https://open-vsx.org/">Open VSX 仓库&lt;/a> 都可以获取到的。下面所涉及的配置文件主要会和 Native Debug 插件有一定相关性。&lt;/p>
&lt;h2 id="构建项目">构建项目&lt;/h2>
&lt;p>事实上，dde-shell 的项目和常规 CMake 项目并无不同，常规的 CMake 配置和构建过程即适用于 dde-shell 项目。不过为了更好的 IDE 集成，建议确保 &lt;code>-DCMAKE_EXPORT_COMPILE_COMMANDS=ON&lt;/code> 配置以便在构建目录中生成可供 clangd 消费的 &lt;code>compile_commands.json&lt;/code>。&lt;/p>
&lt;p>你亦可直接使用 CMake Tools 完成配置和构建，也是本文推荐的做法。好处是你还可以在 VSCode 中看到相关的单元测试，以及直接在 IDE 中点击相关按钮完成整个构建过程。&lt;/p>
&lt;blockquote>
&lt;p>[!NOTE]
请注意：不要进行安装（&lt;code>make install&lt;/code> / &lt;code>cmake --build build --target install&lt;/code>），因为默认的安装位置是 &lt;code>/usr/local/bin&lt;/code>，并非期望的位置。盲目手动安装意味着卸载也会非常困难。若要运行自己所构建的版本，请参阅后面的部分。&lt;/p>&lt;/blockquote>
&lt;h2 id="运行项目">运行项目&lt;/h2>
&lt;p>dde-shell 项目在 DDE 桌面环境承载的责任包括使对应组件能够准确的在屏幕的指定位置进行展示。dde-shell 作为一个桌面环境外壳程序，自身也是由多个不同的插件构成的。dde-shell 需要通过参数来获知自己需要加载或排除加载哪些插件。对于任务栏组件而言，任务栏的部分插件也涉及到了一些额外的环境变量需要关心。&lt;/p>
&lt;p>下面是一个适用于 VSCode + Native Debug 的构建和运行模板(&lt;code>.vscode/launch.json&lt;/code>)：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-jsonc" data-lang="jsonc">&lt;span style="display:flex;">&lt;span>&amp;#123;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"version"&lt;/span>: &lt;span style="color:#e6db74">"0.2.0"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"configurations"&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;#123;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"name"&lt;/span>: &lt;span style="color:#e6db74">"Debug"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"type"&lt;/span>: &lt;span style="color:#e6db74">"gdb"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"request"&lt;/span>: &lt;span style="color:#e6db74">"launch"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"target"&lt;/span>: &lt;span style="color:#e6db74">"./build/shell/dde-shell"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"cwd"&lt;/span>: &lt;span style="color:#e6db74">"$&amp;#123;workspaceRoot}"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"valuesFormatting"&lt;/span>: &lt;span style="color:#e6db74">"prettyPrinters"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"env"&lt;/span>: &amp;#123;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"TRAY_LOADER_EXECUTE_PATH"&lt;/span>: &lt;span style="color:#e6db74">"/usr/libexec/trayplugin-loader"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"TRAY_DEBUG_PLUGIN_PATH"&lt;/span>: &lt;span style="color:#e6db74">"/usr/lib/dde-dock/plugins/:/usr/lib/dde-dock/plugins/system-tray/"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"DISPLAY"&lt;/span>: &lt;span style="color:#e6db74">":0"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"QT_QPA_PLATFORM"&lt;/span>: &lt;span style="color:#e6db74">"dxcb"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"LANGUAGE"&lt;/span>: &lt;span style="color:#e6db74">"zh_CN.UTF-8"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"QT_LOGGING_RULES"&lt;/span>: &lt;span style="color:#e6db74">"*.debug=true;qt*.debug=false"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"DDE_SHELL_PLUGIN_PATH"&lt;/span>: &lt;span style="color:#e6db74">"/home/username/Sources/dde-launchpad/build/plugins/"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"DDE_SHELL_PACKAGE_PATH"&lt;/span>: &lt;span style="color:#e6db74">"/home/username/Sources/dde-launchpad/build/packages/"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// "DDE_SHELL_PLUGIN_PATH": "/usr/lib/x86_64-linux-gnu/dde-shell/",
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// "DDE_SHELL_PACKAGE_PATH": "/usr/share/dde-shell/",
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#f92672">"XDG_CURRENT_DESKTOP"&lt;/span>: &lt;span style="color:#e6db74">"DDE"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"arguments"&lt;/span>: &lt;span style="color:#e6db74">"-p org.deepin.ds.dock -p org.deepin.ds.dde-apps"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"preLaunchTask"&lt;/span>: &lt;span style="color:#e6db74">"Build (Debug)"&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>以及对应的 &lt;code>.vscode/tasks.json&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-jsonc" data-lang="jsonc">&lt;span style="display:flex;">&lt;span>&amp;#123;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"version"&lt;/span>: &lt;span style="color:#e6db74">"2.0.0"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"tasks"&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;#123;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"label"&lt;/span>: &lt;span style="color:#e6db74">"Build (Debug)"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"type"&lt;/span>: &lt;span style="color:#e6db74">"shell"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"command"&lt;/span>: &lt;span style="color:#e6db74">"cmake"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"args"&lt;/span>: [&lt;span style="color:#e6db74">"--build"&lt;/span>, &lt;span style="color:#e6db74">"build"&lt;/span>, &lt;span style="color:#e6db74">"-j4"&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;#123;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"label"&lt;/span>: &lt;span style="color:#e6db74">"Run (x11)"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"type"&lt;/span>: &lt;span style="color:#e6db74">"shell"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"command"&lt;/span>: &lt;span style="color:#e6db74">"./build/shell/dde-shell"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"args"&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">"-p"&lt;/span>, &lt;span style="color:#e6db74">"org.deepin.ds.dock"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">"-p"&lt;/span>, &lt;span style="color:#e6db74">"org.deepin.ds.dde-apps"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">"-p"&lt;/span>, &lt;span style="color:#e6db74">"org.deepin.ds.example.bridge"&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"options"&lt;/span>: &amp;#123;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"env"&lt;/span>: &amp;#123;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"TRAY_LOADER_EXECUTE_PATH"&lt;/span>: &lt;span style="color:#e6db74">"/usr/libexec/trayplugin-loader"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"TRAY_DEBUG_PLUGIN_PATH"&lt;/span>: &lt;span style="color:#e6db74">"/usr/lib/dde-dock/plugins/:/usr/lib/dde-dock/plugins/system-tray/"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"DISPLAY"&lt;/span>: &lt;span style="color:#e6db74">":0"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"QT_QPA_PLATFORM"&lt;/span>: &lt;span style="color:#e6db74">"dxcb"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"LANGUAGE"&lt;/span>: &lt;span style="color:#e6db74">"zh_CN.UTF-8"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"QT_LOGGING_RULES"&lt;/span>: &lt;span style="color:#e6db74">"*.debug=true;qt*.debug=false"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"DDE_SHELL_PLUGIN_PATH"&lt;/span>: &lt;span style="color:#e6db74">"/home/username/Sources/dde-launchpad/build/plugins/"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"DDE_SHELL_PACKAGE_PATH"&lt;/span>: &lt;span style="color:#e6db74">"/home/username/Sources/dde-launchpad/build/packages/"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"XDG_CURRENT_DESKTOP"&lt;/span>: &lt;span style="color:#e6db74">"DDE"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>若只是从 VSCode 中运行，则事实上 &lt;code>.vscode/tasks.json&lt;/code> 中的 &lt;code>Run (x11)&lt;/code> 部分是不必要的。后者是供 &lt;a href="https://github.com/NathanVaughn/vscode-task-runner">vscode-task-runner&lt;/a> 类工具调用使用的，这对目前的各类 Agent 助手类应用会更有帮助。&lt;/p>
&lt;p>上述列出的环境变量和参数列表均为常用内容，对于各类 bug 很可能需要做出相应调整。例如：&lt;/p>
&lt;ul>
&lt;li>加载自己构建的插件还是系统的插件？是否要加载其他外部插件（比如 dde-launchpad 插件？）（&lt;code>DDE_SHELL_PLUGIN_PATH&lt;/code>/&lt;code>DDE_SHELL_PACKAGE_PATH&lt;/code>）&lt;/li>
&lt;li>测试其他语言的效果？（&lt;code>LANGUAGE&lt;/code>）&lt;/li>
&lt;li>使用自己构建的 tray-loader 或者加载自己构建的托盘插件？（&lt;code>TRAY_LOADER_EXECUTE_PATH&lt;/code>/&lt;code>TRAY_DEBUG_PLUGIN_PATH&lt;/code>）&lt;/li>
&lt;li>确保日志输出？&lt;/li>
&lt;/ul>
&lt;p>另外，如果你要测试 TreeLand 环境下的 dde-shell，你需要移除 &lt;code>DISPLAY&lt;/code> 环境变量并添加下面的环境变量（推荐另写一组或多组 launch configuration）：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-jsonc" data-lang="jsonc">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// 窗口化 treeland
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#e6db74">"WAYLAND_DISPLAY"&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> &lt;span style="color:#e6db74">"wayland-0"&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">"QT_QPA_PLATFORM"&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> &lt;span style="color:#e6db74">"wayland"&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// 常规 treeland 会话
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#e6db74">"WAYLAND_DISPLAY"&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> &lt;span style="color:#e6db74">"treeland.socket"&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">"QT_QPA_PLATFORM"&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> &lt;span style="color:#e6db74">"wayland"&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="结语">结语&lt;/h2>
&lt;p>希望上述快速指南对你有帮助。若有问题，欢迎在 GitHub linuxdeepin/developer-center 仓库发起讨论。你也可以考虑添加相关实时群聊来与开发者进行交流（ &lt;a href="https://t.me/deepin_community">Telegram&lt;/a> / &lt;a href="https://matrix.to/#/%23deepin-community:matrix.org">Matrix&lt;/a> ）。&lt;/p></description><author>DDE 桌面环境小组</author></item><item><title>deepin-ports SIG 周报 (2026-01-16)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20260116-weekly/</link><pubDate>Fri, 16 Jan 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20260116-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="rva23-仓库-进展预估-20">RVA23 仓库 &lt;span>进展预估 20%&lt;/span>&lt;/h3>&lt;ul>&lt;li>RVA23 方案已在本周 TC 例会上进行讨论，初步结论如下：
&lt;ul>&lt;li>开 deepin next/unstable 分支用于 RVA23（及后续更多实验性项目）&lt;/li>&lt;li>引入 gcc-15，并修改 target (rva23u64)，关闭 LTO&lt;/li>&lt;li>修改 /usr/share/perl5/Dpkg 下的 flag 以增加 -march=&lt;/li>&lt;li>是否引入自动向量化待评估&lt;/li>&lt;li>rebuild 或升级 glibc&lt;/li>&lt;li>可能需要升级/修改/rebuild的软件包：
&lt;ul>&lt;li>基础：binutils （需要 patch）&lt;/li>&lt;li>数学：openblas（调参数），fftw，gmp/mpfr&lt;/li>&lt;li>密码：openssl（OPENSSL_riscvcap 调整（动态）），gnutls&lt;/li>&lt;li>压缩：zlib/xz/zstd&lt;/li>&lt;li>媒体：ffmpeg/gstreamer，libjpeg，x264/x265/dav1d&lt;/li>&lt;li>运行时：python，v8/nodejs，llvm (21)，rust，go (1.24 rva22)（rebuild go 包？）&lt;/li>&lt;li>图形：mesa，pixman，cairo&lt;/li>&lt;li>桌面：qt6&lt;/li>&lt;/ul>&lt;/li>&lt;li>确定一批需要 rebuild 的软件包列表&lt;/li>&lt;li>由于 hwcap 支持问题，使用 6.18 内核，并需要注意 abi 变动&lt;/li>&lt;li>本阶段使用 SG2044 + 模拟 V 等缺失拓展实现，等待接下来支持 rva23 的设备发布&lt;/li>&lt;li>参考 TC 会议纪要：&lt;a href="https://github.com/deepin-community/SIG/blob/master/TC/Meeting%20Minutes/2026-01.md" rel="nofollow">https://github.com/deepin-community/SIG/blob/master/TC/Meeting%20Minutes/2026-01.md&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;li>已向仓库新增 gcc-15 包并创建 PR，预计下周进行构建，月内集成。后续将在此基础上增加 RVA23 支持。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配-进展预估-10">RISC-V 转译应用适配 &lt;span>进展预估 10%&lt;/span>&lt;/h3>&lt;ul>&lt;li>生态现状调研：
&lt;ul>&lt;li>针对商店中的 deepin-box64 向 deepin-wine 开发组提出质询，了解到该包已处于消极维护状态。deepin-ports SIG 预备介入此事。&lt;/li>&lt;li>针对商店中的 deepin-wine* 的多架构可用性向 deepin-wine 开发组提出质询，了解到该组无意提供 riscv64/loong64 架构的相关软件包。deepin-ports SIG 预备介入此事。&lt;/li>&lt;/ul>&lt;/li>&lt;li>运行时打包验证：
&lt;ul>&lt;li>已基于 arm64 deepin-wine-runtime 自行修改至 riscv64/loong64 架构并推送 deepin-ports 仓库。&lt;/li>&lt;li>已基于 amd64 deepin-wine&amp;#123;10,8}-stable 自行修改 至 riscv64/loong64 架构（增加 box64 转译依赖）并推送 deepin-ports 仓库。&lt;/li>&lt;/ul>&lt;/li>&lt;li>已在 DP1000 设备上验证上述 wine 程序运行状态良好。&lt;/li>&lt;/ul>&lt;h3 id="其它">其它&lt;/h3>&lt;ul>&lt;li>deepin 仓库浏览器：
&lt;ul>&lt;li>增加了应用商店查包功能&lt;/li>&lt;li>已在 deepin 论坛推广使用：&lt;a href="https://bbs.deepin.org/post/294965" rel="nofollow">https://bbs.deepin.org/post/294965&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;li>deepin-ports SIG 页面：
&lt;ul>&lt;li>新增 asciinema 支持，以便在安装文档中提供更直观的命令行操作。&lt;/li>&lt;li>效果如下：
&lt;asciinema-player src="https://deepin-community.github.io/sig-deepin-ports/asciinema/demo.cast">&lt;/asciinema-player>&lt;/li>&lt;/ul>&lt;/li>&lt;li>deepin-ports-kernel：
&lt;ul>&lt;li>已打包支持树莓派 4/5 的内核，后续将做树莓派设备适配的更新。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;h3 id="rva23-仓库">RVA23 仓库&lt;/h3>&lt;ul>&lt;li>调研 Ubuntu 的 Architecture-Variant 方案。&lt;/li>&lt;li>调试 SG2044 的模拟 RVA23 方案。&lt;/li>&lt;li>尝试构建 gcc-15，并升级 gcc-defaults 至 15，将后续在 RVA23 仓库内集成。&lt;/li>&lt;li>尝试构建更新版本的 binutils，并打入 rva23 相关 patch，将后续在 RVA23 仓库内集成。&lt;/li>&lt;/ul>&lt;h3 id="risc-v-转译应用适配">RISC-V 转译应用适配&lt;/h3>&lt;ul>&lt;li>计划在 deepin-ports 仓库内新增 apps-translated 组件提供转译应用包。&lt;/li>&lt;li>计划申请 deepin-wine-helper 代码权限，调查是否能打包出 RISC-V/LoongArch 架构的对应软件包。&lt;/li>&lt;li>同步更新 deepin 仓库内的 wine 和 box64 至上游最新版本。&lt;/li>&lt;/ul>&lt;h3 id="其它-1">其它&lt;/h3>&lt;ul>&lt;li>修改现有设备的安装文档，在一些用户易混淆的地方使用 asciinema，降低用户使用门槛。&lt;/li>&lt;li>调研 guestfish 方案是否能统一 deepin-ports 的镜像打包工作流。&lt;/li>&lt;li>自购串口线，在 DP1000 设备上测试最新的 deepin RISC-V ISO，若测试无误即发布到公开仓库。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin 25.0.10 正式版 DDE 移植简要指南</title><link>https://deepin-community.github.io/sig-dde-porting/posts/deepin-25-0-10-dde-porting-guide/?utm_source=atom_feed</link><pubDate>Wed, 07 Jan 2026 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/deepin-25-0-10-dde-porting-guide/</guid><description>&lt;p>deepin 25.0.10 已经发布一段时间了，而即将发布的 deepin 25.0.10 正式版镜像也会包含对应版本的 DDE 组件。为了方便各个其它发行版的包维护者可以更方便的移植 DDE 到对应的发行版，这里提供一篇简要的移植指南，用以描述常见的移植问题和解决方案。&lt;/p>
&lt;blockquote>
&lt;p>下面对项目名称的称呼均以 GitHub 对应的原始仓库名为准。&lt;/p>&lt;/blockquote>
&lt;h2 id="概览">概览&lt;/h2>
&lt;p>相对于 deepin 25 最初的正式版本，在 deepin 25.0.10 中并不存在较大幅的架构调整，而是以缺陷修复作为研发的重心，并涵盖了一些小功能的更新。本阶段中有部分组件的首位版本号存在调整，另存在一些注意事项，详情请参见后续的描述。&lt;/p>
&lt;p>由于这些项目的版本间互相影响，我们建议移植人员参照 deepin 25 中实际所使用的包版本进行打包，下面会对主要的部分进行详细说明。&lt;/p>
&lt;p>需要注意的是，由于此文章编写时间早于新 ISO 的发布时间，故最终版本镜像中使用的版本可能高于下面列出的版本。我们尽可能确保此文章的准确性，但若您需要获取 ISO 镜像中使用的确切软件版本列表，请挂载 ISO 后参阅 &lt;code>LIVE/FILESYS&amp;#123;T,0,1}.MAN&lt;/code>/&lt;code>live/filesystem.manifest&lt;/code> 路径对应的文件的内容。&lt;/p>
&lt;p>另外，由于前述 manifest 文件包含了 ISO 所附带的所有软件包的版本信息，而 DDE 移植并不需要关注整个列表，你可以参考 &lt;a href="https://github.com/deepin-community/deepin-desktop-environment/">deepin-community/deepin-desktop-environment&lt;/a> 提供的几个 Meta 包中描述的依赖关系，来了解哪些软件包和 DDE 有关。&lt;/p>
&lt;h2 id="主要组件">主要组件&lt;/h2>
&lt;h3 id="dtk-与-dtk6">DTK 与 DTK6&lt;/h3>
&lt;p>DTK 是 DDE 组件与应用的基础依赖，适用于deepin 25 正式版的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>project&lt;/th>
&lt;th>package name&lt;sup id="fnref:1">&lt;a class="footnote-ref" href="https://deepin-community.github.io/sig-dde-porting/atom.xml#fn:1" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>dtkcommon&lt;/td>
&lt;td>libdtkdata&lt;/td>
&lt;td>5.7.26&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtklog&lt;/td>
&lt;td>libdtklog&lt;/td>
&lt;td>0.0.6&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkcore&lt;/td>
&lt;td>libdtkcore5&lt;/td>
&lt;td>5.7.26&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkgui&lt;/td>
&lt;td>libdtkgui5&lt;/td>
&lt;td>5.7.26&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkwidget&lt;/td>
&lt;td>libdtkwidget5&lt;/td>
&lt;td>5.7.26.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkdeclarative&lt;/td>
&lt;td>libdtkdeclarative5&lt;/td>
&lt;td>5.7.26&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5integration&lt;/td>
&lt;td>dde-qt5integration&lt;/td>
&lt;td>5.7.26&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5platform-plugins&lt;/td>
&lt;td>dde-qt5xcb-plugin&lt;/td>
&lt;td>5.7.26&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6core&lt;/td>
&lt;td>libdtk6core&lt;/td>
&lt;td>6.0.46&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6gui&lt;/td>
&lt;td>libdtk6gui&lt;/td>
&lt;td>6.0.46&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6widget&lt;/td>
&lt;td>libdtk6widget&lt;/td>
&lt;td>6.0.46.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6declarative&lt;/td>
&lt;td>libdtk6declarative&lt;/td>
&lt;td>6.0.46&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6integration&lt;/td>
&lt;td>dde-qt6integration&lt;/td>
&lt;td>6.0.46&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6platform-plugins&lt;/td>
&lt;td>dde-qt6xcb-plugin&lt;/td>
&lt;td>6.0.46&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>本次 DTK 组件大部分版本号以及相对应的平台插件等版本号均已对齐，例外的有 dtkcommon 与 dtklog。可参照上表进行打包。&lt;/p>
&lt;p>关于 qt5platform-plugins，现有的 dwayland 插件可能对非 DDE 环境（例如 KDE）的 wayland 用户存在影响，可参照 &lt;a href="https://github.com/linuxdeepin/developer-center/issues/7217">linuxdeepin/developer-center#7217&lt;/a> 打对应的 patch 规避影响。&lt;/p>
&lt;p>另外，之前版本的打包采取了 dtk5 与 dtk6 分仓库进行打包的方案，就于上述（在 deepin 25.0.10 镜像中使用的 DTK）版本，您暂时仍然需要在 &lt;code>dtk6blah&lt;/code> 仓库中获取对应的 tag。目前正在向回归到同一仓库打包的方案进行过度，且过度即将完成，即未来版本将会不再需要使用 &lt;code>dtk6blah&lt;/code> 仓库，只需 &lt;code>dtkblah&lt;/code> 即可。对于此事项的进展可以关注移植小组的 Telegram 或 Matrix 群聊。&lt;/p>
&lt;h3 id="dde-主要组件">DDE 主要组件&lt;/h3>
&lt;p>下面仅涉及变化较大或影响较广的组件。其余未涉及的组件可正常参照最新 tag 进行打包与移植。&lt;/p>
&lt;p>在 alpha 与 beta 阶段使用 1.99.z 系列版本号的项目现均启用了 2.0.z 版本号，以表示相关的行为变动。针对 dde-shell 所提供库的依赖的 SOVERSION 仍为 1，不受打包版本号的影响。&lt;/p>
&lt;p>下面涉及到的组件的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>project&lt;/th>
&lt;th>package name&lt;sup id="fnref1:1">&lt;a class="footnote-ref" href="https://deepin-community.github.io/sig-dde-porting/atom.xml#fn:1" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>deepin-desktop-schemas&lt;/td>
&lt;td>deepin-desktop-schemas&lt;/td>
&lt;td>6.0.13&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-daemon&lt;/td>
&lt;td>dde-daemon&lt;/td>
&lt;td>6.1.64&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-session&lt;/td>
&lt;td>dde-session&lt;/td>
&lt;td>2.0.9&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-session-ui&lt;/td>
&lt;td>dde-session-ui&lt;/td>
&lt;td>6.0.35&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-session-shell&lt;/td>
&lt;td>dde-session-shell&lt;/td>
&lt;td>6.0.50&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-manager&lt;/td>
&lt;td>dde-application-manager&lt;/td>
&lt;td>1.2.37&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-shell&lt;/td>
&lt;td>dde-shell&lt;/td>
&lt;td>2.0.19&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launchpad&lt;/td>
&lt;td>dde-launchpad&lt;/td>
&lt;td>2.0.17&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-tray-loader&lt;/td>
&lt;td>dde-tray-loader&lt;/td>
&lt;td>2.0.17&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-wizard&lt;/td>
&lt;td>dde-application-wizard-daemon-compat&lt;/td>
&lt;td>0.1.21&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-clipboard&lt;/td>
&lt;td>dde-clipboard&lt;/td>
&lt;td>6.1.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>deepin-service-manager&lt;/td>
&lt;td>deepin-service-manager&lt;/td>
&lt;td>1.0.17&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-services&lt;/td>
&lt;td>dde-services&lt;/td>
&lt;td>1.0.12&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-appearance&lt;/td>
&lt;td>dde-appearance&lt;/td>
&lt;td>1.1.71&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-api&lt;/td>
&lt;td>dde-api&lt;/td>
&lt;td>6.0.29&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>treeland-protocols&lt;/td>
&lt;td>treeland-protocols&lt;/td>
&lt;td>0.5.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-network-core&lt;/td>
&lt;td>libdde-network-core&lt;/td>
&lt;td>2.0.74&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-control-center&lt;/td>
&lt;td>dde-control-center&lt;/td>
&lt;td>6.1.58&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-file-manager&lt;/td>
&lt;td>dde-desktop&lt;/td>
&lt;td>6.5.106&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launcher&lt;/td>
&lt;td>被 dde-launchpad 取代，不再使用&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-dock&lt;/td>
&lt;td>被 dde-shell 取代，不再使用&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>startdde&lt;/td>
&lt;td>已被废弃，不再使用&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="dde-application-manager">dde-application-manager&lt;/h4>
&lt;p>由于涉及到诸多关于应用识别的改善，故建议总是使用最新版本。&lt;/p>
&lt;h4 id="dde-session-shell">dde-session-shell&lt;/h4>
&lt;p>请注意，当前项目获取 tag 的源码仓库现在变为了 &lt;a href="https://github.com/linuxdeepin/dde-session-shell-snipe">https://github.com/linuxdeepin/dde-session-shell-snipe&lt;/a> 。&lt;/p>
&lt;h4 id="dde-shell">dde-shell&lt;/h4>
&lt;p>dde-shell 在本段时间的更新迭代中，修复了若干关于任务栏图标识别问题以及图标错位的问题。对于比较受到争议的 cgroup 识别问题也给出了配置项运行用户进行微调。对于移植，也处理了高版本 Qt 的一些兼容性问题。&lt;/p>
&lt;p>我们假定您要移植的目标发行版使用的 Qt 版本大于 6.8.2，如果早于此小版本，请阅读上一篇（正式版时的）博客来了解相关的补丁信息。&lt;/p>
&lt;p>dde-shell 在 alpha 中为修正一个特定问题所包含的一个变更依赖另一个 Qt Wayland 的 patch：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/deepin-community/qt6-wayland/pull/12">https://github.com/deepin-community/qt6-wayland/pull/12&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>若你所移植的目标发行版不接受此补丁，则可考虑对 dde-shell 项目 revert 于此相关的对应 commit：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/dde-shell/commit/77cac3d5a346728f6678eedc77fce404f0ffd8b6">https://github.com/linuxdeepin/dde-shell/commit/77cac3d5a346728f6678eedc77fce404f0ffd8b6&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="dde-launchpad">dde-launchpad&lt;/h4>
&lt;p>dde-launchpad 现仅支持以 dde-shell 插件的形式被最终用户使用。因而，打包 dde-launchpad 现需要先打包 dde-shell，并确保用户最终使用的是 dde-shell。&lt;/p>
&lt;h4 id="dde-session">dde-session&lt;/h4>
&lt;p>我们已在 deepin 23 beta3 起放弃了对 deepin-kwin wayland 的支持，DDE 后续所有 wayland 相关的支持均由 treeland 提供。请参见后续的 Treeland 段落。&lt;/p>
&lt;h2 id="deepin-kwin-环境">deepin-kwin 环境&lt;/h2>
&lt;p>此部分相对 beta 也未存在较大变动，但由于此组件的重要性，方便起见，此处重新阐述 beta 阶段的变化：&lt;/p>
&lt;p>deepin-kwin 对 Qt 的版本依赖切换到了 Qt 6，不过值得注意的是，当前的 deepin-kwin 并非从上游 kwin 6.x 中 fork 出来的，而是基于 uos 20 版本的 kwin 5.27.x 进行的 qt6/kf6 迁移，并且由于一些原因，其二进制可执行的文件名恢复到了 kwin，这会导致与上游原版 kwin 的冲突。&lt;/p>
&lt;p>就于此事项的详细介绍，请参见 &lt;a href="https://github.com/orgs/linuxdeepin/discussions/11471">https://github.com/orgs/linuxdeepin/discussions/11471&lt;/a> 讨论。对于移植人员，我们建议考虑下面三种方案：&lt;/p>
&lt;ul>
&lt;li>使用 Qt 6 的 deepin-kwin，放弃与上游 kwin 的共存支持。&lt;/li>
&lt;li>继续使用 deepin-kwin 5.27，尽管存在一些问题，但此版本仍然可保证和上游版本 kwin 共存。&lt;/li>
&lt;li>考虑打包 Treeland 环境。&lt;/li>
&lt;/ul>
&lt;h2 id="treeland-环境">Treeland 环境&lt;/h2>
&lt;p>Treeland 环境尽管目前仍然是实验性支持，但目前 treeland 构建不再需要事先构建 waylib 与 qwlroots。&lt;/p>
&lt;p>下面涉及到的组件的版本参照如下。对于位于非 linuxdeepin 组织的软件包，此处一并给出了组织名：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>treeland-protocols&lt;/td>
&lt;td>0.5.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>treeland&lt;/td>
&lt;td>0.7.8&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ddm&lt;/td>
&lt;td>0.2.3&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="ddm">DDM&lt;/h3>
&lt;p>尽管 DDM 目前是基本功能可用状态，DDM 目前仍相对而言不够稳定。对于打包移植而言，建议采用其他DM来启动用户级的treeland。&lt;/p>
&lt;p>对于其它 DM，只需要打包时安装 &lt;code>usr/share/wayland-sessions/treeland-user.desktop&lt;/code> 即可。&lt;/p>
&lt;h3 id="qt-补丁">Qt 补丁&lt;/h3>
&lt;p>我们假定您要移植的目标发行版使用的 Qt 版本大于 6.8.2，如果早于此小版本，请阅读上一篇（正式版时的）博客来了解相关的补丁信息。&lt;/p>
&lt;p>如果你在 Treeland 下遇到小 launchpad 无法输入中文的问题，可以打下面的 patch，但是该 patch 目前尚未进行完整测试，可能存在一些问题。&lt;/p>
&lt;p>&lt;a href="https://codereview.qt-project.org/c/qt/qtbase/+/611940">https://codereview.qt-project.org/c/qt/qtbase/+/611940&lt;/a>&lt;/p>
&lt;h2 id="建议忽略移植的组件">建议忽略移植的组件&lt;/h2>
&lt;h3 id="dde-api-proxy">dde-api-proxy&lt;/h3>
&lt;p>此组件的存在目的是给部分仍在依赖 deepin 20/23 所提供的 D-Bus 服务的第三方组件提供兼容。DDE 和 deepin 第一方应用均不依赖这些旧的 D-Bus 接口，且此组件目前不被 deepin 自身所需，所以此项目不应当被移植。&lt;/p>
&lt;h3 id="deepin-anything">deepin-anything&lt;/h3>
&lt;p>尽管被部分 DDE 组件依赖，但均为可选。anything 提供了内核模块，对于滚动发行版移植可能不友好，可能移植并不能得到很高的体验改善。&lt;/p>
&lt;p>若忽略 deepin-anything 移植，则下列组件也应当被忽略（因为依赖了 deepin-anything）：&lt;/p>
&lt;ul>
&lt;li>dde-grand-search&lt;/li>
&lt;/ul>
&lt;h3 id="dde-application-wizard">dde-application-wizard&lt;/h3>
&lt;p>尽管此项目初衷之一是提供可移植的模块化卸载服务，但并未达到理想状态。尽管事实上可被顺利移植，此项目可以考虑忽略。缺失此组件并不会影响 DDE 主要功能。&lt;/p>
&lt;h2 id="已知问题">已知问题&lt;/h2>
&lt;p>下述问题是已知问题，需酌情处理。&lt;/p>
&lt;h3 id="deepin-kwin-与-kwin-原版的共存问题">deepin-kwin 与 kwin 原版的共存问题&lt;/h3>
&lt;p>目前由于 Qt 6 版的 deepin-kwin 与上游原版 kwin 存在一些同名可执行文件/配置文件，故无法做到 deepin-kwin 与 kwin 的共存。若要移植 x11 会话，选择如下：&lt;/p>
&lt;ol>
&lt;li>使用 Qt 5 版本的 deepin-kwin（目前 openSUSE 移植的做法如此）&lt;/li>
&lt;li>打包 deepin-kwin，并声明与上游 kwin 的冲突（会导致 KDE x11 与 DDE x11 会话无法共存，目前 Arch Linux 移植的做法如此）&lt;/li>
&lt;/ol>
&lt;p>由于 dde-session 的依赖关系，这也会导致如果仅希望使用 deepin 的 wayland 会话，也会受到上述问题的影响而无法使 KDE 与 DDE 共存，此问题会在后续进行解决来确保允许仅移植 DDE wayland，从而使 DDE wayland 和 KDE 可以无冲突的共存。&lt;/p>
&lt;h3 id="任务栏托盘部分插件的背景颜色异常">任务栏托盘部分，插件的背景颜色异常&lt;/h3>
&lt;p>这个问题是目前移植过程中发现的已知问题，但暂时没有时间定位分析。若您移植过程中遇到此问题，可暂时忽略，当然也欢迎协助定位并提交 PR :)&lt;/p>
&lt;h2 id="获取移植帮助">获取移植帮助&lt;/h2>
&lt;p>如果您希望得到移植相关的帮助，请考虑加入我们 DDE 移植小组的在线交流群（下列房间有桥接，任选其一即可），一起展开相关的交流：&lt;/p>
&lt;ul>
&lt;li>Matrix 群：&lt;code>#dde-port:deepin.org&lt;/code>&lt;/li>
&lt;li>Telegram 群：&lt;a href="https://t.me/ddeport">https://t.me/ddeport&lt;/a>&lt;/li>
&lt;/ul>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr />
&lt;ol>
&lt;li id="fn:1">
&lt;p>一个项目可能有多个软件包，此处的包名仅列出了此项目在 deepin 发行版中具有代表性的包名&amp;#160;&lt;a class="footnote-backref" href="https://deepin-community.github.io/sig-dde-porting/atom.xml#fnref:1" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&amp;#160;&lt;a class="footnote-backref" href="https://deepin-community.github.io/sig-dde-porting/atom.xml#fnref1:1" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description><author>DDE 移植兴趣小组</author></item><item><title>deepin-ports SIG 周报 (2025-12-26)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20251226-weekly/</link><pubDate>Fri, 26 Dec 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20251226-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="sg2044-适配-进展预估-90">SG2044 适配 &lt;span>进展预估 90%&lt;/span>&lt;/h3>&lt;ul>&lt;li>已在本地 SG2044 EVB 上成功验证 6.12 内核通用 ISO 镜像的启动与安装流程。&lt;/li>&lt;li>SG2044 正式成为继 DP1000 之后，deepin 第二款实现 ISO 通用镜像完整支持的 RISC-V 设备。&lt;/li>&lt;li>集成 SG2044 补丁的 6.12 内核的通用 ISO 镜像已发布至 deepin-ports SIG 下载页，后续的详细功能验证与测试工作已移交相关测试人员安排。&lt;/li>&lt;/ul>&lt;h3 id="dduc-2025-进展预估-80">DDUC 2025 &lt;span>进展预估 80%&lt;/span>&lt;/h3>&lt;ul>&lt;li>已完成部分参展设备的系统重装和配置。剩余设备将在 DDUC 当日上午完成调试。&lt;/li>&lt;/ul>&lt;h3 id="rva23-仓库-进展预估-15">RVA23 仓库 &lt;span>进展预估 15%&lt;/span>&lt;/h3>&lt;ul>&lt;li>已对照部分发行版的 RVA23 适配状况进行了简单评估。仓库划分将很快开始。&lt;/li>&lt;/ul>&lt;h3 id="milkv-titan-适配-进展预估-30">MilkV Titan 适配 &lt;span>进展预估 30%&lt;/span>&lt;/h3>&lt;ul>&lt;li>已接收来自 UltraRISC 的 MilkV Titan 相关补丁，经代码审查后 PR 已合并。&lt;/li>&lt;li>已将基于补丁构建的通用 ISO 镜像发送至厂商以供测试。&lt;/li>&lt;/ul>&lt;h3 id="其它">其它&lt;/h3>&lt;ul>&lt;li>应香橙派厂商要求，提供了 deepin 25 兼容认证证书，以助力推进其生态。&lt;/li>&lt;li>修改了通用镜像的安装指南，现在包括了部分适配通用镜像的 deepin-ports 设备的安装方法。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;p>下周适逢元旦佳节，SIG 组成员休假，暂无具体开发任务规划。&lt;/p></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-12-18)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20251218-weekly/</link><pubDate>Thu, 18 Dec 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20251218-weekly/</guid><description>&lt;h2 id="本周进展">本周进展&lt;/h2>&lt;h3 id="香橙派适配-进展预估-10">香橙派适配 &lt;span>进展预估 10%&lt;/span>&lt;/h3>&lt;ul>&lt;li>接收一台 OrangePi AIPro 20T 开发板。&lt;/li>&lt;li>已与香橙派开发人员沟通，先行临时镜像暂不涉及 bootloader 和内核部分。&lt;/li>&lt;li>已完成临时基础镜像交付，可开机进入命令行界面。&lt;/li>&lt;/ul>&lt;h3 id="sg2044-适配-进展预估-70">SG2044 适配 &lt;span>进展预估 70%&lt;/span>&lt;/h3>&lt;ul>&lt;li>已在 &lt;code class="">deepin-ports-profiles&lt;/code> 包中修复 u-boot 启动菜单相关 bug。&lt;/li>&lt;li>已指导测试人员修改启动选项开机，已可进入 deepin 桌面。&lt;/li>&lt;li>新的测试镜像已发布。&lt;/li>&lt;/ul>&lt;h3 id="rva23-仓库-进展预估-10">RVA23 仓库 &lt;span>进展预估 10%&lt;/span>&lt;/h3>&lt;ul>&lt;li>已评估仓库划分方案，拟切出 testing/next（名称待定）分支，全架构构建，版本至少对齐 rva23 支持的最低要求，并作为 rva23 支持的基础，未来将服务 deepin 下个版本。&lt;/li>&lt;/ul>&lt;h3 id="dduc-2025-进展预估-50">DDUC 2025 &lt;span>进展预估 50%&lt;/span>&lt;/h3>&lt;ul>&lt;li>已与运营协商，向部分厂商和开发者提出邀请。&lt;/li>&lt;li>deepin-ports SIG 展位部分文案已准备完毕。&lt;/li>&lt;/ul>&lt;h3 id="其它">其它&lt;/h3>&lt;ul>&lt;li>通用 ISO 镜像正在 DP1000 新设备上进行 6.6/6.12 内核安装测试。&lt;/li>&lt;li>通用 ISO 镜像正在 SG2044 设备上进行 6.12 内核安装测试。&lt;/li>&lt;/ul>&lt;h2 id="下周规划">下周规划&lt;/h2>&lt;ul>&lt;li>DDUC 2025：进行参展设备选择、安装和调试。&lt;/li>&lt;li>RVA23 仓库：评估软件版本基线，设置工作流。&lt;/li>&lt;li>SG2044 适配：等待测试结论，如有 bug 则继续 debug。&lt;/li>&lt;li>香橙派适配：等待厂商交付完整的内核代码和启动文档。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-12-12)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20251212-weekly/</link><pubDate>Fri, 12 Dec 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20251212-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>在 Radxa Dragon Q6A 设备上搭配 UFS 测试了 deepin 25 镜像，运行桌面环境成功。&lt;/li>&lt;li>新增了以下设备的 deepin 25 测试镜像:
&lt;ul>&lt;li>&lt;code class="">sg2044&lt;/code>:
&lt;ul>&lt;li>20251208-100625&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-kernel&lt;/code> 项目：
&lt;ul>&lt;li>增加了 &lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/9d51b3801f589af09b3e78eef93e02ca6eddc701" rel="nofollow">revyOS 提供的 SG2044 6.18 内核&lt;/a>。&lt;/li>&lt;li>由于 Zhihe A210 u-boot 持续构建失败，现从列表中移除。&lt;/li>&lt;/ul>&lt;/li>&lt;li>&lt;code class="">ports-kernel&lt;/code> 仓库对已有内核更新了新构建的版本。&lt;/li>&lt;/ul>&lt;h2 id="文档">文档&lt;/h2>&lt;h3 id="安装指南">安装指南&lt;/h3>&lt;ul>&lt;li>新增了如下设备的安装指南：
&lt;ul>&lt;li>&lt;a href="https://deepin-community.github.io/sig-deepin-ports/docs/install/sg2044" rel="nofollow">Sophgo SG2044&lt;/a>&lt;/li>&lt;li>&lt;a href="https://deepin-community.github.io/sig-deepin-ports/docs/install/q6a" rel="nofollow">Radxa Dragon Q6A&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>deepin-ports SIG 将携设备参与本月的 DDUC 2025 并进行展示。&lt;/li>&lt;/ul>&lt;h2 id="其它-sig-非-sig-事务">其它 SIG / 非 SIG 事务&lt;/h2>&lt;ul>&lt;li>&lt;a href="https://deepin-repo-viewer.pages.dev" rel="nofollow">deepin 仓库查看器&lt;/a> 现已部署自动更新脚本，将不定期自动刷写数据。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-12-05)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20251205-weekly/</link><pubDate>Fri, 05 Dec 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20251205-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>在 SG2044 设备上测试了 deepin 25 镜像，运行桌面环境成功。&lt;/li>&lt;li>&lt;code class="">deepin-ports-images&lt;/code> 项目：
&lt;ul>&lt;li>&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/4e338cbbd0690fd8a85a76e97040a9cba15a73b9" rel="nofollow">现可创建 &lt;code class="">sg2044&lt;/code> 镜像&lt;/a>，预计下周可发布。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-kernel&lt;/code> 项目：
&lt;ul>&lt;li>增加了&lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/baf9cb81b45616690e48701aaa66f6da2de40530" rel="nofollow">u-boot 发布版本工作流&lt;/a>，后续将可以手动将 u-boot 发布到对象存储的 &lt;code class="">release&lt;/code> 目录。&lt;/li>&lt;li>修改了&lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/fdd98b91efdc567dc5c8a90880e392ca295187d8" rel="nofollow">u-boot 快照版本工作流&lt;/a>，后续每次 u-boot 自动构建将上传至 &lt;code class="">rolling&lt;/code> 目录。&lt;/li>&lt;li>设置了 &lt;a href="https://deepin-ports-kernel.0x7f.cc/" rel="nofollow">deepin-ports-kernel.0x7f.cc&lt;/a> 作为 deepin-ports-kernel 的临时仓库地址，托管在 Cloudflare R2 对象存储上。
&lt;ul>&lt;li>&lt;code class="">rolling&lt;/code> 快照版本产物默认保留 14 天，并在发布 7 天后标记为非频繁访问存储。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-profiles&lt;/code> 项目：
&lt;ul>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/d161808171773768b63be19e6bcf49c6f66d3482" rel="nofollow">增加了&lt;/a> &lt;code class="">sg2044&lt;/code> 的配置包。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="测试">测试&lt;/h2>&lt;ul>&lt;li>合并了以下镜像的测试报告：
&lt;ul>&lt;li>&lt;code class="">eic7700&lt;/code>: deepin 25 Preview 20250729-125156 &lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/44f27d7a87999b57f99c287b2496a144d6cc6e81c" rel="nofollow">测试报告&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>接收到一台 DP1000 台式机，用作镜像 debug。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-11-28)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20251128-weekly/</link><pubDate>Fri, 28 Nov 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20251128-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>新增了以下设备的 deepin 25 测试镜像:
&lt;ul>&lt;li>&lt;code class="">a733&lt;/code>:
&lt;ul>&lt;li>20251121-162850&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-kernel&lt;/code> 项目：
&lt;ul>&lt;li>跟随上游 &lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/5d60202e481feeea87696ec758a34abe94cb50d4" rel="nofollow">刷新了&lt;/a> Rockchip 内核补丁。&lt;/li>&lt;li>增加了 &lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/59f2afc1f53a10ddfb27bdbfd1848935bca320a7" rel="nofollow">clang 编译支持&lt;/a>。&lt;/li>&lt;li>增加了 &lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/7de29974dbab50111a0756b03401e90bd53b5c4c" rel="nofollow">LFRon 提供的 SM8150 6.17 内核&lt;/a>。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports&lt;/code> 仓库:
&lt;ul>&lt;li>修复了 &lt;code class="">img-gpu-bin-ddk242-a733&lt;/code> 因 git lfs 打包出错的问题。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>接收了 SG2044 的文档，开始进行适配。&lt;/li>&lt;/ul>&lt;h2 id="文档">文档&lt;/h2>&lt;h3 id="安装指南">安装指南&lt;/h3>&lt;ul>&lt;li>&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/352d552fbba7a535c24893c32b6cb835cc7bea5b" rel="nofollow">改进了 A733 设备 的安装指南&lt;/a>。&lt;/li>&lt;/ul>&lt;h3 id="网页">网页&lt;/h3>&lt;ul>&lt;li>&lt;code class="">sig-deepin-ports&lt;/code> 项目：
&lt;ul>&lt;li>增加了&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/be9dcb383365b6ae0932bb449a85b7137ce598f7" rel="nofollow">全局搜索功能&lt;/a>。&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/be9dcb383365b6ae0932bb449a85b7137ce598f7" rel="nofollow">调整了部分界面风格&lt;/a>。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-11-21)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20251121-weekly/</link><pubDate>Fri, 21 Nov 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20251121-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>在 Radxa Cubie A7Z 设备上测试了 deepin 25 镜像，运行桌面环境成功。&lt;/li>&lt;li>新增了以下设备的 deepin 25 测试镜像:
&lt;ul>&lt;li>&lt;code class="">a733&lt;/code>:
&lt;ul>&lt;li>20251118-115143&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-kernel&lt;/code> 项目：
&lt;ul>&lt;li>新增&lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/3c9d458ee6f016cf4ee4829e0d3422524db7383f" rel="nofollow">上传工作流&lt;/a>，后续将自动上传 u-boot 产物到对象存储。&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/a025d3b1cbf46e0dcac389c52008d7e602fa401c" rel="nofollow">修复了&lt;/a> collabora RK3576 主线 u-boot 构建。&lt;/li>&lt;li>增加了 &lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/b8d1fafd41ea5197925369a6b73d6a3b5b157739" rel="nofollow">revyOS 提供的 SG2044 6.17 内核&lt;/a>。&lt;/li>&lt;li>更新了 &lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/b8d1fafd41ea5197925369a6b73d6a3b5b157739" rel="nofollow">revyOS 提供的 SG2042 6.17 内核&lt;/a>。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>获得一台 SG2044 机器，即将开始进行适配。&lt;/li>&lt;li>甲辰计划实习活动即将结束，已对现有 PR 进行了清理和加速处理。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-11-14)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20251114-weekly/</link><pubDate>Fri, 14 Nov 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20251114-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>在 DshanPi A1 设备上测试了 deepin 25 镜像，运行桌面环境成功。&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-kernel&lt;/code> 项目：
&lt;ul>&lt;li>增加了 &lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/4e1163da863378822b9c3659e6257a4b69e0edeb" rel="nofollow">collabora 提供的 RK3576 主线 u-boot&lt;/a>。&lt;/li>&lt;li>为所有 &lt;code class="">rk3588-collabora&lt;/code> uboot 支持的设备&lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/6a63914d1a12c083e200afa010e74e52bf3f9666" rel="nofollow">增加了构建&lt;/a>。&lt;/li>&lt;li>为所有 &lt;code class="">rk3576-collabora&lt;/code> uboot 支持的设备&lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/52d87ef0c05079b7c980bbd8fc77956961db9087" rel="nofollow">增加了构建&lt;/a>。&lt;/li>&lt;li>在 &lt;code class="">rk3576-collabora&lt;/code> uboot 为 DshanPi A1 &lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/071945bee02384a0f3b7a839c5ec4b9bbede228e" rel="nofollow">增加了支持并导入了上游设备树&lt;/a>。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>尝试为 Allwinner A733 内置的 IMG GPU 适配 &lt;code class="">mesa-ddk242&lt;/code>、增加相关二进制驱动包。&lt;/li>&lt;li>&lt;code class="">deepin-ports-profiles&lt;/code> 项目:
&lt;ul>&lt;li>为 &lt;code class="">a733&lt;/code> &lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/1f199bf023e4d93f4fec4a5750f010f39186a117" rel="nofollow">修复了 &lt;code class="">aic8800&lt;/code> 的固件路径&lt;/a>。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="文档">文档&lt;/h2>&lt;h3 id="安装指南">安装指南&lt;/h3>&lt;ul>&lt;li>新增了如下设备的安装指南：
&lt;ul>&lt;li>&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/493b66e08d190d060385e73e42a02accddaa6bf1" rel="nofollow">Allwinner A733&lt;/a>&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/493b66e08d190d060385e73e42a02accddaa6bf1" rel="nofollow">Rockchip&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h3 id="网页">网页&lt;/h3>&lt;ul>&lt;li>&lt;code class="">sig-deepin-ports&lt;/code> 项目：
&lt;ul>&lt;li>&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/998d74020824e40b9cc70739ec7cbf0a25aad360" rel="nofollow">更新了&lt;/a>「设备支持矩阵」的数据。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-11-07)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20251107-weekly/</link><pubDate>Fri, 07 Nov 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20251107-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-images&lt;/code> 项目：
&lt;ul>&lt;li>为 &lt;code class="">a733&lt;/code> 镜像&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/622b8e53415dc41c9dd105470662f2325e1314c1" rel="nofollow">配置了 profile&lt;/a> 并&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/acff7853ad0365b6c640552bb329021323e0346a" rel="nofollow">合并了 &lt;code class="">boot&lt;/code> 和 &lt;code class="">root&lt;/code> 分区&lt;/a>。&lt;/li>&lt;/ul>&lt;/li>&lt;li>完成 &lt;code class="">rockchip&lt;/code> 通用镜像的适配，在 rock5b 上进行了测试。&lt;/li>&lt;li>在 Radxa Cubie A7Z 设备上实现了 deepin 镜像的运行，预计下周开始进一步适配。&lt;/li>&lt;li>新增了以下设备的 deepin 25 测试镜像:
&lt;ul>&lt;li>&lt;code class="">rockchip&lt;/code>:
&lt;ul>&lt;li>20251103-111909&lt;/li>&lt;li>20251103-114144&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-kernel&lt;/code> 项目：
&lt;ul>&lt;li>增加了 &lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/35680499e715d76287ff13ce1acdcefb0ea2032f" rel="nofollow">radxa 提供的 A733 u-boot&lt;/a>。&lt;/li>&lt;li>为 radxa 提供的 A733 u-boot 修改：
&lt;ul>&lt;li>拆分了原仓库的构建流程。&lt;/li>&lt;li>提升 BOOTM_SIZE 以启动更大的内核和 initrd。&lt;/li>&lt;li>backport &lt;code class="">ufetch&lt;/code> 命令。&lt;/li>&lt;li>backport &lt;code class="">booti&lt;/code> 命令的压缩支持以启动 deepin 内核。&lt;/li>&lt;li>调整默认环境变量以启动压缩内核启动支持。&lt;/li>&lt;li>使用系统 arm 工具链编译 u-boot。&lt;/li>&lt;/ul>&lt;/li>&lt;li>修复了 Radxa 提供的 A733 5.15 内核的构建流程，&lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/d10f0a9f8b1b4f308c598c3d07c09594fd7cc320" rel="nofollow">使得 bsp 相关选项被打开&lt;/a>。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-profiles&lt;/code> 项目:
&lt;ul>&lt;li>增加了 &lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/1f1f28ab86291160221061255e1dd575f8c92e25" rel="nofollow">&lt;code class="">a733&lt;/code> 的配置包&lt;/a>。
&lt;ul>&lt;li>为 &lt;code class="">a733&lt;/code> 增加 Radxa 提供的 &lt;code class="">img-bxm-dkms&lt;/code> 内核驱动包。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;li>&lt;code class="">deepin-ports&lt;/code> 仓库:
&lt;ul>&lt;li>增加了 &lt;code class="">ports-board-allwinner&lt;/code> 仓库，提供 Allwinner (主要是 A733) 的驱动和编解码器支持。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="文档">文档&lt;/h2>&lt;h3 id="网页">网页&lt;/h3>&lt;ul>&lt;li>&lt;code class="">sig-deepin-ports&lt;/code> 项目：
&lt;ul>&lt;li>进一步&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/da11a3176cfa9624725d41f4fcb675f49afc1384" rel="nofollow">清理了代码&lt;/a>，并在项目中完全使用 TypeScript。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-10-31)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20251031-weekly/</link><pubDate>Fri, 31 Oct 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20251031-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-images&lt;/code> 项目：
&lt;ul>&lt;li>现可创建 &lt;code class="">rockchip&lt;/code> 通用镜像，预计下周可发布。&lt;/li>&lt;li>默认 &lt;a href="https://github.com/deepin-community/deepin-ports-image/commit/4654fe02162d038809e3fc4f032ff85933087301" rel="nofollow">使用 deb 包替代玲珑&lt;/a> 预装软件。&lt;/li>&lt;li>增加 &lt;a href="https://github.com/deepin-community/deepin-ports-image/commit/a405ca5a0e1a23b811288e83f4b9f75308af9db6" rel="nofollow">基础镜像缓存&lt;/a> 以极大提升构建速度。&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-ports-image/commit/d2ed5e1844570c42c5d850a19d28dd98e5d95ede" rel="nofollow">使用 &lt;code class="">falloc&lt;/code> 替代 &lt;code class="">dd&lt;/code>&lt;/a> 创建镜像，极大提升镜像创建速度。&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-ports-image/commit/9b0dc011e631ae54178ce1632f430b11bee88ecc" rel="nofollow">自动判断内网/外网环境&lt;/a>以切换到更快的镜像。&lt;/li>&lt;li>新增 &lt;a href="https://github.com/deepin-community/deepin-ports-image/commit/96c8df880c0ac1258f5984e787c2386856cf0cfd" rel="nofollow">GitHub 镜像打包 Workflow&lt;/a>。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-kernel&lt;/code> 项目：
&lt;ul>&lt;li>增加了 &lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/9ef2958bde5de4e631a4d399702e195282115e26" rel="nofollow">collabora 提供的 RK3588 主线 u-boot&lt;/a>。&lt;/li>&lt;li>新增 &lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/0015412cc3784b72fad72c84cfe8f5da2a543e23" rel="nofollow">&lt;code class="">PKG_DEPS&lt;/code>&lt;/a> 配置以避免无必要的 override。&lt;/li>&lt;li>新增 &lt;span>CIX 提供的 P1 6.6 内核&lt;/span>。&lt;/li>&lt;li>新增 &lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/a0ac8b407633812af75013219079284ee8c6919e" rel="nofollow">Radxa 提供的 Dragon Q6A 6.17 内核&lt;/a>。&lt;/li>&lt;li>新增 &lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/5573e6203f76d7c9a586eb0be583c5075b04b18f" rel="nofollow">Radxa 提供的 Rockchip 的 6.1 内核&lt;/a>。&lt;/li>&lt;li>为 &lt;span>Rockchip 6.1 内核增加&lt;/span>:
&lt;ul>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/928691a58bbbb31821747970a557c361d7bd994f" rel="nofollow">PWN 风扇驱动&lt;/a>&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/c11778f8f69971a625e9605fcc66b6d19c315e62" rel="nofollow">启用固件压缩&lt;/a>&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/20d0122defaf7a9f8f5616d94eaf6996aa2b12b3" rel="nofollow">为 RK3588 默认启用 Panthor GPU&lt;/a>&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-ports-kernel/commit/e8a50c9b8d43bcb588eb3cd299fff8b1fd877bf2" rel="nofollow">增加了由 Radxa 提供的部分选项&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-profiles&lt;/code> 项目:
&lt;ul>&lt;li>增加了 &lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/eaeb92dcb8234f04989813c6f7ab5ce255ed74f4" rel="nofollow">&lt;code class="">cix-p1&lt;/code> 的配置包&lt;/a>。&lt;/li>&lt;li>增加了 &lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/ad6dc0a782f54fbc0ac043df9e29d2c5ed8c3758" rel="nofollow">&lt;code class="">rockchip&lt;/code> 的配置包&lt;/a>。
&lt;ul>&lt;li>由于内核问题，为 &lt;code class="">rockchip&lt;/code> 配置包暂时 &lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/ad6dc0a782f54fbc0ac043df9e29d2c5ed8c3758" rel="nofollow">禁用 &lt;code class="">deepin-anything&lt;/code>&lt;/a> 服务。&lt;/li>&lt;li>为 &lt;code class="">rockchip&lt;/code> 配置包增加 &lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/a17fbcc58e76d776dd72cc090f740b6808f84529" rel="nofollow">u-boot 配置&lt;/a>。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;li>&lt;code class="">deepin-ports&lt;/code> 仓库:
&lt;ul>&lt;li>增加了 &lt;code class="">ports-board-rockchip&lt;/code> 仓库，提供 Rockchip 的编解码器支持。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-10-24)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20251024-weekly/</link><pubDate>Fri, 24 Oct 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20251024-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>deepin-ports SIG 开始进行 Radxa Cubie A7Z 和 Rock5B 的适配工作。&lt;/li>&lt;li>&lt;code class="">deepin-ports-images&lt;/code> 项目：
&lt;ul>&lt;li>开始 Radxa Cubie A7Z 的初步适配，&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/15d53dde5131b1153a5aa7157baab14d701961a1" rel="nofollow">并增加了镜像配置&lt;/a>。&lt;/li>&lt;/ul>&lt;/li>&lt;li>新增了以下设备的 deepin 25 测试镜像:
&lt;ul>&lt;li>&lt;code class="">a7z&lt;/code>:
&lt;ul>&lt;li>20251023-174830&lt;/li>&lt;li>20251023-212347&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-kernel&lt;/code> 项目：
&lt;ul>&lt;li>增加了 &lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/9e1de3c29be9a80bb3f6827f8ac9887f383deeca" rel="nofollow">Radxa Dragon Q6A 的 6.17 内核&lt;/a>。&lt;/li>&lt;li>增加了 &lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/2306b1a36101e5c8bf7bad6146cb3c8fcfb08b74" rel="nofollow">Radxa 提供的 Allwinner A733 的 5.15 内核&lt;/a>，由 &lt;code class="">a7z-radxa-515y&lt;/code> 更名而来，提供 Radxa 下所有 Allwinner A733 设备的通用支持。&lt;/li>&lt;li>增加了 &lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/54b957c798a6a26725323a1c0e4c3d73d5a14c5d" rel="nofollow">Radxa 提供的 Rockchip 的 6.1 内核&lt;/a>，提供 Radxa 下所有 Rockchip 设备的通用支持。&lt;/li>&lt;/ul>&lt;/li>&lt;li>&lt;code class="">ports-kernel&lt;/code> 仓库增加了上述内核打包好的 deb 软件包。&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>尝试为 &lt;code class="">ports-apps&lt;/code> 仓库加入来自 Radxa 的 &lt;code class="">rsetup&lt;/code> 支持。&lt;/li>&lt;/ul>&lt;h2 id="测试">测试&lt;/h2>&lt;ul>&lt;li>测试了以下镜像：
&lt;ul>&lt;li>&lt;code class="">k1&lt;/code>: deepin 25 Preview 20250804-152839 &lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/e8bb5f6e07f4b9ae1ceab305f5a32476fd25272c" rel="nofollow">测试报告&lt;/a>&lt;/li>&lt;li>&lt;code class="">th1520&lt;/code>: deepin 25 Preview 20250928-121959 &lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/20ce9a86a4c67037b0c783948386500f793b8bf4" rel="nofollow">测试报告&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;li>针对 RuyiSDK 论坛上&lt;a href="https://ruyisdk.cn/t/topic/1715/2" rel="nofollow">有关 TH1520 测试的反馈&lt;/a>，对报告进行了修改和补充说明。&lt;/li>&lt;/ul>&lt;h2 id="文档">文档&lt;/h2>&lt;h3 id="安装指南">安装指南&lt;/h3>&lt;ul>&lt;li>新增了如下设备的安装指南：
&lt;ul>&lt;li>&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/de7713b9115fa2f0b42e99f2cd831971888fed0c" rel="nofollow">T-Head TH1520&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;li>为 T-Head TH1520 安装指南增加了 &lt;a href="https://github.com/deepin-community/sig-deepin-ports/pull/15" rel="nofollow">SD 卡启动的说明&lt;/a>。&lt;/li>&lt;/ul>&lt;h3 id="网页">网页&lt;/h3>&lt;ul>&lt;li>&lt;code class="">sig-deepin-ports&lt;/code> 项目：
&lt;ul>&lt;li>为「支持矩阵」&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/35c878be46a88cafb31f60a2ba81aee233effbb0" rel="nofollow">增加多架构显示功能&lt;/a>&lt;/li>&lt;li>为网页中引用的 JSON 数据&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/35c878be46a88cafb31f60a2ba81aee233effbb0" rel="nofollow">配置了类型注解&lt;/a>。&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/f2261dca674e867f9f15b2d3931c2b6e6d7a6210" rel="nofollow">修复和优化&lt;/a>了首页按钮的排版。&lt;/li>&lt;/ul>&lt;/li>&lt;li>新增 &lt;a href="https://github.com/YukariChiba/deepin-repo-viewer/" rel="nofollow">&lt;code class="">deepin-repo-viewer&lt;/code>&lt;/a> 项目，可通过 &lt;a href="https://deepin-repo-viewer.pages.dev" rel="nofollow">deepin-repo-viewer.pages.dev&lt;/a> 访问并查询 deepin 绝大部分公开仓库内的软件包版本信息。&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>开始筹备 deepin 的 RVA23 支持计划，调研可行的方案。&lt;/li>&lt;li>调整了 GitHub Project 的&lt;a href="https://github.com/orgs/deepin-community/projects/684/views/1" rel="nofollow">甲辰计划看板&lt;/a>，增加了分类信息以便优先处理软件包的修复 PR。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-10-17)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20251017-weekly/</link><pubDate>Fri, 17 Oct 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20251017-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>新增了以下设备的 deepin 25 测试镜像:
&lt;ul>&lt;li>&lt;code class="">q6a&lt;/code>:
&lt;ul>&lt;li>20251014-163626&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin/kernel&lt;/code> 审阅并接收了 SG2042/SG2044 的支持 patch:
&lt;ul>&lt;li>&lt;a href="https://github.com/deepin-community/kernel/pull/1232" rel="nofollow">第一部分&lt;/a>&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/kernel/pull/1233" rel="nofollow">第二部分&lt;/a>&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/kernel/pull/1235" rel="nofollow">第三部分&lt;/a>&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/kernel/pull/1236" rel="nofollow">第四部分&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;li>&lt;code class="">deepin-ports-kernel&lt;/code> 项目：
&lt;ul>&lt;li>为 &lt;code class="">q6a&lt;/code> 内核&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/1234b58aecce3e29159fbc355dd8947eb537ee04" rel="nofollow">补全 USB 声卡驱动&lt;/a>&lt;/li>&lt;li>增加了&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/135e5cd1a401f01045bdd19def8cbdcb2e3d80d7" rel="nofollow">知合计算 A210 6.6 内核&lt;/a>的构建&lt;/li>&lt;li>增加了&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/9c8438a7599620fdf1d8c3cc5d446a3c64768058" rel="nofollow">知合计算 A210 u-boot&lt;/a>的构建&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="文档">文档&lt;/h2>&lt;h3 id="安装指南">安装指南&lt;/h3>&lt;ul>&lt;li>新增了如下设备的安装指南：
&lt;ul>&lt;li>&lt;a href="https://deepin-community.github.io/sig-deepin-ports/docs/install/th1520" rel="nofollow">T-Head TH1520&lt;/a> (eMMC)&lt;/li>&lt;/ul>&lt;/li>&lt;li>在 SpacemiT K1 安装指南中&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/9b4f4bc47651484514d7f47d7de9cb137715debc" rel="nofollow">提及 Ky X1 的兼容方法&lt;/a>。&lt;/li>&lt;/ul>&lt;h3 id="测试报告">测试报告&lt;/h3>&lt;ul>&lt;li>&lt;code class="">th1520&lt;/code>: deepin 25 Preview 20250928-121959 &lt;a href="https://deepin-community.github.io/docs/test/deepin-25-crimson-preview-riscv64-eic7700-20250603-105747.tar.xz">测试报告&lt;/a>&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>接收两位新的甲辰计划实习生。&lt;/li>&lt;li>为甲辰计划实习生增加 &lt;a href="https://github.com/orgs/deepin-community/projects/684/views/1" rel="nofollow">GitHub 的 Project 看板&lt;/a>，以便更快地协助和参与 PR 的评审。&lt;/li>&lt;li>获得一台 DP1000 整机，对已有镜像进行了测试。&lt;/li>&lt;li>新闻稿：&lt;a href="https://mp.weixin.qq.com/s/bbUrDXbQEffxVJGZ9U1Csg" rel="nofollow">deepin 25 成功适配瑞莎 Dragon Q6A，开启高性能 ARM 桌面体验！&lt;/a>&lt;/li>&lt;li>寄出一台 MUSE Pi Pro 用于甲辰计划打包。&lt;/li>&lt;/ul>&lt;h2 id="其它-sig-非-sig-事务">其它 SIG / 非 SIG 事务&lt;/h2>&lt;ul>&lt;li>Ham Radio SIG: gridtracker 已上架 deepin 应用商店。&lt;/li>&lt;li>为社区配置了 deepin 社区宽带，提升了内部工作效率。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-09-26)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20250926-weekly/</link><pubDate>Fri, 26 Sep 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20250926-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-images&lt;/code> 项目：
&lt;ul>&lt;li>&lt;code class="">q6a&lt;/code> 镜像&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/7700fb5825cf4d67d515a6a50fd6254274ffd003" rel="nofollow">增加了 &lt;code class="">ports-board-radxa-q6a&lt;/code> 仓库支持&lt;/a>。&lt;/li>&lt;li>&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/648167306fa929e51c2d79f93ccc6747e6b3ad45" rel="nofollow">不再提供&lt;/a> deepin 23 的镜像构建。&lt;/li>&lt;li>&lt;code class="">/etc/product-info&lt;/code> 的内容&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/0b8d7b0d8503c24c08e72d5ce38ed4443d788e04" rel="nofollow">现已使用正确的方式写入&lt;/a>。&lt;/li>&lt;li>&lt;code class="">ports-tmpfix&lt;/code> 仓库&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/ce3aa836ff7fe6ccc97a6ac2793abad196e2877f" rel="nofollow">已被移除预置&lt;/a>。&lt;/li>&lt;li>创建 EFI 分区时，&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/a87214881b4d20ae43fa7570e01e210ad52b3cd2" rel="nofollow">现使用 &lt;code class="">mkfs.fat&lt;/code>&lt;/a> 而非 &lt;code class="">mkfs.fat -F 32&lt;/code>。&lt;/li>&lt;li>对设备&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/03d065a288410e70f5f5d65242c773fe176333c6" rel="nofollow">默认设置 &lt;code class="">RESUME=none&lt;/code>&lt;/a> 禁用 STD 休眠。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>&lt;a href="https://ci.deepin.com/repo/deepin/deepin-ports/repo/pool/ports-board-radxa-q6a/" rel="nofollow">增加了&lt;/a> &lt;code class="">ports-board-radxa-q6a&lt;/code> 仓库以提供 &lt;code class="">q6a&lt;/code> 特定软件包支持。&lt;/li>&lt;li>&lt;a href="https://ci.deepin.com/repo/deepin/deepin-ports/repo/pool/ports-board-radxa-q6a/r/" rel="nofollow">增加了&lt;/a> &lt;code class="">radxa-alsa-config&lt;/code> &lt;code class="">rockchip-alsa-config&lt;/code> 包以尝试提供音频支持。&lt;/li>&lt;li>&lt;code class="">deepin-ports-profiles&lt;/code> 项目：
&lt;ul>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/3df27306d78180a1894228a4ae2f304408ec606f" rel="nofollow">增加了&lt;/a> &lt;code class="">rockchip-alsa-config&lt;/code> 依赖。&lt;/li>&lt;/ul>&lt;/li>&lt;li>由于不再使用，&lt;code class="">deepin-ports&lt;/code> 仓库移除 &lt;code class="">ports-tmpfix&lt;/code> 组件。&lt;/li>&lt;li>为 &lt;code class="">mesa&lt;/code> 引入 patch 以&lt;a href="https://github.com/deepin-community/mesa/pull/31" rel="nofollow">修复 LLVMpipe 问题&lt;/a>。&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>接收一位新的甲辰计划实习生。&lt;/li>&lt;li>寄出一台 lpi4a 用于甲辰计划测试。&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/rfcs/pull/13" rel="nofollow">重写了 RFC 0013 (electron 打包提案) 的草稿&lt;/a>，并将其标记为等待审阅。&lt;/li>&lt;/ul>&lt;h2 id="其它-sig-非-sig-事务">其它 SIG / 非 SIG 事务&lt;/h2>&lt;ul>&lt;li>Ham Radio SIG: 将对应的无线电相关软件包&lt;a href="https://github.com/deepin-community/Repository-Manager/commit/2e7786705a12c6cb6da5e58719b5714d2a32b9bf" rel="nofollow">转移到 hamradio 组维护&lt;/a>。&lt;/li>&lt;li>发布了使用 deepin 利用 GitHub Actions 进行打包的&lt;a href="https://github.com/YukariChiba/buildpackage-deepin/" rel="nofollow">自动化流程&lt;/a>。
&lt;ul>&lt;li>在 deepin 论坛上同步发布了内容：&lt;a href="https://bbs.deepin.org/post/291787" rel="nofollow">白嫖 GitHub Actions，一键打包 deepin deb 软件包&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;li>发布了将 deepin ISO 镜像转换为包含持久化分区的 LiveUSB 的&lt;a href="https://gist.github.com/YukariChiba/065f83938bc3ccdd90bbfa81995c1254" rel="nofollow">脚本&lt;/a>。
&lt;ul>&lt;li>在 deepin 论坛上同步发布了内容：&lt;a href="https://bbs.deepin.org/post/291384" rel="nofollow">轻松创建持久化、可保存数据的 deepin live USB 镜像&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-09-19)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20250919-weekly/</link><pubDate>Fri, 19 Sep 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20250919-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-images&lt;/code> 项目：
&lt;ul>&lt;li>&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/60a7b3c65b4fb487b60c3dde02b496771fbe9a50" rel="nofollow">修复了&lt;/a> x-systemd.device-timeout 不生效的问题，降低了挂载的等待时间。&lt;/li>&lt;li>&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/91b004a4557ae60d76745ce831654f016a894529" rel="nofollow">增加了&lt;/a> &lt;code class="">mkfs_helper&lt;/code> 方便创建不同的文件系统，并默认 4k 对齐。&lt;/li>&lt;/ul>&lt;/li>&lt;li>新增了以下设备的 deepin 25 测试镜像:
&lt;ul>&lt;li>&lt;code class="">q6a&lt;/code>:
&lt;ul>&lt;li>20250918-174812&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;li>验证了 &lt;code class="">q6a&lt;/code> 的 deepin 镜像和 ubuntu 镜像的功能可用性，接收来自 radxa 的 &lt;code class="">q6a&lt;/code> 镜像测试报告。&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>发现了 &lt;code class="">packagekit&lt;/code> riscv64 版本未更新，已修复。&lt;/li>&lt;li>为 &lt;code class="">grub2&lt;/code> &lt;a href="https://github.com/deepin-community/grub2/pull/28" rel="nofollow">打上了上游 patch&lt;/a>，以使用 &lt;code class="">rdtime&lt;/code> 而非 &lt;code class="">rdcycle&lt;/code> 指令。&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>接收了&lt;a href="https://github.com/deepin-community/kernel/pull/1160" rel="nofollow">来自超睿的内核 patch&lt;/a>，审阅测试中。&lt;/li>&lt;/ul>&lt;h2 id="文档">文档&lt;/h2>&lt;ul>&lt;li>&lt;a href="https://github.com/deepin-community/SIG/commit/707846ca738211062e7f60ca560be79e57fed08f" rel="nofollow">修改了&lt;/a> deepin-ports SIG 的目标:
&lt;ul>&lt;li>负责 deepin 多种架构、多种设备的适配（尤其是无法通过标准的主线 iso 适配、需要额外支持的），并进行针对性的软件包构建、镜像构建、Bug 修复等工作。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>获得一块 Radxa Cubie A7Z 开发板，即将开始进行适配。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-09-12)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20250912-weekly/</link><pubDate>Fri, 12 Sep 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20250912-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-images&lt;/code> 项目：
&lt;ul>&lt;li>后续镜像构建&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/61441b495e7db25507fe5f4db1937a463ccd8701" rel="nofollow">将包括 &lt;code class="">deepin-desktop-environment-ll&lt;/code> 包&lt;/a>，并&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/1d3167972ff2b8e3a4876d9579787728523ca341" rel="nofollow">使用 &lt;code class="">ll-cli&lt;/code> 预装应用&lt;/a>，以修复在非 &lt;code class="">riscv64&lt;/code> 架构下预装包列表不全的问题。&lt;/li>&lt;li>&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/f52f9e35b454c2046739f736efe6941a7a84ee4d" rel="nofollow">提供了&lt;/a> linglong 的内网镜像支持，以便在内网构建时进行加速。&lt;/li>&lt;li>&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/6892a56cea22d8fbcea7ab860b7d27ffe26a42a7" rel="nofollow">增加了&lt;/a>构建镜像时写入 &lt;code class="">grub.cfg&lt;/code> 配置文件的功能。&lt;/li>&lt;li>&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/b691efb24f61c420b88c364955ed5b6e83de064d" rel="nofollow">增加了&lt;/a> &lt;code class="">q6a&lt;/code> 的 GRUB 配置文件，并&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/7502c14ab220a38c48b08c21c8acdf2d4a09b747" rel="nofollow">修复了&lt;/a>了启动参数，以支持在非 sd 卡启动。&lt;/li>&lt;li>由于打包 tarball 时并不存在 network-manager 和 locales，&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/044584875039eab90a5a336cb03fd1d6c6226926" rel="nofollow">为相关的 hook 进行了禁用&lt;/a>。&lt;/li>&lt;li>&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/62a89347cc3950d0d749b4a4b1f9e0d1b832e202" rel="nofollow">增加了&lt;/a> &lt;code class="">q6a&lt;/code> 的 &lt;code class="">genimage&lt;/code> 配置，新镜像提供整个 &lt;code class="">.img&lt;/code> 格式的文件，可直接进行写入。&lt;/li>&lt;li>新的 &lt;code class="">q6a&lt;/code> 镜像将&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/4e0976754d43582b6569457fba8cb26e93ef2bca" rel="nofollow">预装内核 header&lt;/a> 和 aic8800 驱动。&lt;/li>&lt;/ul>&lt;/li>&lt;li>重新发布了 tarball 镜像，验证并解决了 &lt;a href="https://github.com/linuxdeepin/developer-center/issues/12157" rel="nofollow">issue 12157&lt;/a> 中 riscv64 tarball 中库文件损坏的问题。&lt;/li>&lt;li>新增了以下设备的 deepin 25 测试镜像:
&lt;ul>&lt;li>&lt;code class="">tarball&lt;/code>:
&lt;ul>&lt;li>20250908-140807&lt;/li>&lt;/ul>&lt;/li>&lt;li>&lt;code class="">q6a&lt;/code>:
&lt;ul>&lt;li>20250908-150501&lt;/li>&lt;li>20250908-225014&lt;/li>&lt;li>20250908-231253&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-profiles&lt;/code> 项目：
&lt;ul>&lt;li>现&lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/48189972891bdecccb39893e0e5a489e9dfc82df" rel="nofollow">支持 &lt;code class="">q6a&lt;/code> 设备&lt;/a>，并增加了相应的配置文件和&lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/edd1779fdae97a1740dd03ae76b8d57bbd35f546" rel="nofollow">驱动依赖&lt;/a>，&lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/dc752b52025ccaea4a9ce823e3b608269fc0531c" rel="nofollow">增加模块加载配置文件&lt;/a>以修复蓝牙问题。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">deepin-ports-kernel&lt;/code> 项目：
&lt;ul>&lt;li>arm 内核打包时，现在会&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/d57a51a8ad59c82f346209b61b906b93700f22ff" rel="nofollow">使用 GitHub 提供的原生 arm worker&lt;/a>。&lt;/li>&lt;li>在非 cross 打包环境下，打包&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/7e04c7a7b9a408be2c1b57aecfbaf0a2a8edbe34" rel="nofollow">会产生 linux-headers 包&lt;/a>。&lt;/li>&lt;li>在非 cross 打包环境下，&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/c273f1344edf2b3213402fa98c5ec33e56b44153" rel="nofollow">不再安装&lt;/a> cross 工具链并设置 CROSS_COMPILE 变量。&lt;/li>&lt;/ul>&lt;/li>&lt;li>&lt;code class="">ports-kernel&lt;/code> 仓库增加了基于 &lt;code class="">radxa-pkg&lt;/code> 源码构建的 &lt;a href="https://ci.deepin.com/repo/deepin/deepin-ports/repo/pool/ports-kernel/a/aic8800/" rel="nofollow">&lt;code class="">aic8800&lt;/code> 的 dkms 模块包&lt;/a>。&lt;/li>&lt;li>&lt;code class="">ports-kernel&lt;/code> 仓库增加了 &lt;a href="https://ci.deepin.com/repo/deepin/deepin-ports/repo/pool/ports-kernel/l/linux-upstream/" rel="nofollow">&lt;code class="">q6a&lt;/code> 的 header 包&lt;/a>。&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/cc4c5a64629fc9a2918acfc7dd8ff97b47423ce2" rel="nofollow">修复了&lt;/a> q6a 内核因为 &lt;code class="">run-parts&lt;/code> 新版行为修改导致无法安装内核的问题。&lt;/li>&lt;/ul>&lt;h2 id="文档">文档&lt;/h2>&lt;h3 id="网页">网页&lt;/h3>&lt;ul>&lt;li>在「镜像下载」页面&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/c93caff1934efe6dd45afb5265c86f4053cbaabf" rel="nofollow">区分了 RISC-V 和 ARM 架构&lt;/a>，分别展示各自的镜像。&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>由于 deepin-ports SIG 现在接手 ARM 设备适配，且 LoongArch 架构的主线支持已足够成熟，已在 TC 例会中要求更新 deepin-ports SIG 的目标。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-09-05)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20250905-weekly/</link><pubDate>Fri, 05 Sep 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20250905-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>在 &lt;code class="">deepin-ports-images&lt;/code> 内同时&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/9d7982327752a14f219439f4b1ab531af8ba2065" rel="nofollow">支持了 Dragon Q6A 的镜像构建&lt;/a>&lt;/li>&lt;li>&lt;code class="">deepin-ports-images&lt;/code> &lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/d50c08b420d65b438db360ef7bf1e426d16a3ad9" rel="nofollow">现在可以&lt;/a>在没有独立 boot 分区的情况下创建 efi 分区了&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>为 &lt;code class="">sg2042&lt;/code> &lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/4dee03da2c17752386072835dd9a116e15982e8e" rel="nofollow">预装 &lt;code class="">mtd-utils&lt;/code>&lt;/a> 以便写入 bootloader 到 MTD 中，对应的操作步骤已经写入安装指南。&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>&lt;a href="https://ci.deepin.com/repo/deepin/deepin-ports/repo/pool/ports-kernel/l/linux-upstream/" rel="nofollow">更新了&lt;/a> &lt;code class="">sg2042&lt;/code> 的内核到 6.16.4 以修复 nvme 识别的问题。&lt;/li>&lt;li>由于 cross 编译的 linux-headers 二进制为宿主架构，为所有 cross 构建的内核&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/dca791b3d3adf7788d24b0a48cfcebbbc880ca78" rel="nofollow">关闭构建&lt;/a> linux-headers 包&lt;/li>&lt;li>deepin-riscv-kernel &lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/pull/47" rel="nofollow">同时接受&lt;/a> &lt;code class="">q6a-radxa-615y&lt;/code> 的 arm 内核构建。&lt;/li>&lt;/ul>&lt;h2 id="文档">文档&lt;/h2>&lt;h3 id="安装指南">安装指南&lt;/h3>&lt;ul>&lt;li>新增了如下设备的安装指南：
&lt;ul>&lt;li>&lt;a href="https://deepin-community.github.io/docs/install/sg2042">Sophgo SG2042&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>（上周）deepin 参展 elexcon 2025。详细介绍请参阅公众号新闻：&lt;a href="https://mp.weixin.qq.com/s/ToU8pwdHp18NH0iV64P2Kg" rel="nofollow">deepin 亮相 elexcon2025 深圳国际电子展，全面展示 RISC-V 生态建设新成果&lt;/a>&lt;/li>&lt;li>为 OSPP 学生寄出一台 MUSE Pi Pro 以便更好完成项目。&lt;/li>&lt;li>为甲辰计划实习生寄出一台 LicheePi 4A。&lt;/li>&lt;li>甲辰计划迎来两名新实习生。&lt;/li>&lt;li>获得一块 Radxa Dragon Q6A 开发板，开始进行适配。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-08-22)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20250822-weekly/</link><pubDate>Fri, 22 Aug 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20250822-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>为 &lt;code class="">th1520&lt;/code> 的启动配置&lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/55d68608879f79d2e90c6a19e31268592462904d" rel="nofollow">移除了&lt;/a> &lt;code class="">hidev0p7&lt;/code> 的无用选项。&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>&lt;code class="">chromium&lt;/code> 139 版本已在以下架构完成构建，将在测试完成后推入主仓库：
&lt;ul>&lt;li>&lt;code class="">riscv64&lt;/code>&lt;/li>&lt;li>&lt;code class="">amd64&lt;/code>&lt;/li>&lt;li>&lt;code class="">arm64&lt;/code>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>由于 deepin 主线内核 riscv64-desktop-* 已有稳定的构建工作流，此内核&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/c14d204b5c34325d25876e1c6f72841be61ec343" rel="nofollow">不再在 &lt;code class="">deepin-riscv-kernel&lt;/code> 中提供&lt;/a>。&lt;/li>&lt;li>观测到处于开发分支的 eic7700 6.12 内核，现于 &lt;code class="">deepin-riscv-kernel&lt;/code> 中&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/656127bdfadfcf18703320ac9aeb0802f9323bc9" rel="nofollow">跟踪观测&lt;/a>。&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>筹备参展 elexcon 2025 的海报和材料。
&lt;ul>&lt;li>海报制作完毕，设计稿如下：
&lt;img alt="Poster" src="https://deepin-community.github.io/sig-deepin-ports/img/blog/20250822-weekly/poster.jpg" />&lt;/li>&lt;/ul>&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-08-15)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20250815-weekly/</link><pubDate>Fri, 15 Aug 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20250815-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>现在 &lt;code class="">sg2042&lt;/code> 镜像的内核&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/4ee912292c5d34c9d388a436f8f0bb142701a45b" rel="nofollow">默认使用 6.16 版本&lt;/a>。&lt;/li>&lt;li>由于 &lt;code class="">u-boot-menu&lt;/code> 配置现在由 &lt;code class="">deepin-ports-profiles&lt;/code> 提供，镜像创建脚本中除定制镜像以外的对应配置文件&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/8b4f56c13fed66ba76d1c09d7e37b46cfec8abb3" rel="nofollow">均被移除&lt;/a>。&lt;/li>&lt;li>所有镜像&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/0a699d3d8c278dcbe3dbc28223c06e7f7e3e8f89" rel="nofollow">均预装 &lt;code class="">deepin-ports-keyring&lt;/code>&lt;/a>，因此 &lt;code class="">[trusted=yes]&lt;/code> 从 &lt;code class="">deepin-ports&lt;/code> 源配置中移除。&lt;/li>&lt;li>由镜像生成脚本定制的 &lt;code class="">u-boot-menu&lt;/code> 文件现在&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/242820e4fc14b61d4fb283aef79940bc1234a1d0" rel="nofollow">将被安装&lt;/a>至 &lt;code class="">usr/share/u-boot-menu/conf.d/99-custom.conf&lt;/code> 以避免覆盖默认配置。&lt;/li>&lt;li>新增了以下设备的 deepin 25 测试镜像:
&lt;ul>&lt;li>&lt;code class="">sg2042&lt;/code>:
&lt;ul>&lt;li>20250812-205045&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>由实习生提交的 &lt;code class="">chromium&lt;/code> 139 版本&lt;a href="https://github.com/deepin-community/chromium-riscv64/pull/1" rel="nofollow">正在打包测试&lt;/a>中。&lt;/li>&lt;li>&lt;code class="">deepin-ports-keyring&lt;/code> &lt;a href="https://github.com/YukariChiba/deepin-ports-keyring/" rel="nofollow">已被创建&lt;/a>并包含在 &lt;code class="">ports-profiles&lt;/code> 中，将在 &lt;code class="">deepin-ports&lt;/code> 各镜像中预装。&lt;/li>&lt;li>&lt;code class="">deepin-ports-profiles&lt;/code> 现在&lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/915eeb1c96dd77cca7249f2778be65a021664a7d" rel="nofollow">包含各个设备的 &lt;code class="">u-boot-menu&lt;/code> 配置&lt;/a>。&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>仓库&lt;a href="https://ci.deepin.com/repo/deepin/deepin-ports/repo/pool/ports-kernel/l/" rel="nofollow">不再提供&lt;/a> &lt;code class="">th1520&lt;/code> 的 &lt;code class="">5.15&lt;/code> 内核。&lt;/li>&lt;li>为内核构建流程增加&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/f97934d79b21d1c817f87727c123ca73695d5340" rel="nofollow">引入外部 patch 的支持&lt;/a>。&lt;/li>&lt;li>为以下内核&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/712f13faff619ac3013aaac4c4ccfdc3f3c81148" rel="nofollow">增加 &lt;code class="">cjktty&lt;/code> 支持&lt;/a>：
&lt;ul>&lt;li>&lt;code class="">eic770x-rockos-66y&lt;/code>&lt;/li>&lt;li>&lt;code class="">k1-spacemit-66y&lt;/code>&lt;/li>&lt;li>&lt;code class="">sg2042-upstream-66y&lt;/code>&lt;/li>&lt;li>&lt;code class="">th1520-revyos-66y&lt;/code>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>新闻稿：&lt;a href="https://mp.weixin.qq.com/s/MDnIhPNAh6KfKCeHHNHeEQ" rel="nofollow">RISC-V World Day&lt;/a>&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-08-08)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20250808-weekly/</link><pubDate>Fri, 08 Aug 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20250808-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>现在 &lt;code class="">k1&lt;/code> 镜像的内核&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/0a97d9d1cafd0c9e686c0504d71a7be260ee65a3" rel="nofollow">已更新&lt;/a>为使用 deepin-riscv-kernel 构建工作流的产出版本。&lt;/li>&lt;li>由于 &lt;code class="">hwe&lt;/code> 仓库位置变更，&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/5833b47773ba4559e1fa35b14c24b54801edf430" rel="nofollow">更新了&lt;/a> &lt;code class="">hwe&lt;/code> 仓库的地址。&lt;/li>&lt;li>由于 &lt;code class="">hwe&lt;/code> 仓库位置变更，&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/5b5407c7b5f8cec7d334ad0d8492d0add7718ea4" rel="nofollow">再次更新了&lt;/a> &lt;code class="">hwe&lt;/code> 仓库的地址，并同时将一部分 &lt;code class="">ci.deepin.com&lt;/code> 的地址换成了正式仓库的地址。
&lt;ul>&lt;li>已有用户可将 stable 源替换为 &lt;code class="">deb https://community-packages.deepin.com/deepin/beige/ crimson main community commercial&lt;/code> 以提升访问速度。&lt;/li>&lt;li>已有用户可将 testing 源替换为 &lt;code class="">deb https://proposed-packages.deepin.com/beige-testing/ unstable/crimson main community commercial&lt;/code> 以提升访问速度。&lt;/li>&lt;li>已有用户可将 hwe 源替换为 &lt;code class="">deb https://community-packages.deepin.com/hwe-25/ unstable main community commercial&lt;/code> 以提升访问速度。&lt;/li>&lt;/ul>&lt;/li>&lt;li>&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/093a69b7a8a09d65292829fca6f0240cd3f7e6b6" rel="nofollow">修改内核参数&lt;/a>以修复 &lt;code class="">sg2042&lt;/code> 6.16 内核在 MilkV Pioneer 上桥片失灵导致 USB 不工作的问题。&lt;/li>&lt;li>在镜像生成脚本中&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/a32eeb0dee67ec38d07f97b3ac7d61cf80cacee6" rel="nofollow">新增了&lt;/a>免安装器（预先创建用户）的镜像支持。&lt;/li>&lt;li>镜像生成脚本&lt;a href="https://github.com/deepin-community/deepin-ports-profiles/tree/k1-cloud" rel="nofollow">新增 &lt;code class="">k1-cloud&lt;/code>&lt;/a>，以支持生成在云端环境（bianbu cloud）中开箱即用的镜像，仍在调试中，对应配置&lt;a href="https://github.com/deepin-community/deepin-ports-profiles/pull/1" rel="nofollow">等待合并&lt;/a>。&lt;/li>&lt;li>新增了以下设备的 deepin 25 测试镜像:
&lt;ul>&lt;li>&lt;code class="">k1&lt;/code>:
&lt;ul>&lt;li>20250804-121920&lt;/li>&lt;li>20250804-152839&lt;/li>&lt;/ul>&lt;/li>&lt;li>&lt;code class="">k1-cloud&lt;/code>:
&lt;ul>&lt;li>20250806-180508&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>deepin-ports 仓库 &lt;code class="">ports-board-spacemit-k1&lt;/code> 支持仓库&lt;a href="https://ci.deepin.com/repo/deepin/deepin-ports/repo/pool/ports-board-spacemit-k1/b/bianbu-esos/" rel="nofollow">新增&lt;/a>打包的 &lt;code class="">esos.elf&lt;/code> 以固定内核对应的版本。&lt;/li>&lt;li>deepin-ports 仓库 &lt;code class="">ports-imggpu&lt;/code> &lt;a href="https://github.com/deepin-community/img-gpu-bin" rel="nofollow">已更新&lt;/a> &lt;code class="">k1&lt;/code> 的 DDK 24.2 firmware 和驱动二进制。&lt;/li>&lt;li>旧的 &lt;code class="">ddk232&lt;/code> 对应的 &lt;code class="">mesa&lt;/code> 分支已不再使用，&lt;a href="https://github.com/deepin-community/mesa" rel="nofollow">已删除&lt;/a>。&lt;/li>&lt;li>由于 deepin 环境下 &lt;code class="">glx&lt;/code> 打开 zink 存在问题，&lt;a href="https://github.com/deepin-community/mesa/commit/cc2461e8abd630d20f47be71e6ee916baab72c26" rel="nofollow">暂时移除&lt;/a> &lt;code class="">eic7700&lt;/code> 对应的 &lt;code class="">mesa&lt;/code> 中的强制 zink 补丁。&lt;/li>&lt;li>为所有基于 Imagination GPU 的设备通过 &lt;code class="">Recommends&lt;/code> 方式&lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/242faaafa45fc898845cb7deca9dc60415a3b6c6" rel="nofollow">预装 vulkan 驱动&lt;/a>。&lt;/li>&lt;li>为 &lt;code class="">eic7700&lt;/code> &lt;a href="https://ci.deepin.com/repo/deepin/deepin-ports/repo/pool/ports-board-eswin-eic770x/a/ap12275-firmware/" rel="nofollow">打包&lt;/a>来自 rockos 的 &lt;code class="">ap12275-firmware&lt;/code> 固件包，并&lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/aadee0c60a2a206c21419b27fbf97f6cdc03ed59" rel="nofollow">设置为预装&lt;/a>。&lt;/li>&lt;li>在deepin-ports 仓库 &lt;code class="">ports-apps&lt;/code> 中&lt;a href="https://ci.deepin.com/repo/deepin/deepin-ports/repo/pool/ports-apps/c/code-server/" rel="nofollow">新增&lt;/a> &lt;code class="">code-server&lt;/code> 应用。&lt;/li>&lt;li>deepin-ports 仓库 &lt;code class="">ports-gcc14&lt;/code> 组件被 &lt;code class="">hwe&lt;/code> 仓库替代，已删除。&lt;/li>&lt;li>应用商店因为仓库地址错误，首页无内容，已向应用商店维护组反馈，等待下一次更新修复。
&lt;ul>&lt;li>如需临时体验，可修改 &lt;code class="">/etc/apt/sources.list.d/appstore.list&lt;/code> 为 &lt;code class="">deb https://com-store-packages.uniontech.com/appstore-V25 crimson appstore&lt;/code>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>deepin-ports 仓库内 &lt;code class="">k1&lt;/code> 内核&lt;a href="https://ci.deepin.com/repo/deepin/deepin-ports/repo/pool/ports-kernel/l/linux-upstream/" rel="nofollow">新增&lt;/a>使用 deepin-riscv-kernel 构建工作流的产出。
&lt;ul>&lt;li>旧的 &lt;code class="">k1&lt;/code> 内核已从 &lt;code class="">ports-kernel&lt;/code> 仓库移除。&lt;/li>&lt;/ul>&lt;/li>&lt;li>因为众多报错无法构建，&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/54f3f6b85731fe7786b99386099a82d06b1c7f8e" rel="nofollow">暂时移除&lt;/a> jh7110 的 6.12 上游内核打包工作流。&lt;/li>&lt;li>&lt;code class="">sg2042&lt;/code> &lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/1067d776ba15372cc093193ceadfd5b3537d2bea" rel="nofollow">现在切换为&lt;/a>同时提供 &lt;code class="">6.6&lt;/code>(LTS) &lt;code class="">6.12&lt;/code>（LTS） &lt;code class="">6.16&lt;/code>（最新） 内核。&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/59cff05e97f371a80c9186374c1c19ea6b197736" rel="nofollow">不再提供&lt;/a>单独的 &lt;code class="">unmatched&lt;/code> 的主线内核。&lt;/li>&lt;/ul>&lt;h2 id="文档">文档&lt;/h2>&lt;h3 id="安装指南">安装指南&lt;/h3>&lt;ul>&lt;li>新增了如下设备的安装指南：
&lt;ul>&lt;li>&lt;a href="https://deepin-community.github.io/docs/install/k1">SpacemiT K1&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>由于堆积开发板过多且缺乏有效测试，针对现有实习生开展&lt;a href="https://wiki.deepin.org/zh/06_%E5%85%B3%E4%BA%8EDeepin/Deepin%E6%B4%BB%E5%8A%A8/%E7%94%B2%E8%BE%B0%E8%AE%A1%E5%88%92/%E7%94%B2%E8%BE%B0%E8%AE%A1%E5%88%92%EF%BC%9A%E5%BC%80%E5%8F%91%E6%9D%BF%E9%A2%86%E5%85%BB%E8%AE%A1%E5%88%92" rel="nofollow">『开发板领养计划』&lt;/a>。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-08-01)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20250801-weekly/</link><pubDate>Fri, 01 Aug 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20250801-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>由于 &lt;a href="https://ci.deepin.com/repo/deepin/deepin-community/hwe/pool/main/g/gcc-14/" rel="nofollow">&lt;code class="">hwe&lt;/code> 仓库已合入 &lt;code class="">gcc-14&lt;/code> 相关组件&lt;/a>，deepin-ports 仓库 &lt;code class="">ports-gcc14&lt;/code> 组件将弃用并将在不久后删除。&lt;/li>&lt;li>现在 &lt;code class="">eic7700&lt;/code> 镜像的内核&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/5bd07bc3a696a3e314b8d54190d5d9dff509c2ea" rel="nofollow">已更新&lt;/a>为 rockos 上游的最新版本 &lt;code class="">6.6.88&lt;/code>。&lt;/li>&lt;li>现在 &lt;code class="">generic&lt;/code> 通用 ISO 已修复 dp1000 上的 &lt;code class="">glibc&lt;/code> 问题且集成新版内核，具备对 dp1000 的支持，dp1000 成为首个获得 deepin 官方主线支持的 RISC-V SoC，详细介绍请参阅后续的公众号新闻。&lt;/li>&lt;li>由于 &lt;code class="">generic&lt;/code> 通用 ISO 已支持 dp1000，旧的 &lt;code class="">dp1000&lt;/code> 定制镜像&lt;a href="https://github.com/deepin-community/sig-deepin-ports/commit/87643487304b38fd0ec75460a0ee117c73f7ced4" rel="nofollow">已被归档&lt;/a>。&lt;/li>&lt;li>新增了以下设备的 deepin 25 测试镜像:
&lt;ul>&lt;li>&lt;code class="">eic7700&lt;/code>:
&lt;ul>&lt;li>20250729-125156&lt;/li>&lt;/ul>&lt;/li>&lt;li>&lt;code class="">generic&lt;/code>:
&lt;ul>&lt;li>20250728-172422&lt;/li>&lt;li>20250729-143705&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>由于关键依赖包更新尚未推送，以下软件包本周暂无法推送至 &lt;code class="">hwe&lt;/code> 仓库：
&lt;ul>&lt;li>mesa 25&lt;/li>&lt;li>wine&lt;/li>&lt;/ul>&lt;/li>&lt;li>超睿对先前 &lt;code class="">glibc&lt;/code> 的 PR &lt;a href="https://github.com/deepin-community/glibc/pull/31#issuecomment-3138690888" rel="nofollow">进行了解释，并公布了测试报告&lt;/a>&lt;/li>&lt;li>修复了在 k1 上未正确拷贝 &lt;code class="">esos.elf&lt;/code> 导致无法启动的问题，已通过 &lt;code class="">deepin-ports-profiles&lt;/code> &lt;a href="https://github.com/deepin-community/deepin-ports-profiles/commit/1eac5bbbad02fd10f811ab6960c3e1a6b796c438" rel="nofollow">更新&lt;/a>。&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/6fb7e0eac6f9fbdea2c34a69e38f5b1b1a4d4730" rel="nofollow">更新了&lt;/a> eic7700 的内核和 bootloader 以便支持更多开发板，例如 EIC7700 SBC。&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/7397032d4028e36551953b308385b58e778d0fac" rel="nofollow">修复了&lt;/a> eic7700 的构建 bug（导致 ddr 固件选择错误）。&lt;/li>&lt;li>完善了 u-boot 构建流程，为一部分设备&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/0060479d9e5395d7a51cf8164dae62fdaa9f272e" rel="nofollow">打上 &lt;code class="">ufetch&lt;/code> 支持补丁&lt;/a>（目前 &lt;code class="">eic7700&lt;/code> 和 &lt;code class="">k1&lt;/code> 已可用）&lt;/li>&lt;li>由于 &lt;code class="">k1&lt;/code> 内核已普遍采用 6.6 版本，不再构建旧的 &lt;code class="">k1&lt;/code> &lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/b662810a95827e53ebf2e7cb0587d683c5158b76" rel="nofollow">u-boot&lt;/a> 和 &lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/6d3f64617577cff2e354112af2c05b09c9ec0332" rel="nofollow">6.1 内核&lt;/a>。&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/deepin-riscv-kernel/commit/ac6e1a43117244dbc366a260b41d8b7300239ed2" rel="nofollow">更新&lt;/a> &lt;code class="">k1&lt;/code> 的内核到最新的 &lt;code class="">k1-bl-v2.2.y&lt;/code> 分支。&lt;/li>&lt;/ul>&lt;h2 id="基础设施">基础设施&lt;/h2>&lt;ul>&lt;li>于甲辰计划接收一台 MilkV Meles 用于测试。&lt;/li>&lt;li>于甲辰计划接收一台 EIC7700 树莓派用于测试。&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>新一轮的甲辰计划实习生招聘开始了，详情可见 &lt;a href="https://mp.weixin.qq.com/s/DooOWJQUUxRdNdUFKoXdDQ" rel="nofollow">deepin 公众号&lt;/a> 或 &lt;a href="https://ruyisdk.cn/t/topic/758" rel="nofollow">RuyiSDK 社区&lt;/a>。&lt;/li>&lt;li>预备对甲辰计划实习生开放部分 RV 设备的 SSH 访问权限，基础设施正在设置中。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-07-25)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20250725-weekly/</link><pubDate>Fri, 25 Jul 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20250725-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>后续更新的 rv 镜像将&lt;a href="https://github.com/YukariChiba/deepin-ports-image/commit/cd7398c19ea99605950b23d23e7615a269222362" rel="nofollow">默认添加 &lt;code class="">hwe&lt;/code> 仓库&lt;/a>，以提供更高版本的 GPU 驱动和 GCC 版本，以及依赖它们的更新软件包。&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>打包了 &lt;a href="https://github.com/deepin-community/llvm-toolchain-20/pull/2" rel="nofollow">llvm 20&lt;/a> 和 &lt;a href="https://github.com/deepin-community/mesa/pull/27" rel="nofollow">mesa 25&lt;/a>，将推入 &lt;code class="">hwe&lt;/code> 仓库提供更好的驱动支持。&lt;/li>&lt;li>为 Chromium 系的应用&lt;a href="https://github.com/deepin-community/chromium-riscv64/commit/66597bd514a6d5c45a8d78b509cc809c84254649" rel="nofollow">打上对应 patch&lt;/a>，修复 &lt;code class="">RDCYCLE&lt;/code> 在 RISC-V 新版内核被设置为特权指令，进而导致崩溃的问题。影响以下应用：
&lt;ul>&lt;li>electron 各版本&lt;/li>&lt;li>依赖 electron 的应用需要升级 electron&lt;/li>&lt;li>chromium&lt;/li>&lt;/ul>&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/chromium-riscv64/commit/fe4f93ca786830528361f3987232a9a4b6645269" rel="nofollow">修复了&lt;/a> Chromium 构建问题 &lt;a href="https://chromium-review.googlesource.com/c/chromium/src/+/5487538" rel="nofollow">#5487538&lt;/a>&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/box64/pull/3" rel="nofollow">升级了&lt;/a> &lt;code class="">box64&lt;/code> 应用到最新版本，并为其打开了 &lt;code class="">box32&lt;/code> 功能以兼容更多应用。&lt;/li>&lt;li>验证了 WPS Office 可通过 box64 在 RISC-V 平台上运行。&lt;/li>&lt;li>为辅助 &lt;code class="">box64&lt;/code> 更好地工作，尝试在 &lt;code class="">amd64/arm64&lt;/code> 架构上&lt;a href="https://github.com/deepin-community/wine/pull/1" rel="nofollow">打包 &lt;code class="">wine&lt;/code>&lt;/a>。
&lt;ul>&lt;li>尝试为 &lt;code class="">wine&lt;/code> 打开 WoW64 功能。&lt;/li>&lt;li>&lt;a href="https://github.com/deepin-community/khronos-api/pull/1" rel="nofollow">尝试升级&lt;/a> &lt;code class="">khronos-api&lt;/code> 到最新版本，&lt;a href="https://github.com/deepin-community/vulkan-loader/pull/4" rel="nofollow">尝试升级&lt;/a> vulkan 全家桶到 1.4。
&lt;ul>&lt;li>gcc-12 有潜在 bug 导致 spirv-tools 编译失败，&lt;a href="https://github.com/deepin-community/spirv-tools/pull/7/commits/054afdc61776915b9d5f6e759f978f71458855df" rel="nofollow">已临时绕过&lt;/a>，标记待修复：&lt;a href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115824" rel="nofollow">#115824&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;li>尝试打包 &lt;code class="">felix86&lt;/code> 以提供 RISC-V 上的 Steam 和游戏支持。&lt;/li>&lt;li>由超睿提供 patch，&lt;a href="https://github.com/deepin-community/spirv-tools/pull/7/commits/054afdc61776915b9d5f6e759f978f71458855df" rel="nofollow">修复了&lt;/a> &lt;code class="">glibc&lt;/code> 在部分情形下随机出现的以下崩溃问题：
&lt;ul>&lt;li>&lt;code class="">Fatal glibc error: ../npctl/pthread_mutex_lock:94 (_pthread_mutex_cond_lock): assertion failed: mutex->__data.__owner == 0&lt;/code>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>&lt;a href="https://github.com/deepin-community/kernel/pull/960" rel="nofollow">修复了&lt;/a>因未开启 &lt;code class="">DRM_AMD_DC_FP&lt;/code> 导致 RDNA2 显卡在 RISC-V 上不可用的问题。&lt;/li>&lt;li>修复了 amdgpu 代码中对不带向量拓展的 isa string 替换 v 导致 isa 变成 &lt;code class="">r64*&lt;/code> 的问题。&lt;/li>&lt;li>接收了来自超睿对 PCIE Bar 空间修改 &lt;a href="https://github.com/deepin-community/kernel/pull/973" rel="nofollow">的 PR&lt;/a>。&lt;/li>&lt;/ul>&lt;h2 id="测试">测试&lt;/h2>&lt;ul>&lt;li>（上周）测试了以下镜像：
&lt;ul>&lt;li>&lt;code class="">th1520&lt;/code>: deepin 25 Preview 20250711 103950 &lt;a href="https://deepin-community.github.io/docs/test/deepin-25-crimson-preview-riscv64-th1520-20250711-103950.tar.xz">测试报告&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="基础设施">基础设施&lt;/h2>&lt;ul>&lt;li>接收一台 MUSE Pi Pro 用于适配与测试。&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>（上周）deepin 参展 RISC-V 中国峰会。详细介绍请参阅公众号新闻：&lt;a href="https://mp.weixin.qq.com/s/Yn48NzciUuSoyVrnptNv3A" rel="nofollow">deepin 亮相 2025 RISC-V 中国峰会，展现开源生态无限可能！&lt;/a>&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-07-11)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20250711-weekly/</link><pubDate>Fri, 11 Jul 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20250711-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>默认使用 6.6 内核构建 &lt;code class="">th1520&lt;/code> 镜像，默认对 &lt;code class="">th1520&lt;/code> 使用主线 openSBI。&lt;/li>&lt;li>新增了以下设备的 deepin 25 测试镜像:
&lt;ul>&lt;li>&lt;code class="">th1520&lt;/code>:
&lt;ul>&lt;li>20250707-135951&lt;/li>&lt;li>20250707-164130&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>为 deepin-ports-profiles 移除对 &lt;code class="">th1520-vendor-opensbi&lt;/code> 的硬依赖。&lt;/li>&lt;/ul>&lt;h2 id="内核更新">内核更新&lt;/h2>&lt;ul>&lt;li>向 deepin-ports 仓库推送了最新的 &lt;code class="">th1520-revyos-66y&lt;/code> 内核，并设置为后续构建镜像的默认内核。&lt;/li>&lt;li>跟随上游同步更新了 &lt;code class="">th1520&lt;/code> 的启动固件。&lt;/li>&lt;/ul>&lt;h2 id="测试">测试&lt;/h2>&lt;ul>&lt;li>针对 &lt;code class="">th1520&lt;/code> 5.10 和 6.6 内核镜像展开测试工作。&lt;/li>&lt;/ul>&lt;h2 id="文档">文档&lt;/h2>&lt;h3 id="网页">网页&lt;/h3>&lt;ul>&lt;li>为 deepin-ports SIG（本站）：
&lt;ul>&lt;li>Blog RSS &lt;a href="https://github.com/deepin-community/.planet.deepin.org/pull/16" rel="nofollow">对接至 deepin planet&lt;/a> (&lt;a href="https://planet.deepin.org/" rel="nofollow">planet.deepin.org&lt;/a>)&lt;/li>&lt;/ul>&lt;/li>&lt;li>添加了甲辰计划和 RuyiSDK 的友情链接。&lt;/li>&lt;/ul>&lt;h2 id="基础设施">基础设施&lt;/h2>&lt;ul>&lt;li>一台 Pioneer &lt;del>损坏&lt;/del> 查明是内核问题，已 &lt;del>寄修&lt;/del> 排查反馈问题。&lt;/li>&lt;li>一台 MUSEbox 损坏，已寄修。&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>deepin 现已加入 RuyiSDK 社区，并在 &lt;a href="https://ruyisdk.cn" rel="nofollow">RuyiSDK 社区论坛&lt;/a> 设立专区，后续将同步发布周报进展和新闻。&lt;/li>&lt;li>完成参展 RISC-V 峰会的设备清单和周边的准备，下周 RV 中国峰会见！&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports SIG 周报 (2025-07-04)</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20250704-weekly/</link><pubDate>Fri, 04 Jul 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20250704-weekly/</guid><description>&lt;h2 id="镜像更新">镜像更新&lt;/h2>&lt;ul>&lt;li>由于 &lt;code class="">deepin-screen-recorder-plugin&lt;/code> 在仓库中被移除，且新版 &lt;code class="">deepin-desktop-environment-extras&lt;/code> 尚未集成，本周暂不可打包新镜像。
&lt;ul>&lt;li>拟下周更新 TH1520 镜像。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="应用更新">应用更新&lt;/h2>&lt;ul>&lt;li>为 xorg 增加了部分 GLES 支持补丁。&lt;/li>&lt;li>为 &lt;code class="">jh7110&lt;/code> &lt;code class="">th1520&lt;/code> &lt;code class="">eic7700&lt;/code> &lt;code class="">k1&lt;/code> 的 &lt;code class="">ffmpeg&lt;/code> 针对主分支修改进行了 rebase。&lt;/li>&lt;li>协助厂商适配新版 electron 应用，推进 deepin 25 nodejs 更新 (18 -> 20)。
&lt;ul>&lt;li>拟下周验证各版本 electron 可用性。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="测试">测试&lt;/h2>&lt;ul>&lt;li>测试了以下镜像：
&lt;ul>&lt;li>&lt;code class="">eic7700&lt;/code>: deepin 25 Preview 20250627-155140 &lt;a href="https://deepin-community.github.io/docs/test/deepin-25-crimson-preview-riscv64-eic7700-20250627-155140.tar.xz">测试报告&lt;/a>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="文档">文档&lt;/h2>&lt;h3 id="安装指南">安装指南&lt;/h3>&lt;ul>&lt;li>新增了如下设备的安装指南：
&lt;ul>&lt;li>&lt;a href="https://deepin-community.github.io/docs/install/jh7110">StarFive JH7110&lt;/a> (SD 卡)&lt;/li>&lt;li>&lt;a href="https://deepin-community.github.io/docs/install/eic7700">ESWIN EIC7700&lt;/a> (SD 卡 / eMMC / SATA / NVMe)&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h3 id="网页">网页&lt;/h3>&lt;ul>&lt;li>为 deepin-ports SIG（本站）：
&lt;ul>&lt;li>优化了移动端显示&lt;/li>&lt;li>为 blog 和文档内容增加了 TOC&lt;/li>&lt;li>为 blog 和文档增加了 RSS 支持，文件均为 &lt;code class="">atom1&lt;/code> 格式：
&lt;ul>&lt;li>现可通过 &lt;code class="">feed.blog.xml&lt;/code> 订阅「博客」内容。&lt;/li>&lt;li>现可通过 &lt;code class="">feed.installdocs.xml&lt;/code> 订阅「安装文档」内容。&lt;/li>&lt;li>现可通过 &lt;code class="">feed.testdocs.xml&lt;/code> 订阅「测试报告」内容。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="基础设施">基础设施&lt;/h2>&lt;ul>&lt;li>为社区其中一台 SG2042 安装了 6.14 内核并重新接入构建集群。&lt;/li>&lt;li>为第三方打包方便，&lt;a href="https://github.com/YukariChiba/buildpackage-deepin/" rel="nofollow">新建了 deepin 打包工作流仓库&lt;/a>（现施工中）&lt;/li>&lt;/ul>&lt;h2 id="活动">活动&lt;/h2>&lt;ul>&lt;li>继续筹备参展 RISC-V 峰会的海报和材料。&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin-ports RISC-V 测试流程与模板 v0.1</title><link>https://deepin-community.github.io/sig-deepin-ports/blog/20250627-test-template-0.1/</link><pubDate>Fri, 27 Jun 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-ports/blog/20250627-test-template-0.1/</guid><description>&lt;blockquote>&lt;p>本文档旨在为 deepin 移植到 RISC-V 架构提供一套标准化、可复现的测试流程。该文档将随测试工作逐渐完善。&lt;/p>&lt;/blockquote>&lt;h2 id="_0-注意事项">0. 注意事项&lt;/h2>&lt;h3 id="_01-测试条件">0.1 测试条件&lt;/h3>&lt;p>根据 RFC2119 制定以下规则，用于确定测试是否继续进行：&lt;/p>&lt;h4 id="必须-must">&lt;strong>必须 (MUST)&lt;/strong>:&lt;/h4>&lt;p>任何情形下出现与陈述相悖的行为或结果，都将导致测试流程终止。&lt;/p>&lt;h4 id="禁止不得-must-not">&lt;strong>禁止/不得 (MUST NOT)&lt;/strong>:&lt;/h4>&lt;p>任何情形下出现与陈述相符的行为或结果，都将导致测试流程终止。&lt;/p>&lt;h4 id="应该-should">&lt;strong>应该 (SHOULD)&lt;/strong>:&lt;/h4>&lt;p>当出现与陈述相悖的行为或结果时：如果文本仅有一条陈述，可选择忽略该结果并继续测试流程；如果文本存在多条陈述，仅大部分陈述内容均与当前结果相符时可忽略该结果并继续测试流程；未达到忽略条件的结果出现将导致测试流程终止；同一小节的测试不得忽略结果超过 3 次。&lt;/p>&lt;h4 id="不应该-should-not">&lt;strong>不应该 (SHOULD NOT)&lt;/strong>:&lt;/h4>&lt;p>当出现与陈述相符的行为或结果时：如果文本仅有一条陈述，可选择忽略该结果并继续测试流程；如果文本存在多条陈述，仅大部分陈述内容均与当前结果相悖时可忽略该结果并继续测试流程；未达到忽略条件的结果出现将导致测试流程终止；同一小节的测试不得忽略结果超过 3 次。&lt;/p>&lt;h4 id="可以可能-may">&lt;strong>可以/可能 (MAY)&lt;/strong>:&lt;/h4>&lt;p>当出现与陈述相符或相悖的行为或结果时，均可继续进行测试流程，相应行为或结果应被记录。&lt;/p>&lt;h3 id="_02-支持等级划分">0.2 支持等级划分&lt;/h3>&lt;p>根据能够进入的最高章节的测试流程，将设备支持等级划分为 1-10 级，等级格式为 &lt;code class="">X(最高章节).Y(最高小节)&lt;/code>：&lt;/p>&lt;h4 id="_0-不支持">0. 不支持&lt;/h4>&lt;ul>&lt;li>硬件不满足基本要求，或在可预见的未来无法被支持。&lt;/li>&lt;li>设备将从设备支持列表中除名，如果存在镜像，镜像将被删除。&lt;/li>&lt;/ul>&lt;h4 id="_1-仅可安装无法启动">1. 仅可安装，无法启动&lt;/h4>&lt;ul>&lt;li>完成了系统镜像的写入，但设备无法从该镜像成功引导至 bootloader 之后阶段。说明镜像的 bootloader 或内核存在严重问题。&lt;/li>&lt;li>镜像将被标记为不可用并隐藏&lt;/li>&lt;/ul>&lt;h4 id="_2-基本命令行支持">2. 基本命令行支持&lt;/h4>&lt;ul>&lt;li>系统可成功启动至命令行 Shell，核心系统（内核、init、systemd）工作正常，但图形功能完全缺失或无法启动。&lt;/li>&lt;li>镜像将被标记为不可用并隐藏&lt;/li>&lt;/ul>&lt;h4 id="_3-基本图形化支持">3. 基本图形化支持&lt;/h4>&lt;ul>&lt;li>图形化登录界面（Greeter）可以启动，并可登录桌面，但完全依赖 CPU 进行软件渲染（如 llvmpipe）。无硬件图形加速。&lt;/li>&lt;li>镜像将被标记为不可用并隐藏&lt;/li>&lt;/ul>&lt;h4 id="_4-dde-桌面">4. DDE 桌面&lt;/h4>&lt;ul>&lt;li>DDE 桌面环境核心组件均可正常工作，提供稳定的基本桌面体验，但图形加速不可用，仅能以软件渲染模式运行。&lt;/li>&lt;/ul>&lt;h4 id="_5-图形化支持">5. 图形化支持&lt;/h4>&lt;ul>&lt;li>具备 2D/3D 硬件加速能力，提供完整的 DDE 桌面特效支持。&lt;/li>&lt;li>镜像达到 Preview 发布标准&lt;/li>&lt;/ul>&lt;h4 id="_6-完整的-deepin-环境">6. 完整的 deepin 环境&lt;/h4>&lt;ul>&lt;li>所有 deepin 内置应用程序均可正常启动和使用，系统功能完整。&lt;/li>&lt;li>镜像达到 Preview 发布标准&lt;/li>&lt;/ul>&lt;h4 id="_7-开箱即用的-deepin-环境">7. 开箱即用的 deepin 环境&lt;/h4>&lt;ul>&lt;li>核心外设（如有线网络、USB、音频）无需手动配置即可自动驱动并正常工作。&lt;/li>&lt;li>镜像达到 Preview 发布标准&lt;/li>&lt;/ul>&lt;h4 id="_8-完整支持">8. 完整支持&lt;/h4>&lt;ul>&lt;li>所有板载硬件功能（如 Wi-Fi、蓝牙、VPU、NPU）均得到驱动支持和功能验证。&lt;/li>&lt;li>镜像达到发布标准&lt;/li>&lt;/ul>&lt;h4 id="_9-具备兼容性的完整支持">9. 具备兼容性的完整支持&lt;/h4>&lt;ul>&lt;li>系统具备良好的软件兼容性。&lt;/li>&lt;li>镜像达到发布标准&lt;/li>&lt;/ul>&lt;h4 id="_10-risc-v崛起">10. RISC-V，崛起&lt;/h4>&lt;ul>&lt;li>系统高度完善，性能优化良好，达到或接近 amd64/arm64 水准。&lt;/li>&lt;/ul>&lt;h2 id="_1-硬件设置篇">1. 硬件设置篇&lt;/h2>&lt;h3 id="_11-硬件基本状况">1.1 硬件基本状况&lt;/h3>&lt;h4 id="操作">操作&lt;/h4>&lt;ul>&lt;li>通过任何渠道获得该设备，如果无法获得设备，请立即向 mentor 报告。&lt;/li>&lt;li>尝试接入电源并开启设备。&lt;/li>&lt;/ul>&lt;h4 id="条件">条件&lt;/h4>&lt;ul>&lt;li>设备 &lt;strong>必须 (MUST)&lt;/strong> 存在并可被测试者实际接触和操作。&lt;/li>&lt;li>设备 &lt;strong>必须 (MUST)&lt;/strong> 能够通过某种方式开机，且 &lt;strong>不得 (MUST NOT)&lt;/strong> 发生起火、爆炸等使设备不可用的状况。&lt;/li>&lt;/ul>&lt;h3 id="_12-硬件基本信息">1.2 硬件基本信息&lt;/h3>&lt;h4 id="操作-1">操作&lt;/h4>&lt;ul>&lt;li>查阅设备的基本信息，包括：
&lt;ul>&lt;li>厂商名称&lt;/li>&lt;li>SoC（核心型号、核心数、支持的指令集架构）&lt;/li>&lt;li>内存（类型，安装方式）&lt;/li>&lt;li>存储（类型，安装方式）&lt;/li>&lt;li>板载功能单元如 GPU、VPU、NPU 等&lt;/li>&lt;li>I/O 接口（USB 端口、HDMI/DP、音频、网络接口）&lt;/li>&lt;li>板型（ATX、ITX、开发板类型）&lt;/li>&lt;li>调试接口（如 UART）位置和参数（波特率等）&lt;/li>&lt;li>......&lt;/li>&lt;/ul>&lt;/li>&lt;li>查阅设备的厂商，获取安装文档。&lt;/li>&lt;/ul>&lt;h4 id="条件-1">条件&lt;/h4>&lt;ul>&lt;li>设备 &lt;strong>必须 (MUST)&lt;/strong> 存在任何可访问的说明文档。&lt;/li>&lt;li>设备 &lt;strong>必须 (MUST)&lt;/strong> 支持至少 rv64gc 指令集拓展。
&lt;ul>&lt;li>设备 &lt;strong>可以 (MAY)&lt;/strong> 支持 v 拓展等其它拓展指令集。&lt;/li>&lt;/ul>&lt;/li>&lt;li>设备 &lt;strong>必须 (MUST)&lt;/strong> 存在存储器或存储器插槽， &lt;strong>应该 (SHOULD)&lt;/strong> 支持至少 16G 存储空间。&lt;/li>&lt;li>设备 &lt;strong>应该 (SHOULD)&lt;/strong> 具备至少 2GB 内存。为获得良好桌面体验，&lt;strong>可以 (MAY)&lt;/strong> 具备 4GB 或以上内存。&lt;/li>&lt;li>设备 &lt;strong>必须 (MUST)&lt;/strong> 存在可重复使用的写入系统镜像的方法。&lt;/li>&lt;li>设备 &lt;strong>必须 (MUST)&lt;/strong> 具备启动 linux 系统的能力。&lt;/li>&lt;li>设备 &lt;strong>应该 (SHOULD)&lt;/strong> 提供或能够连接一个串口控制台用于调试。&lt;/li>&lt;li>设备 &lt;strong>应该 (SHOULD)&lt;/strong> 通过某种方式连接到互联网。&lt;/li>&lt;li>设备 &lt;strong>应该 (SHOULD)&lt;/strong> 具有厂商提供的安装文档。&lt;/li>&lt;li>设备 &lt;strong>不得 (MUST NOT)&lt;/strong> 存在无法关闭或兼容的安全启动/启动锁。&lt;/li>&lt;/ul>&lt;h2 id="_2-系统写入与安装篇">2. 系统写入与安装篇&lt;/h2>&lt;h3 id="_21-镜像获取与校验">2.1 镜像获取与校验&lt;/h3>&lt;h4 id="操作-2">操作&lt;/h4>&lt;ul>&lt;li>从 deepin-ports 镜像发布页或指定地址下载系统镜像文件和对应的校验文件。&lt;/li>&lt;li>校验下载的镜像文件。&lt;/li>&lt;/ul>&lt;h4 id="条件-2">条件&lt;/h4>&lt;ul>&lt;li>镜像文件 &lt;strong>必须 (MUST)&lt;/strong> 成功下载。&lt;/li>&lt;li>镜像文件的校验和 &lt;strong>必须 (MUST)&lt;/strong> 与校验文件中的记录一致。如果出现不一致的状况，请立即向 mentor/镜像创建者报告。&lt;/li>&lt;/ul>&lt;h3 id="_22-固件与-bootloader-设置">2.2 固件与 Bootloader 设置&lt;/h3>&lt;h4 id="操作-3">操作&lt;/h4>&lt;ul>&lt;li>根据厂商文档，判断设备是否需要预先刷写特定版本的固件或 Bootloader（如 U-Boot）。&lt;/li>&lt;li>如果设备存在需要刷写的固件
&lt;ul>&lt;li>根据厂商文档，检查并刷写推荐版本的固件。&lt;/li>&lt;/ul>&lt;/li>&lt;li>如果设备不需要额外刷写固件
&lt;ul>&lt;li>跳过本小节&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h4 id="条件-3">条件&lt;/h4>&lt;ul>&lt;li>如果设备存在需要刷写的固件
&lt;ul>&lt;li>镜像 &lt;strong>必须 (MUST)&lt;/strong> 无报错地刷写 bootloader。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h3 id="_23-写入系统镜像">2.3 写入系统镜像&lt;/h3>&lt;h4 id="操作-4">操作&lt;/h4>&lt;ul>&lt;li>根据设备文档，准备启动介质并写入镜像。&lt;/li>&lt;/ul>&lt;h4 id="条件-4">条件&lt;/h4>&lt;ul>&lt;li>镜像 &lt;strong>必须 (MUST)&lt;/strong> 无报错地写入存储设备。&lt;/li>&lt;/ul>&lt;h2 id="_3-基础命令行篇">3. 基础命令行篇&lt;/h2>&lt;h3 id="_31-bootloader-启动">3.1 bootloader 启动&lt;/h3>&lt;h4 id="操作-5">操作&lt;/h4>&lt;ul>&lt;li>在串行控制台中执行引导命令（或等待自动引导）。&lt;/li>&lt;li>观察日志，确认 bootloader 启动并准备开始引导启动。&lt;/li>&lt;li>如果存在启动菜单，确认菜单存在对应的启动条目。&lt;/li>&lt;/ul>&lt;h4 id="条件-5">条件&lt;/h4>&lt;ul>&lt;li>串口控制台 &lt;strong>应该 (SHOULD)&lt;/strong> 显示 bootloader 的启动日志。&lt;/li>&lt;li>串口控制台 &lt;strong>不应该 (SHOULD NOT)&lt;/strong> 在 bootloader 阶段输出致命性的错误日志。&lt;/li>&lt;li>如果存在启动菜单
&lt;ul>&lt;li>启动菜单 &lt;strong>必须 (MUST)&lt;/strong> 包括至少一条合法的启动项，对应的选项 &lt;strong>应该 (SHOULD)&lt;/strong> 存在内核、initrd 等字段。&lt;/li>&lt;/ul>&lt;/li>&lt;li>如果设备需要设备树
&lt;ul>&lt;li>使用的设备树文件 &lt;strong>应该 (SHOULD)&lt;/strong> 与设备型号对应。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h3 id="_32-内核启动">3.2 内核启动&lt;/h3>&lt;h4 id="操作-6">操作&lt;/h4>&lt;ul>&lt;li>观察内核解压和启动信息（dmesg 输出）。&lt;/li>&lt;li>等待一定时间后，尝试切换键盘的 NumLock/CapsLock。&lt;/li>&lt;/ul>&lt;h4 id="条件-6">条件&lt;/h4>&lt;ul>&lt;li>串口控制台 &lt;strong>应该 (SHOULD)&lt;/strong> 显示 Linux 内核的启动日志。&lt;/li>&lt;li>内核 &lt;strong>不得 (MUST NOT)&lt;/strong> 在早期启动阶段出现 Kernel panic 或致命错误。&lt;/li>&lt;li>切换键盘 NumLock/CapsLock，对应指示灯 &lt;strong>应该 (SHOULD)&lt;/strong> 相应地变化。&lt;/li>&lt;li>initrd &lt;strong>必须 (MUST)&lt;/strong> 被内核正确加载。&lt;/li>&lt;/ul>&lt;h3 id="_33-initrd-启动">3.3 initrd 启动&lt;/h3>&lt;h4 id="操作-7">操作&lt;/h4>&lt;ul>&lt;li>观察启动日志，确认 initrd 启动，根文件系统 (rootfs) 被成功挂载。&lt;/li>&lt;/ul>&lt;h4 id="条件-7">条件&lt;/h4>&lt;ul>&lt;li>initrd &lt;strong>不得 (MUST NOT)&lt;/strong> 因报错进入 fallback 命令行。&lt;/li>&lt;li>根文件系统 &lt;strong>必须 (MUST)&lt;/strong> 被成功挂载。&lt;/li>&lt;li>plymouth 界面 &lt;strong>可能 (MAY)&lt;/strong> 显示。&lt;/li>&lt;/ul>&lt;h3 id="_34-systemd-启动">3.4 systemd 启动&lt;/h3>&lt;h4 id="操作-8">操作&lt;/h4>&lt;ul>&lt;li>观察启动日志，确认 systemd init 进程被成功拉起，并开始启动系统服务。&lt;/li>&lt;li>启动完成后，在终端执行 systemctl --failed 查看是否有失败的服务。&lt;/li>&lt;/ul>&lt;h4 id="条件-8">条件&lt;/h4>&lt;ul>&lt;li>systemd init 进程 &lt;strong>必须 (MUST)&lt;/strong> 被启动。&lt;/li>&lt;li>大部分关键服务 &lt;strong>应该 (SHOULD)&lt;/strong> 被成功启动。&lt;/li>&lt;li>启动服务过程中 &lt;strong>可能 (MAY)&lt;/strong> 出现非关键服务长时间等待或启动失败。&lt;/li>&lt;li>systemd &lt;strong>不应该 (SHOULD NOT)&lt;/strong> 因关键服务启动失败进入 fallback 命令行。&lt;/li>&lt;/ul>&lt;h3 id="_35-基本环境验证">3.5 基本环境验证&lt;/h3>&lt;h4 id="操作-9">操作&lt;/h4>&lt;ul>&lt;li>如果启动后直接进入图形化 greeter，可按 &lt;code class="">Ctrl+Alt+F2&lt;/code> 切换到 tty2。&lt;/li>&lt;li>进入命令行，使用默认密码登录系统。&lt;/li>&lt;li>执行以下命令：
&lt;ul>&lt;li>执行 &lt;code class="">ip link&lt;/code> 查看网络接口状况。&lt;/li>&lt;li>执行 &lt;code class="">uname -a&lt;/code> 查看内核版本。&lt;/li>&lt;li>&lt;code class="">df -h&lt;/code> / &lt;code class="">mount&lt;/code> 查看磁盘挂载情况。&lt;/li>&lt;li>如果存在 &lt;code class="">fastfetch&lt;/code> 命令，执行 &lt;code class="">fastfetch&lt;/code> 命令。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h4 id="条件-9">条件&lt;/h4>&lt;ul>&lt;li>&lt;code class="">Ctrl+Alt+F[1-12]&lt;/code> &lt;strong>必须 (MUST)&lt;/strong> 能给切换到对应 tty。&lt;/li>&lt;li>登录提示 &lt;strong>必须 (MUST)&lt;/strong> 弹出并能输入。&lt;/li>&lt;li>默认密码 &lt;strong>必须 (MUST)&lt;/strong> 能够登录并进入 shell 命令行。&lt;/li>&lt;li>&lt;strong>不应该 (SHOULD NOT)&lt;/strong> 提示密码错误或其它认证方面的错误。&lt;/li>&lt;li>以上命令 &lt;strong>应该 (SHOULD)&lt;/strong> 能够执行，并且输出的信息与预期相符。&lt;/li>&lt;/ul>&lt;h2 id="_4-基本图形功能篇">4. 基本图形功能篇&lt;/h2>&lt;h3 id="_41-显示服务启动">4.1 显示服务启动&lt;/h3>&lt;h4 id="操作-10">操作&lt;/h4>&lt;ul>&lt;li>如果需要连接显示器，检查对应的连接线是否接好。&lt;/li>&lt;li>观察显示器输出。&lt;/li>&lt;/ul>&lt;h4 id="条件-10">条件&lt;/h4>&lt;ul>&lt;li>显示器 &lt;strong>必须 (MUST)&lt;/strong> 显示 deepin 的图形化登录界面。&lt;/li>&lt;li>使用 &lt;code class="">Ctrl+Alt+F[2-6]&lt;/code> 应该 (SHOULD) 能切换至 tty 虚拟终端并显示登录提示，&lt;code class="">Ctrl+Alt+F1&lt;/code> 应该 (SHOULD) 能切回图形登录界面。&lt;/li>&lt;/ul>&lt;h3 id="_42-图形化登录">4.2 图形化登录&lt;/h3>&lt;h4 id="操作-11">操作&lt;/h4>&lt;ul>&lt;li>在登录界面输入正确的用户名和密码。&lt;/li>&lt;li>点击登录或按回车。&lt;/li>&lt;/ul>&lt;h4 id="条件-11">条件&lt;/h4>&lt;ul>&lt;li>系统 &lt;strong>必须 (MUST)&lt;/strong> 成功完成登录并尝试启动会话。&lt;/li>&lt;li>&lt;strong>不应该 (SHOULD NOT)&lt;/strong> 提示密码错误或其它认证方面的错误。&lt;/li>&lt;/ul>&lt;h3 id="_43-基本桌面会话">4.3 基本桌面会话&lt;/h3>&lt;h4 id="操作-12">操作&lt;/h4>&lt;ul>&lt;li>观察。&lt;/li>&lt;/ul>&lt;h4 id="条件-12">条件&lt;/h4>&lt;ul>&lt;li>系统 &lt;strong>必须 (MUST)&lt;/strong> 至少能渲染出 DDE 桌面环境的基本形态。（即使非常卡顿）。&lt;/li>&lt;li>桌面 &lt;strong>应该 (SHOULD)&lt;/strong> 至少显示出壁纸、任务栏（Dock）和桌面图标的轮廓或实际内容。&lt;/li>&lt;li>会话 &lt;strong>不应该 (SHOULD NOT)&lt;/strong> 在成功登录后数秒内崩溃并返回到登录界面。&lt;/li>&lt;/ul>&lt;h2 id="_5-dde-桌面体验篇">5. DDE 桌面体验篇&lt;/h2>&lt;h3 id="_51-核心交互">5.1 核心交互&lt;/h3>&lt;h4 id="操作-13">操作&lt;/h4>&lt;ul>&lt;li>观察桌面。&lt;/li>&lt;li>移动鼠标，查看光标是否跟随。&lt;/li>&lt;li>点击任务栏上的启动器图标。&lt;/li>&lt;li>右键点击桌面。&lt;/li>&lt;li>尝试在桌面上框选。&lt;/li>&lt;li>尝试使用 DDE 的快捷键。&lt;/li>&lt;/ul>&lt;h4 id="条件-13">条件&lt;/h4>&lt;ul>&lt;li>DDE &lt;strong>应该（SHOULD）&lt;/strong> 正常渲染：
&lt;ul>&lt;li>桌面壁纸与桌面图标&lt;/li>&lt;li>任务栏、任务栏托盘图标、托盘图标弹出菜单&lt;/li>&lt;/ul>&lt;/li>&lt;li>动画和窗口特效 &lt;strong>可以 (MAY)&lt;/strong> 正常显示。&lt;/li>&lt;li>鼠标光标 &lt;strong>应该 (SHOULD)&lt;/strong> 正常渲染并平滑移动，&lt;strong>不应该 (SHOULD NOT)&lt;/strong> 出现闪烁、撕裂或卡顿。&lt;/li>&lt;li>任务栏（Dock）、启动器（Launcher）、桌面右键菜单 &lt;strong>必须 (MUST)&lt;/strong> 能够响应点击操作并正常显示。&lt;/li>&lt;li>DDE各快捷键 &lt;strong>应当（SHOULD）&lt;/strong> 正常工作， &lt;strong>不应该 (SHOULD NOT)&lt;/strong> 在键盘按键与快捷键按下时产生冲突或卡顿。&lt;/li>&lt;/ul>&lt;h3 id="_52-窗口管理">5.2 窗口管理&lt;/h3>&lt;h4 id="操作-14">操作&lt;/h4>&lt;ul>&lt;li>从启动器打开“文件管理器”和“终端”。&lt;/li>&lt;li>尝试拖动、最大化、最小化、关闭窗口。&lt;/li>&lt;li>尝试使用 Alt+Tab 切换窗口。&lt;/li>&lt;/ul>&lt;h4 id="条件-14">条件&lt;/h4>&lt;ul>&lt;li>窗口 &lt;strong>必须 (MUST)&lt;/strong> 可以被拖动、缩放、最大化、最小化和关闭。&lt;/li>&lt;li>窗口切换功能 &lt;strong>应该 (SHOULD)&lt;/strong> 正常工作。&lt;/li>&lt;li>窗口操作 &lt;strong>不应该 (SHOULD NOT)&lt;/strong> 导致桌面环境（dde-shell）崩溃。&lt;/li>&lt;/ul>&lt;h3 id="_53-控制中心与系统托盘">5.3 控制中心与系统托盘&lt;/h3>&lt;h4 id="操作-15">操作&lt;/h4>&lt;ul>&lt;li>观察系统托盘，点击托盘右侧的插件区域，展开控制面板。
&lt;ul>&lt;li>尝试调节音量、设置网络。&lt;/li>&lt;/ul>&lt;/li>&lt;li>从启动器打开“控制中心”
&lt;ul>&lt;li>查看并调整网络配置&lt;/li>&lt;li>查看关于信息&lt;/li>&lt;li>查看并调整个性化设置&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h4 id="条件-15">条件&lt;/h4>&lt;ul>&lt;li>系统托盘和右侧插件区域 &lt;strong>必须 (MUST)&lt;/strong> 正常显示和操作。
&lt;ul>&lt;li>调节音量、网络等操作 &lt;strong>应该 (SHOULD)&lt;/strong> 成功。&lt;/li>&lt;/ul>&lt;/li>&lt;li>控制中心 &lt;strong>必须 (MUST)&lt;/strong> 能够正常打开和关闭。
&lt;ul>&lt;li>控制中心内的各个模块 &lt;strong>应该 (SHOULD)&lt;/strong> 能够加载并显示正确信息。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="_6-高级图形功能篇">6. 高级图形功能篇&lt;/h2>&lt;p>在本章节的任意时刻：&lt;/p>&lt;ul>&lt;li>如果设备已知无 OpenGL/OpenGLES/Vulkan 之中的&lt;strong>任一&lt;/strong>图形加速
&lt;ul>&lt;li>跳过本章节&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h3 id="_61-设备节点验证">6.1 设备节点验证&lt;/h3>&lt;h4 id="操作-16">操作&lt;/h4>&lt;ul>&lt;li>查看 &lt;code class="">/dev/dri&lt;/code> 下内容。&lt;/li>&lt;/ul>&lt;h4 id="条件-16">条件&lt;/h4>&lt;ul>&lt;li>设备 &lt;strong>必须 (MUST)&lt;/strong> 存在 &lt;code class="">/dev/dri&lt;/code> 目录，并存在至少一个渲染节点(&lt;code class="">render*&lt;/code>)和至少一个设备节点(&lt;code class="">card*&lt;/code>)。&lt;/li>&lt;/ul>&lt;h3 id="_62-gpu-驱动验证">6.2 GPU 驱动验证&lt;/h3>&lt;h4 id="操作-17">操作&lt;/h4>&lt;ul>&lt;li>执行 &lt;code class="">glxinfo&lt;/code> 和 &lt;code class="">glxgears&lt;/code>&lt;/li>&lt;li>执行 &lt;code class="">es2_info&lt;/code> 和 &lt;code class="">es2gears_x11&lt;/code>&lt;/li>&lt;li>执行 &lt;code class="">eglinfo&lt;/code> 和 &lt;code class="">eglgears_x11&lt;/code>&lt;/li>&lt;li>执行 &lt;code class="">vkgears -info&lt;/code>&lt;/li>&lt;/ul>&lt;h4 id="条件-17">条件&lt;/h4>&lt;ul>&lt;li>设备 &lt;strong>应当（SHOULD）&lt;/strong> 在 &lt;code class="">es2_info&lt;/code> 和 &lt;code class="">eglinfo&lt;/code> 中显示硬件和驱动，&lt;strong>不应该 (SHOULD NOT)&lt;/strong> 出现 &lt;code class="">llvmpipe&lt;/code> 或 &lt;code class="">softpipe&lt;/code> 字样。&lt;/li>&lt;li>设备 &lt;strong>可能 (MAY)&lt;/strong> 在 &lt;code class="">glxinfo&lt;/code> 和/或 &lt;code class="">vkgears -info&lt;/code> 中显示 &lt;code class="">llvmpipe&lt;/code> 或 &lt;code class="">softpipe&lt;/code> 字样。&lt;/li>&lt;li>设备 &lt;strong>可能 (MAY)&lt;/strong> 无法运行 &lt;code class="">eglgears_x11&lt;/code> 和/或 &lt;code class="">es2gears_x11&lt;/code>。&lt;/li>&lt;li>如果设备使用 AMD GPU:
&lt;ul>&lt;li>设备 &lt;strong>不应该（SHOULD NOT）&lt;/strong> 在 &lt;code class="">glxinfo&lt;/code> 或 &lt;code class="">vkgears -info&lt;/code> 中显示 &lt;code class="">llvmpipe&lt;/code> 或 &lt;code class="">softpipe&lt;/code> 字样。&lt;/li>&lt;li>设备 &lt;strong>应当 (SHOULD)&lt;/strong> 能够运行 &lt;code class="">eglgears_x11&lt;/code> 和/或 &lt;code class="">es2gears_x11&lt;/code>。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h3 id="_63-gpu-基准测试">6.3 GPU 基准测试&lt;/h3>&lt;h4 id="操作-18">操作&lt;/h4>&lt;ul>&lt;li>安装 &lt;code class="">glmark2&lt;/code> 和 &lt;code class="">glmark2-es2-x11&lt;/code>&lt;/li>&lt;li>执行 &lt;code class="">glmark2&lt;/code>，记录分数。&lt;/li>&lt;li>执行 &lt;code class="">glmark2-es2&lt;/code>，记录分数。&lt;/li>&lt;/ul>&lt;h4 id="条件-18">条件&lt;/h4>&lt;ul>&lt;li>设备 &lt;strong>应当（SHOULD）&lt;/strong> 能够运行上述程序并输出分数，&lt;strong>不应该 (SHOULD NOT)&lt;/strong> 报错退出。&lt;/li>&lt;li>设备 &lt;strong>应当（SHOULD）&lt;/strong> 在 &lt;code class="">glmark2-es2&lt;/code> 中显示硬件和驱动，&lt;strong>不应该 (SHOULD NOT)&lt;/strong> 出现 &lt;code class="">llvmpipe&lt;/code> 或 &lt;code class="">softpipe&lt;/code> 字样。&lt;/li>&lt;li>如果设备使用 AMD GPU:
&lt;ul>&lt;li>设备 &lt;strong>不应该（SHOULD NOT）&lt;/strong> 在 &lt;code class="">glmark2&lt;/code> 中显示 &lt;code class="">llvmpipe&lt;/code> 或 &lt;code class="">softpipe&lt;/code> 字样。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h3 id="_63-显示管理">6.3 显示管理&lt;/h3>&lt;h4 id="操作-19">操作&lt;/h4>&lt;ul>&lt;li>打开“控制中心”，进入显示设置，尝试调整分辨率（若显示器支持）和刷新率（若显示器和显卡支持）。&lt;/li>&lt;li>如果设备存在其它视频输出接口：
&lt;ul>&lt;li>接入第二台显示器，观察输出状况。&lt;/li>&lt;li>尝试切换多屏显示模式（复制/拓展）。&lt;/li>&lt;li>打开“控制中心”，进入显示设置，尝试调整第二台显示器的分辨率（若显示器支持）和刷新率（若显示器和显卡支持）。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h4 id="条件-19">条件&lt;/h4>&lt;ul>&lt;li>如果硬件支持，通过控制中心 &lt;strong>应当（SHOULD）&lt;/strong> 可以正常调整所有显示输出的分辨率和刷新率。&lt;/li>&lt;li>如果设备存在其它视频输出接口：
&lt;ul>&lt;li>两台显示器 &lt;strong>应当（SHOULD）&lt;/strong> 支持同时输出。&lt;/li>&lt;li>复制/拓展 显示模式 &lt;strong>应当（SHOULD）&lt;/strong> 能够正常输出和切换。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h2 id="_7-deepin-内置应用篇">7. deepin 内置应用篇&lt;/h2>&lt;h3 id="_71-核心工具">7.1 核心工具&lt;/h3>&lt;h4 id="操作-20">操作&lt;/h4>&lt;ul>&lt;li>在启动器中打开“终端”、“文本编辑器”、“计算器”、“看图”、“画板”。
&lt;ul>&lt;li>在文本编辑器中输入文字并尝试保存。&lt;/li>&lt;li>使用看图打开一张 PNG 和一张 JPG 图片。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h4 id="条件-20">条件&lt;/h4>&lt;ul>&lt;li>以上应用 &lt;strong>必须 (MUST)&lt;/strong> 能够成功启动并显示主界面。&lt;/li>&lt;li>应用的核心功能（如输入、保存、打开文件）&lt;strong>必须 (MUST)&lt;/strong> 正常工作。&lt;/li>&lt;/ul>&lt;h3 id="_72-系统管理">7.2 系统管理&lt;/h3>&lt;h4 id="操作-21">操作&lt;/h4>&lt;ul>&lt;li>打开“系统监视器”、“日志收集工具”、“设备管理器”。&lt;/li>&lt;li>在系统监视器中查看进程、资源占用情况。&lt;/li>&lt;li>在设备管理器中查看硬件列表。&lt;/li>&lt;/ul>&lt;h4 id="条件-21">条件&lt;/h4>&lt;ul>&lt;li>以上应用 &lt;strong>必须 (MUST)&lt;/strong> 能够成功启动。&lt;/li>&lt;li>应用 &lt;strong>应该 (SHOULD)&lt;/strong> 正确读取并显示一些系统信息
&lt;ul>&lt;li>设备管理器 &lt;strong>可能 (MAY)&lt;/strong> 无法显示设备的一部分信息。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h3 id="_73-影音娱乐">7.3 影音娱乐&lt;/h3>&lt;h4 id="操作-22">操作&lt;/h4>&lt;ul>&lt;li>打开“音乐”，尝试播放音乐文件夹的示例音乐。&lt;/li>&lt;li>打开“影院”，尝试播放视频文件夹的示例视频。&lt;/li>&lt;/ul>&lt;h4 id="条件-22">条件&lt;/h4>&lt;ul>&lt;li>“音乐”和“影院” &lt;strong>必须 (MUST)&lt;/strong> 能够启动。&lt;/li>&lt;li>音频和视频 &lt;strong>应该 (SHOULD)&lt;/strong> 能够正常播放（允许卡顿，但声音和画面需要基本同步）。&lt;/li>&lt;/ul>&lt;h2 id="_8-硬件加速篇">8. 硬件加速篇&lt;/h2>&lt;h3 id="_81-视频硬件加速解码">8.1 视频硬件加速解码&lt;/h3>&lt;h4 id="操作-23">操作&lt;/h4>&lt;ul>&lt;li>准备若干常见码率和格式视频，如
&lt;ul>&lt;li>1080p60_6000Kbps_h264&lt;/li>&lt;li>1440p60_10000Kbps_hevc&lt;/li>&lt;li>2160p30_20000Kbps_av1&lt;/li>&lt;li>......&lt;/li>&lt;/ul>&lt;/li>&lt;li>安装 &lt;code class="">mpv&lt;/code>，使用 &lt;code class="">mpv&lt;/code> 播放视频。&lt;/li>&lt;li>使用系统自带视频播放器 &lt;code class="">deepin-movie&lt;/code> 播放视频。&lt;/li>&lt;li>使用浏览器访问视频网站播放视频。&lt;/li>&lt;/ul>&lt;h4 id="条件-23">条件&lt;/h4>&lt;ul>&lt;li>硬件视频解码器支持的视频格式 &lt;strong>必须（MUST）&lt;/strong> 成功调用硬件解码器&lt;/li>&lt;li>解码结果 &lt;strong>必须（MUST）&lt;/strong> 软件解码结果相同&lt;/li>&lt;li>解码结果 &lt;strong>禁止（MUST NOT）&lt;/strong> 出现颜色错误/亮度错误等问题&lt;/li>&lt;li>CPU使用率 &lt;strong>应当（SHOULD）&lt;/strong> 在合理范围内&lt;/li>&lt;li>设备可以 &lt;strong>可以（MAY）&lt;/strong> 不掉帧的播放视频&lt;/li>&lt;/ul>&lt;h3 id="_82-视频硬件加速编码">8.2 视频硬件加速编码&lt;/h3>&lt;h4 id="操作-24">操作&lt;/h4>&lt;ul>&lt;li>使用 ffmpeg 命令尝试将一个视频文件转码为硬件支持的格式&lt;/li>&lt;/ul>&lt;h4 id="条件-24">条件&lt;/h4>&lt;ul>&lt;li>硬件视频编码器支持的视频格式 &lt;strong>必须（MUST）&lt;/strong> 成功调用硬件编码器&lt;/li>&lt;li>编码结果 &lt;strong>必须（MUST）&lt;/strong> 可以正常解码&lt;/li>&lt;li>CPU使用率 &lt;strong>应当（SHOULD）&lt;/strong> 在合理范围内&lt;/li>&lt;/ul>&lt;h2 id="_9-第一方仓库软件篇">9. 第一方仓库软件篇&lt;/h2>&lt;h3 id="_91-包管理">9.1 包管理&lt;/h3>&lt;h4 id="操作-25">操作&lt;/h4>&lt;ul>&lt;li>执行 &lt;code class="">sudo apt update&lt;/code> 更新软件仓库。&lt;/li>&lt;li>执行 &lt;code class="">sudo apt install&lt;/code> 安装任意软件包&lt;/li>&lt;li>执行 &lt;code class="">sudo apt remove&lt;/code> 删除上一步安装的软件包&lt;/li>&lt;li>执行 &lt;code class="">sudo apt upgrade&lt;/code> 升级软件包&lt;/li>&lt;/ul>&lt;h4 id="条件-25">条件&lt;/h4>&lt;ul>&lt;li>&lt;code class="">apt update&lt;/code> &lt;strong>必须 (MUST)&lt;/strong> 成功完成，无 GPG 错误或无法连接仓库的错误。&lt;/li>&lt;li>软件包的安装和卸载 &lt;strong>必须 (MUST)&lt;/strong> 正常工作。&lt;/li>&lt;li>&lt;code class="">apt upgrade&lt;/code> &lt;strong>应该 (SHOULD)&lt;/strong> 能够正常执行，完成系统更新。&lt;/li>&lt;/ul>&lt;h3 id="_92-办公应用">9.2 办公应用&lt;/h3>&lt;h4 id="操作-26">操作&lt;/h4>&lt;ul>&lt;li>在启动器中打开 LibreOffice Writer。
&lt;ul>&lt;li>输入一段文字并保存。&lt;/li>&lt;/ul>&lt;/li>&lt;li>在启动器中打开 LibreOffice Calc。
&lt;ul>&lt;li>创建一个表格并保存。&lt;/li>&lt;/ul>&lt;/li>&lt;li>在启动器中打开 LibreOffice Impress。
&lt;ul>&lt;li>创建一个演示文档并保存。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h4 id="条件-26">条件&lt;/h4>&lt;ul>&lt;li>LibreOffice &lt;strong>必须 (MUST)&lt;/strong> 能够启动并正常使用其核心功能。&lt;/li>&lt;li>&lt;strong>不应该 (SHOULD NOT)&lt;/strong> 出现因缺少依赖或架构不兼容导致的启动失败。&lt;/li>&lt;/ul>&lt;h3 id="_93-基本构建环境">9.3 基本构建环境&lt;/h3>&lt;h4 id="操作-27">操作&lt;/h4>&lt;ul>&lt;li>执行 &lt;code class="">sudo apt install build-essential&lt;/code>。&lt;/li>&lt;li>创建一个 &lt;code class="">hello.c&lt;/code> 文件，内容为标准的 "Hello, World!" 程序。&lt;/li>&lt;li>执行 &lt;code class="">gcc hello.c -o hello&lt;/code> 进行编译。&lt;/li>&lt;li>执行 &lt;code class="">./hello&lt;/code> 运行程序。&lt;/li>&lt;/ul>&lt;h4 id="条件-27">条件&lt;/h4>&lt;ul>&lt;li>&lt;code class="">build-essential&lt;/code> 包 &lt;strong>必须 (MUST)&lt;/strong> 能够被成功安装。&lt;/li>&lt;li>C 语言 "Hello, World!" 程序 &lt;strong>必须 (MUST)&lt;/strong> 能够被成功编译和运行。&lt;/li>&lt;/ul>&lt;h2 id="_10-外设驱动支持篇">10. 外设驱动支持篇&lt;/h2>&lt;h3 id="_101-核心外设">10.1 核心外设&lt;/h3>&lt;h4 id="操作-28">操作&lt;/h4>&lt;ul>&lt;li>如果设备存在 USB 接口：
&lt;ul>&lt;li>将 USB 键盘和鼠标连接到不同的 USB 2.0/3.0 接口上。&lt;/li>&lt;li>将 U 盘（FAT32/EXT4/NTFS 格式）连接到 USB 接口。&lt;/li>&lt;li>在终端中执行 &lt;code class="">lsusb&lt;/code> 命令。&lt;/li>&lt;/ul>&lt;/li>&lt;li>如果设备存在 RJ45 网络接口：
&lt;ul>&lt;li>在终端中执行 &lt;code class="">ip link&lt;/code> 命令。&lt;/li>&lt;li>连接有线网络。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h4 id="条件-28">条件&lt;/h4>&lt;ul>&lt;li>如果设备存在 USB 接口：
&lt;ul>&lt;li>USB 键盘和鼠标 &lt;strong>必须 (MUST)&lt;/strong> 被即时识别并正常工作。&lt;/li>&lt;li>U 盘 &lt;strong>必须 (MUST)&lt;/strong> 被正确识别并自动挂载，文件管理器中 &lt;strong>应该 (SHOULD)&lt;/strong> 出现对应盘符并可访问。&lt;/li>&lt;li>&lt;code class="">lsusb&lt;/code> 命令 &lt;strong>必须 (MUST)&lt;/strong> 正确显示对应设备。&lt;/li>&lt;/ul>&lt;/li>&lt;li>如果设备存在 RJ45 网络接口：
&lt;ul>&lt;li>&lt;code class="">ip link&lt;/code> 命令 &lt;strong>必须 (MUST)&lt;/strong> 正确显示对应设备。&lt;/li>&lt;li>有线网络 &lt;strong>必须 (MUST)&lt;/strong> 自动连接并获取 IP 地址（DHCP），能够访问互联网。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h3 id="_102-音频输出">10.2 音频输出&lt;/h3>&lt;h4 id="操作-29">操作&lt;/h4>&lt;ul>&lt;li>如果有 3.5mm 音频接口，插入/拔出耳机。&lt;/li>&lt;li>如果存在任何音频输出设备：
&lt;ul>&lt;li>通过音乐播放器或浏览器播放音频或视频，确认音频输出是否正常。&lt;/li>&lt;/ul>&lt;/li>&lt;li>如果有麦克风接口或板载麦克风：
&lt;ul>&lt;li>使用录音测试输入。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h4 id="条件-29">条件&lt;/h4>&lt;ul>&lt;li>各个设备的音频输出/输入 &lt;strong>应该 (SHOULD)&lt;/strong> 正常工作。&lt;/li>&lt;/ul>&lt;h3 id="_103-板载无线功能">10.3 板载无线功能&lt;/h3>&lt;h4 id="操作-30">操作&lt;/h4>&lt;ul>&lt;li>如果有板载 Wi-Fi:
&lt;ul>&lt;li>在终端中执行 &lt;code class="">ip link&lt;/code> 命令。&lt;/li>&lt;li>在控制中心搜索并连接到一个 WPA2/WPA3 加密的无线网络。&lt;/li>&lt;/ul>&lt;/li>&lt;li>如果有板载蓝牙:
&lt;ul>&lt;li>在控制中心尝试搜索并配对一个蓝牙设备（如蓝牙鼠标、耳机）。&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;h4 id="条件-30">条件&lt;/h4>&lt;ul>&lt;li>如果有对应设备，Wi-Fi &lt;strong>应该 (SHOULD)&lt;/strong> 能够扫描到网络并成功连接。&lt;/li>&lt;li>如果有对应设备，蓝牙 &lt;strong>应该 (SHOULD)&lt;/strong> 能够开启、扫描并成功配对设备。&lt;/li>&lt;/ul>&lt;h3 id="_1099-章节施工">10.99 章节施工&lt;/h3>&lt;p>此章节尚未完成，测试在此中止。&lt;/p>&lt;h2 id="_11-第三方软件篇">11. 第三方软件篇&lt;/h2>&lt;p>此章节施工中，测试在此中止。&lt;/p>&lt;h2 id="结论">结论&lt;/h2>&lt;h3 id="测试日期">测试日期:&lt;/h3>&lt;h3 id="测试设备型号">测试设备型号:&lt;/h3>&lt;h3 id="测试镜像版本">测试镜像版本:&lt;/h3>&lt;h3 id="支持等级">支持等级：&lt;/h3>&lt;blockquote>&lt;p>X.Y&lt;/p>&lt;/blockquote>&lt;h3 id="摘要总结">摘要总结:&lt;/h3>&lt;blockquote>&lt;p>(简要描述测试过程中的关键发现、主要障碍和总体评价。)&lt;/p>&lt;/blockquote>&lt;h3 id="测试流程">测试流程&lt;/h3>&lt;blockquote>&lt;p>(测试报告无须写出所有步骤，若某小节所有条件均满足，则简单说明操作即可。）&lt;/p>&lt;/blockquote>&lt;h3 id="未通过的最高条件">未通过的最高条件:&lt;/h3>&lt;blockquote>&lt;p>(详细记录导致测试终止的最后一个条件，包括章节号、条件描述和实际观测现象。)&lt;/p>&lt;/blockquote>&lt;h3 id="异常记录-mayshould">异常记录 (MAY/SHOULD):&lt;/h3>&lt;blockquote>&lt;p>(列出所有在测试中记录的 MAY 和被忽略的 SHOULD/SHOULD NOT 异常。)&lt;/p>&lt;/blockquote>&lt;h2 id="鸣谢">鸣谢&lt;/h2>&lt;blockquote>&lt;p>在此处留下自己的 ID 表示自己参与了编辑，当该文档发布时可作为可见产出。&lt;/p>&lt;/blockquote>&lt;ul>&lt;li>@YukariChiba&lt;/li>&lt;li>@Alphagocc&lt;/li>&lt;li>@apr3vau&lt;/li>&lt;/ul></description><author>deepin-ports SIG</author></item><item><title>deepin 25 正式版 DDE 移植简要指南</title><link>https://deepin-community.github.io/sig-dde-porting/posts/deepin-25-stable-dde-porting-guide/?utm_source=atom_feed</link><pubDate>Wed, 25 Jun 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/deepin-25-stable-dde-porting-guide/</guid><description>&lt;p>&lt;em>编辑的话请把自己的名字加到作者名单里&lt;/em>&lt;/p>
&lt;p>即将发布（你阅读到这个文章的时候可能已经发布了）的 deepin 25 正式版将会包含对应的新版 DDE。为了方便各个其它发行版的包维护者可以更方便的移植 DDE 到对应的发行版，这里提供一篇简要的移植指南，用以描述常见的移植问题和解决方案。&lt;/p>
&lt;blockquote>
&lt;p>下面对项目名称的称呼均以 GitHub 对应的原始仓库名为准。&lt;/p>&lt;/blockquote>
&lt;h2 id="概览">概览&lt;/h2>
&lt;p>相对于 deepin 25 beta，在 deepin 25 正式版中并不存在较大幅的架构调整，而是以缺陷修复以及完善之前尚未完善但计划涵盖在最终版本的组件（例如 QML 版控制中心）作为研发的重心。本阶段中有部分组件的首位版本号存在调整，另存在一些注意事项，详情请参见后续的描述。&lt;/p>
&lt;p>由于这些项目的版本间互相影响，我们建议移植人员参照 deepin 25 正式版所使用的包版本进行打包，下面会对主要的部分进行详细说明。&lt;/p>
&lt;p>需要注意的是，由于此文章编写时间早于版本发布时间，故最终版本镜像中使用的版本可能高于下面列出的版本。我们尽可能确保此文章的准确性，但若您需要获取 ISO 镜像中使用的确切软件版本列表，请挂载 ISO 后参阅 &lt;code>LIVE/FILESYS&amp;#123;T,0,1}.MAN&lt;/code>/&lt;code>live/filesystem.manifest&lt;/code> 路径对应的文件的内容。&lt;/p>
&lt;p>另外，由于前述 manifest 文件包含了 ISO 所附带的所有软件包的版本信息，而 DDE 移植并不需要关注整个列表，你可以参考 &lt;a href="https://github.com/deepin-community/deepin-desktop-environment/">deepin-community/deepin-desktop-environment&lt;/a> 提供的几个 Meta 包中描述的依赖关系，来了解哪些软件包和 DDE 有关。&lt;/p>
&lt;h2 id="主要组件">主要组件&lt;/h2>
&lt;h3 id="dtk-与-dtk6">DTK 与 DTK6&lt;/h3>
&lt;p>DTK 是 DDE 组件与应用的基础依赖，适用于deepin 25 正式版的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>dtkcommon&lt;/td>
&lt;td>5.7.17&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtklog&lt;/td>
&lt;td>0.0.4&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkcore&lt;/td>
&lt;td>5.7.17&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkgui&lt;/td>
&lt;td>5.7.17&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkwidget&lt;/td>
&lt;td>5.7.17&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkdeclarative&lt;/td>
&lt;td>5.7.17&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5integration&lt;/td>
&lt;td>5.7.17&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5platform-plugins&lt;/td>
&lt;td>5.7.17.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6core&lt;/td>
&lt;td>6.0.37&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6gui&lt;/td>
&lt;td>6.0.37&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6widget&lt;/td>
&lt;td>6.0.37&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6declarative&lt;/td>
&lt;td>6.0.37&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6integration&lt;/td>
&lt;td>6.0.37&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6platform-plugins&lt;/td>
&lt;td>6.0.37&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>本次 DTK 组件大部分版本号以及相对应的平台插件等版本号均已对齐，例外的有 dtkcommon 与 dtklog。可参照上表进行打包。&lt;/p>
&lt;p>关于 qt5platform-plugins，现有的 dwayland 插件可能对非 DDE 环境（例如 KDE）的 wayland 用户存在影响，可参照 &lt;a href="https://github.com/linuxdeepin/developer-center/issues/7217">linuxdeepin/developer-center#7217&lt;/a> 打对应的 patch 规避影响。&lt;/p>
&lt;h3 id="dde-主要组件">DDE 主要组件&lt;/h3>
&lt;p>下面仅涉及变化较大或影响较广的组件。其余未涉及的组件可正常参照最新 tag 进行打包与移植。&lt;/p>
&lt;p>在 alpha 与 beta 阶段使用 1.99.z 系列版本号的项目现均启用了 2.0.z 版本号，以表示相关的行为变动。针对 dde-shell 所提供库的依赖的 SOVERSION 仍为 1，不受打包版本号的影响。&lt;/p>
&lt;p>下面涉及到的组件的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>deepin-desktop-schemas&lt;/td>
&lt;td>6.0.11&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-daemon&lt;/td>
&lt;td>6.1.40&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-session&lt;/td>
&lt;td>2.0.2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-session-ui&lt;/td>
&lt;td>6.0.30&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-session-shell&lt;/td>
&lt;td>6.0.41&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-manager&lt;/td>
&lt;td>1.2.31&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-shell&lt;/td>
&lt;td>2.0.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launchpad&lt;/td>
&lt;td>2.0.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-tray-loader&lt;/td>
&lt;td>2.0.2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-wizard&lt;/td>
&lt;td>0.1.15&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-clipboard&lt;/td>
&lt;td>6.1.9&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>deepin-service-manager&lt;/td>
&lt;td>1.0.13&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launcher&lt;/td>
&lt;td>被 dde-launchpad 取代，不再使用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-dock&lt;/td>
&lt;td>被 dde-shell 取代，不再使用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>startdde&lt;/td>
&lt;td>已被废弃，不再使用&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="dde-application-manager">dde-application-manager&lt;/h4>
&lt;p>由于涉及到诸多关于应用识别的改善，故建议总是使用最新版本。&lt;/p>
&lt;h4 id="dde-session-shell">dde-session-shell&lt;/h4>
&lt;p>相比 beta ，此项目不涉及较大的结构调整，但你可能希望阅读 beta 时的变动描述，涉及到了仓库位置和提交历史相关的说明。&lt;/p>
&lt;h4 id="dde-shell">dde-shell&lt;/h4>
&lt;p>dde-shell 旨在将 DDE 桌面环境插件化与模块化，降低开发难度，使各个组件的替换变得更加容易，并且提供更好的桌面环境集成支持。正式阶段相比 beta 阶段集中在缺陷的修复上，并未涵盖太多的结构调整和新特性。对于 beta 以及更早版本的变化，请阅读之前的博客文章。&lt;/p>
&lt;p>为保障 dde-shell 在 Qt 6.8.0 或 6.8.1 的环境可以正常运行（即使是X11环境下），若 ，则 &lt;strong>必须&lt;/strong> 给 qtwayland 打下面的 patch：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://codereview.qt-project.org/c/qt/qtwayland/+/603556">https://codereview.qt-project.org/c/qt/qtwayland/+/603556&lt;/a> （&lt;a href="https://github.com/qt/qtwayland/commit/070414dd4155e13583e5e8b16bed1a5b68d32910">此 patch 已合入 Qt 上游&lt;/a>，涵盖在 &lt;code>Qt Wayland &amp;gt;= 6.8.2&lt;/code> 版本中）&lt;/li>
&lt;/ul>
&lt;p>dde-shell 在 alpha 中为修正一个特定问题所包含的一个变更依赖另一个 Qt Wayland 的 patch：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/deepin-community/qt6-wayland/pull/12">https://github.com/deepin-community/qt6-wayland/pull/12&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>若你所移植的目标发行版不接受此补丁，则可考虑对 dde-shell 项目 revert 于此相关的对应 commit：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/dde-shell/commit/77cac3d5a346728f6678eedc77fce404f0ffd8b6">https://github.com/linuxdeepin/dde-shell/commit/77cac3d5a346728f6678eedc77fce404f0ffd8b6&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="dde-launchpad">dde-launchpad&lt;/h4>
&lt;p>dde-launchpad 现仅支持以 dde-shell 插件的形式被最终用户使用。因而，打包 dde-launchpad 现需要先打包 dde-shell，并确保用户最终使用的是 dde-shell。&lt;/p>
&lt;h4 id="dde-session">dde-session&lt;/h4>
&lt;p>我们已在 deepin 23 beta3 起放弃了对 deepin-kwin wayland 的支持，DDE 后续所有 wayland 相关的支持均由 treeland 提供。请参见后续的 Treeland 段落。&lt;/p>
&lt;p>下面涉及到的组件的版本参照如下。对于位于非 linuxdeepin 组织的软件包，此处一并给出了组织名：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>vioken/waylib&lt;/td>
&lt;td>0.6.13&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>vioken/qwlroots&lt;/td>
&lt;td>0.5.3&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>treeland&lt;/td>
&lt;td>0.5.20&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ddm&lt;/td>
&lt;td>0.1.10&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="deepin-kwin-环境">deepin-kwin 环境&lt;/h2>
&lt;p>此部分相对 beta 也未存在较大变动，但由于此组件的重要性，方便起见，此处重新阐述 beta 阶段的变化：&lt;/p>
&lt;p>deepin-kwin 对 Qt 的版本依赖切换到了 Qt 6，不过值得注意的是，当前的 deepin-kwin 并非从上游 kwin 6.x 中 fork 出来的，而是基于 uos 20 版本的 kwin 5.27.x 进行的 qt6/kf6 迁移，并且由于一些原因，其二进制可执行的文件名恢复到了 kwin，这会导致与上游原版 kwin 的冲突。&lt;/p>
&lt;p>就于此事项的详细介绍，请参见 &lt;a href="https://github.com/orgs/linuxdeepin/discussions/11471">https://github.com/orgs/linuxdeepin/discussions/11471&lt;/a> 讨论。对于移植人员，我们建议考虑下面三种方案：&lt;/p>
&lt;ul>
&lt;li>使用 Qt 6 的 deepin-kwin，放弃与上游 kwin 的共存支持。&lt;/li>
&lt;li>继续使用 deepin-kwin 5.27，尽管存在一些问题，但此版本仍然可保证和上游版本 kwin 共存。&lt;/li>
&lt;li>考虑打包 Treeland 环境。&lt;/li>
&lt;/ul>
&lt;h2 id="treeland-环境">Treeland 环境&lt;/h2>
&lt;p>Treeland 环境在 alpha -&amp;gt; beta 阶段无较大变化，alpha 阶段的移植注意事项仍然适用于 beta。&lt;/p>
&lt;h3 id="ddm">DDM&lt;/h3>
&lt;p>尽管 DDM 目前是基本功能可用状态，DDM 目前仍相对而言不够稳定。对于打包移植而言，建议采用其他DM来启动用户级的treeland。&lt;/p>
&lt;p>对于其它 DM，只需要打包时安装 &lt;code>usr/share/wayland-sessions/treeland-user.desktop&lt;/code> 即可。&lt;/p>
&lt;h3 id="qt-补丁">Qt 补丁&lt;/h3>
&lt;p>下述假定您的发行版使用的 Qt 版本为 Qt 6.8.2。&lt;/p>
&lt;p>如果你在 Treeland 下遇到小 launchpad 无法输入中文的问题，可以打下面的 patch，但是该 patch 目前尚未进行完整测试，可能存在一些问题。&lt;/p>
&lt;p>&lt;a href="https://codereview.qt-project.org/c/qt/qtbase/+/611940">https://codereview.qt-project.org/c/qt/qtbase/+/611940&lt;/a>&lt;/p>
&lt;p>另外，如果你的发行版所附的 Qt 6.8 版本并未更新至 Qt 6.8.2，则可能需要打三个额外的补丁，可参见 &lt;a href="https://deepin-community.github.io/sig-dde-porting/posts/dde-qt6.8-porting-guide/">DDE Qt 6.8 适配说明（针对 Qt 6.8.0）&lt;/a> 以及 &lt;a href="https://deepin-community.github.io/sig-dde-porting/posts/deepin-25-preview-dde-porting-guide/#qt-%E8%A1%A5%E4%B8%81">deepin 25 preview DDE 移植简要指南（针对 Qt 6.8.1）&lt;/a>。&lt;/p>
&lt;h4 id="qt-69-说明">Qt 6.9 说明&lt;/h4>
&lt;p>DDE 尚未完整针对 Qt 6.9 进行测试，但存在一些已知问题，下面列出了对应的问题和处理方式：&lt;/p>
&lt;ol>
&lt;li>QML 的 DelegateChoice/DelegateChooser 组件不存在问题&lt;/li>
&lt;/ol>
&lt;p>Qt 6.9 中，此组件被从 labs “转正”到了 &lt;code>QtQml.Models&lt;/code> 下，相应的 import 需要修正。已知 dde-shell 存在此问题（&lt;a href="https://github.com/linuxdeepin/dde-shell/pull/1173">修正相关问题的 patch&lt;/a>）。&lt;/p>
&lt;p>若有其他项目存在相同问题，则对于此问题的修正，可参考 dde-shell 的相关提交：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/dde-shell/pull/1173">https://github.com/linuxdeepin/dde-shell/pull/1173&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="建议忽略移植的组件">建议忽略移植的组件&lt;/h2>
&lt;h3 id="deepin-anything">deepin-anything&lt;/h3>
&lt;p>尽管被部分 DDE 组件依赖，但均为可选。anything 提供了内核模块，对于滚动发行版移植可能不友好，可能移植并不能得到很高的体验改善。&lt;/p>
&lt;p>若忽略 deepin-anything 移植，则下列组件也应当被忽略（因为依赖了 deepin-anything）：&lt;/p>
&lt;ul>
&lt;li>dde-grand-search&lt;/li>
&lt;/ul>
&lt;h3 id="dde-application-wizard">dde-application-wizard&lt;/h3>
&lt;p>尽管此项目初衷之一是提供可移植的模块化卸载服务，但并未达到理想状态。尽管事实上可被顺利移植，此项目可以考虑忽略。缺失此组件并不会影响 DDE 主要功能。&lt;/p>
&lt;h2 id="获取移植帮助">获取移植帮助&lt;/h2>
&lt;p>如果您希望得到移植相关的帮助，请考虑加入我们 DDE 移植小组的在线交流群（下列房间有桥接，任选其一即可），一起展开相关的交流：&lt;/p>
&lt;ul>
&lt;li>Matrix 群：&lt;code>#dde-port:deepin.org&lt;/code>&lt;/li>
&lt;li>Telegram 群：&lt;a href="https://t.me/ddeport">https://t.me/ddeport&lt;/a>&lt;/li>
&lt;/ul></description><author>DDE 移植兴趣小组</author></item><item><title>deepin 25 beta DDE 移植简要指南</title><link>https://deepin-community.github.io/sig-dde-porting/posts/deepin-25-beta-dde-porting-guide/?utm_source=atom_feed</link><pubDate>Tue, 27 May 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/deepin-25-beta-dde-porting-guide/</guid><description>&lt;p>&lt;em>编辑的话请把自己的名字加到作者名单里&lt;/em>&lt;/p>
&lt;p>已经发布的 deepin 25 beta 包含了对应的新版 DDE。为了方便各个其它发行版的包维护者可以更方便的移植 DDE 到对应的发行版，这里提供一篇简要的移植指南，用以描述常见的移植问题和解决方案。&lt;/p>
&lt;blockquote>
&lt;p>下面对项目名称的称呼均以 GitHub 对应的原始仓库名为准。&lt;/p>&lt;/blockquote>
&lt;h2 id="概览">概览&lt;/h2>
&lt;p>相对于 deepin 25 alpha，在 deepin 25 beta 中并不存在较大幅的架构调整，而是以缺陷修复以及完善之前尚未完善但计划涵盖在最终版本的组件（例如 QML 版控制中心）作为研发的重心。需要注意的是，部分 DDE 组件在本版本中已被废弃，另外本次发布过程中也存在一个组件的 git 仓库有变动的情况，详情请参见后续的描述。&lt;/p>
&lt;p>由于这些项目的版本间互相影响，我们建议移植人员参照 deepin 25 beta 所使用的包版本进行打包，下面会对主要的部分进行详细说明。&lt;/p>
&lt;p>需要注意的是，由于此文章编写时间早于版本发布时间，故最终版本镜像中使用的版本可能高于下面列出的版本。我们尽可能确保此文章的准确性，但若您需要获取 ISO 镜像中使用的确切软件版本列表，请挂载 ISO 后参阅 &lt;code>LIVE/FILESYS&amp;#123;T,0,1}.MAN&lt;/code>/&lt;code>live/filesystem.manifest&lt;/code> 路径对应的文件的内容。&lt;/p>
&lt;h2 id="主要组件">主要组件&lt;/h2>
&lt;h3 id="dtk-与-dtk6">DTK 与 DTK6&lt;/h3>
&lt;p>DTK 是 DDE 组件与应用的基础依赖，适用于 RC 的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>dtkcommon&lt;/td>
&lt;td>5.7.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtklog&lt;/td>
&lt;td>0.0.2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkcore&lt;/td>
&lt;td>5.7.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkgui&lt;/td>
&lt;td>5.7.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkwidget&lt;/td>
&lt;td>5.7.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkdeclarative&lt;/td>
&lt;td>5.7.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5integration&lt;/td>
&lt;td>5.7.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5platform-plugins&lt;/td>
&lt;td>5.7.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6core&lt;/td>
&lt;td>6.0.36&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6gui&lt;/td>
&lt;td>6.0.36&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6widget&lt;/td>
&lt;td>6.0.35&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6declarative&lt;/td>
&lt;td>6.0.36.4&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6integration&lt;/td>
&lt;td>6.0.35&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6platform-plugins&lt;/td>
&lt;td>6.0.35&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>本次 DTK 组件大部分版本号以及相对应的平台插件等版本号均已对齐，例外的有 dtklog、dtk6widget、dtk6declarative、qt6integration、qt6platform-plugins。可参照上表进行打包。&lt;/p>
&lt;p>关于 qt5platform-plugins，现有的 dwayland 插件可能对非 DDE 环境（例如 KDE）的 wayland 用户存在影响，可参照 &lt;a href="https://github.com/linuxdeepin/developer-center/issues/7217">linuxdeepin/developer-center#7217&lt;/a> 打对应的 patch 规避影响。&lt;/p>
&lt;h3 id="dde-主要组件">DDE 主要组件&lt;/h3>
&lt;p>下面仅涉及变化较大或影响较广的组件。其余未涉及的组件可正常参照最新 tag 进行打包与移植。&lt;/p>
&lt;p>由于 deepin 25 preview 仍在持续开发过程中，故较多组件采取了 x.99.z 的版本号策略。此外，一般情况下，此类 tag 并不会实际以 git tag 的形式存在，而只会体现在 &lt;code>debian/changlog&lt;/code> 文件中。下面涉及到的此类版本号将会在版本发布前后补充对应的 git tag。&lt;/p>
&lt;p>下面涉及到的组件的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>deepin-desktop-schemas&lt;/td>
&lt;td>6.0.10&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-daemon&lt;/td>
&lt;td>6.1.32&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-session&lt;/td>
&lt;td>1.99.17&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-manager&lt;/td>
&lt;td>1.2.28&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-shell&lt;/td>
&lt;td>1.99.36&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launchpad&lt;/td>
&lt;td>1.99.15&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-tray-loader&lt;/td>
&lt;td>1.99.26&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-wizard&lt;/td>
&lt;td>0.1.14&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-clipboard&lt;/td>
&lt;td>6.1.8&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launcher&lt;/td>
&lt;td>被 dde-launchpad 取代，不再使用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-dock&lt;/td>
&lt;td>被 dde-shell 取代，不再使用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>startdde&lt;/td>
&lt;td>已被废弃，不再使用&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="dde-application-manager">dde-application-manager&lt;/h4>
&lt;p>由于涉及到诸多关于应用识别的改善，故建议总是使用最新版本。&lt;/p>
&lt;h4 id="dde-session-shell">dde-session-shell&lt;/h4>
&lt;p>此项目未涉及到结构性的巨大调整，但存在仓库历史与 tag 历史变化的事项。由于项目维护需要，之前的 dde-session-shell 仓库被重命名为了 dde-session-shell-snipe，并在此后新建了名为 dde-session-shell 的仓库，这导致你无法在 dde-session-shell 仓库下找到历史（例如alpha之前）的 tag 信息。若有移植需求，可移步 dde-session-shell-snipe 仓库。&lt;/p>
&lt;p>对于 beta 以及未来版本的移植，仍然保持使用 dde-session-shell 仓库即可。&lt;/p>
&lt;h4 id="dde-shell">dde-shell&lt;/h4>
&lt;p>dde-shell 旨在将 DDE 桌面环境插件化与模块化，降低开发难度，使各个组件的替换变得更加容易，并且提供更好的桌面环境集成支持。alpha 阶段相比 preview 阶段集中在缺陷的修复上，并未涵盖太多的结构调整和新特性。对于 alpha 以及更早版本的变化，请阅读之前的博客文章。&lt;/p>
&lt;p>为保障 dde-shell 在 Qt 6.8.0 或 6.8.1 的环境可以正常运行（即使是X11环境下），若 ，则 &lt;strong>必须&lt;/strong> 给 qtwayland 打下面的 patch：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://codereview.qt-project.org/c/qt/qtwayland/+/603556">https://codereview.qt-project.org/c/qt/qtwayland/+/603556&lt;/a> （&lt;a href="https://github.com/qt/qtwayland/commit/070414dd4155e13583e5e8b16bed1a5b68d32910">此 patch 已合入 Qt 上游&lt;/a>，涵盖在 &lt;code>Qt Wayland &amp;gt;= 6.8.2&lt;/code> 版本中）&lt;/li>
&lt;/ul>
&lt;p>dde-shell 在 alpha 中为修正一个特定问题所包含的一个变更依赖另一个 Qt Wayland 的 patch：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/deepin-community/qt6-wayland/pull/12">https://github.com/deepin-community/qt6-wayland/pull/12&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>若你所移植的目标发行版不接受此补丁，则可考虑对 dde-shell 项目 revert 于此相关的对应 commit：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/dde-shell/commit/77cac3d5a346728f6678eedc77fce404f0ffd8b6">https://github.com/linuxdeepin/dde-shell/commit/77cac3d5a346728f6678eedc77fce404f0ffd8b6&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="dde-launchpad">dde-launchpad&lt;/h4>
&lt;p>dde-launchpad 现仅支持以 dde-shell 插件的形式被最终用户使用。因而，打包 dde-launchpad 现需要先打包 dde-shell，并确保用户最终使用的是 dde-shell。&lt;/p>
&lt;h4 id="dde-session">dde-session&lt;/h4>
&lt;p>我们已在 deepin 23 beta3 起放弃了对 deepin-kwin wayland 的支持，DDE 后续所有 wayland 相关的支持均由 treeland 提供。请参见后续的 Treeland 段落。&lt;/p>
&lt;p>下面涉及到的组件的版本参照如下。对于位于非 linuxdeepin 组织的软件包，此处一并给出了组织名：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>vioken/waylib&lt;/td>
&lt;td>0.6.13&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>vioken/qwlroots&lt;/td>
&lt;td>0.5.3&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>treeland&lt;/td>
&lt;td>0.5.20&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ddm&lt;/td>
&lt;td>0.1.10&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="deepin-kwin-环境">deepin-kwin 环境&lt;/h2>
&lt;p>deepin-kwin 对 Qt 的版本依赖切换到了 Qt 6，不过值得注意的是，当前的 deepin-kwin 并非从上游 kwin 6.x 中 fork 出来的，而是基于 uos 20 版本的 kwin 5.27.x 进行的 qt6/kf6 迁移，并且由于一些原因，其二进制可执行的文件名恢复到了 kwin，这会导致与上游原版 kwin 的冲突。&lt;/p>
&lt;p>就于此事项的详细介绍，请参见 &lt;a href="https://github.com/orgs/linuxdeepin/discussions/11471">https://github.com/orgs/linuxdeepin/discussions/11471&lt;/a> 讨论。对于移植人员，我们建议考虑下面两种方案：&lt;/p>
&lt;ul>
&lt;li>继续使用 deepin-kwin 5.27，尽管存在一些问题，但此版本仍然可保证和上游版本 kwin 共存。&lt;/li>
&lt;li>考虑打包 Treeland 环境。&lt;/li>
&lt;/ul>
&lt;h2 id="treeland-环境">Treeland 环境&lt;/h2>
&lt;p>Treeland 环境在 alpha -&amp;gt; beta 阶段无较大变化，alpha 阶段的移植注意事项仍然适用于 beta。&lt;/p>
&lt;h3 id="ddm">DDM&lt;/h3>
&lt;p>尽管 DDM 目前是基本功能可用状态，DDM 目前仍相对而言不够稳定。对于打包移植而言，建议采用其他DM来启动用户级的treeland。&lt;/p>
&lt;p>对于其它 DM，只需要打包时安装 &lt;code>usr/share/wayland-sessions/treeland-user.desktop&lt;/code> 即可。&lt;/p>
&lt;h3 id="qt-补丁">Qt 补丁&lt;/h3>
&lt;p>下述假定您的发行版使用的 Qt 版本为 Qt 6.8.2。&lt;/p>
&lt;p>如果你在 Treeland 下遇到小 launchpad 无法输入中文的问题，可以打下面的 patch，但是该 patch 目前尚未进行完整测试，可能存在一些问题。&lt;/p>
&lt;p>&lt;a href="https://codereview.qt-project.org/c/qt/qtbase/+/611940">https://codereview.qt-project.org/c/qt/qtbase/+/611940&lt;/a>&lt;/p>
&lt;p>另外，如果你的发行版所附的 Qt 6.8 版本并未更新至 Qt 6.8.2，则可能需要打三个额外的补丁，可参见 &lt;a href="https://deepin-community.github.io/sig-dde-porting/posts/dde-qt6.8-porting-guide/">DDE Qt 6.8 适配说明（针对 Qt 6.8.0）&lt;/a> 以及 &lt;a href="https://deepin-community.github.io/sig-dde-porting/posts/deepin-25-preview-dde-porting-guide/#qt-%E8%A1%A5%E4%B8%81">deepin 25 preview DDE 移植简要指南（针对 Qt 6.8.1）&lt;/a>。&lt;/p>
&lt;h4 id="qt-69-说明">Qt 6.9 说明&lt;/h4>
&lt;p>DDE 尚未完整针对 Qt 6.9 进行测试，但存在一些已知问题，下面列出了对应的问题和处理方式：&lt;/p>
&lt;ol>
&lt;li>QML 的 DelegateChoice/DelegateChooser 组件不存在问题&lt;/li>
&lt;/ol>
&lt;p>Qt 6.9 中，此组件被从 labs “转正”到了 QtQml.Models 下，相应的 import 需要修正。已知 dde-control-center 与 dde-shell 存在此问题，其中 dde-control-center 已进行了修正：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/dde-control-center/pull/2304">https://github.com/linuxdeepin/dde-control-center/pull/2304&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="获取移植帮助">获取移植帮助&lt;/h2>
&lt;p>如果您希望得到移植相关的帮助，请考虑加入我们 DDE 移植小组的在线交流群（下列房间有桥接，任选其一即可），一起展开相关的交流：&lt;/p>
&lt;ul>
&lt;li>Matrix 群：&lt;code>#dde-port:deepin.org&lt;/code>&lt;/li>
&lt;li>Telegram 群：&lt;a href="https://t.me/ddeport">https://t.me/ddeport&lt;/a>&lt;/li>
&lt;/ul></description><author>DDE 移植兴趣小组</author></item><item><title>VSCode DevContainer 不完全食用</title><link>https://blog.mkacg.com/2025/05/14/VSCode-DevContainer%E4%B8%8D%E5%AE%8C%E5%85%A8%E9%A3%9F%E7%94%A8/</link><pubDate>Wed, 14 May 2025 01:28:09 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2025/05/14/VSCode-DevContainer%E4%B8%8D%E5%AE%8C%E5%85%A8%E9%A3%9F%E7%94%A8/</guid><description>&lt;h2 id="起因">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#起因" title="起因">&lt;/a>起因&lt;/h2>&lt;p>之前已经写过一篇使用 distrobox 加速开发的文章，主要动机就是在面对不同版本的系统时，准备多份开发环境将变得十分痛苦，最终就演变成了基于 VSCode 的 Remote 开发搭配容器环境。&lt;/p>&lt;p>这样只需要准备一份 base 环境，每次只需要设置环境版本就可以安装对应的开发依赖，并且不需要更换开发工具，甚至可以在环境变量中设置好 DISPLAY 等，还能直接让容器内的程序显示在宿主环境上。&lt;/p>&lt;p>本篇文章是基于 podman 的技术方案执行的。&lt;/p>&lt;h2 id="环境准备">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#环境准备" title="环境准备">&lt;/a>环境准备&lt;/h2>&lt;h3 id="安装-podman">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#安装-podman" title="安装 podman">&lt;/a>安装 podman&lt;/h3>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo pacman -Sy podman&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h3 id="确认配置">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#确认配置" title="确认配置">&lt;/a>确认配置&lt;/h3>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">cat /etc/subuid&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>内容应该是 &lt;code>lxz:100000:65536&lt;/code>。&lt;/p>&lt;h3 id="VSCode">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#VSCode" title="VSCode">&lt;/a>VSCode&lt;/h3>&lt;p>打开 VScode 的设置，搜索 container，将 docker 的二进制改成 podman。&lt;/p>&lt;h2 id="devcontainer-json">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#devcontainer-json" title=".devcontainer.json">&lt;/a>.devcontainer.json&lt;/h2>&lt;p>VSCode 会识别打开目录下的 &lt;code>.devcontainer&lt;/code> 目录和 &lt;code>.devcontainer.json&lt;/code> 文件，本文会先介绍 json 格式的配置，之后会介绍使用 Dockerfile 构建运行环境的方式。&lt;/p>&lt;figure class="highlight json">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;name&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;UOS Dev Container&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;containerUser&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;root&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;image&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;uos:base&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;customizations&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;vscode&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;extensions&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">[&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;llvm-vs-code-extensions.vscode-clangd&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;donjayamanne.git-extension-pack&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;donjayamanne.githistory&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;theqtcompany.qt-cpp-pack&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;eamodio.gitlens&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;ms-vscode.cmake-tools&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;go2sh.cmake-integration-vscode&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;josetr.cmake-language-support-vscode&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;VisualStudioExptTeam.vscodeintellicode&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;VisualStudioExptTeam.intellicode-api-usage-examples&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;esbenp.prettier-vscode&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;ms-vscode.cpptools&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;twxs.cmake&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">]&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">&amp;#125;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;runArgs&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">[&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;--rm&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;--network=host&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;--privileged&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;--userns=host&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">]&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;remoteEnv&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">&amp;#125;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;onCreateCommand&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;apt update &amp;amp;&amp;amp; apt install -y clangd-13 ninja-build curl jq &amp;amp;&amp;amp; update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-13 13 &amp;amp;&amp;amp; echo &amp;gt; /etc/apt/sources.list&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;postCreateCommand&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;echo \&amp;quot;请输入系统版本：\&amp;quot; &amp;amp;&amp;amp; read version &amp;amp;&amp;amp; sh -c \&amp;quot;curl -s &amp;#x27;https://xxx/api/shuttle/repo/info?type=deb&amp;amp;reponame=eagle-$version&amp;#x27; | jq -r &amp;#x27;.data.repobase.archives[].address&amp;#x27; | tee /etc/apt/sources.list\&amp;quot; &amp;amp;&amp;amp; echo &amp;#x27;deb [trusted=yes] https://xxx/pkg/eagle-1073/release-candidate/MDQyNeeql-euoeS4u-e6v-aPkOa1izIwMjUtMDQtMjUgMTQ6NTk6MDE/ unstable main&amp;#x27; &amp;gt;&amp;gt; /etc/apt/sources.list &amp;amp;&amp;amp; apt update &amp;amp;&amp;amp; apt build-dep -y . &amp;amp;&amp;amp; apt install -y qt5-default qtwayland5-private-dev&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="punctuation">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>在 json 中，需要指定一个运行时环境，在这里使用的是本地手动准备的镜像，为了确保开发和服务器构建环境的一致，我直接使用了服务器提供的构建环境，并使用 &lt;code>podman image import&lt;/code> 命令导入。&lt;/p>&lt;p>runArgs 中 &lt;code>--userns=host&lt;/code> 的作用是使用主机的 user namespace，而 containerUser 必须指定成 root，因为容器内没有别的用户。&lt;/p>&lt;p>为了防止泄露内网信息，&lt;code>postCreateCommand&lt;/code> 内已将域名屏蔽。&lt;/p>&lt;p>在 UOS 中，clangd 的版本有点低，需要手动创建符号，否则 VSCode 的 clangd 插件无法正常工作。这个事情就放在 &lt;code>onCreateCommand&lt;/code> 中执行了，&lt;code>postCreateCommand&lt;/code> 则是配置仓库和安装项目的构建依赖。&lt;/p>&lt;p>当打开项目后，VSCode 会提醒在容器内重新打开。&lt;/p>&lt;img alt="容器内打开" src="https://blog.mkacg.com/rss/%E6%88%AA%E5%B1%8F2025-05-14%2009.42.40.png" />&lt;img alt="容器内打开" src="https://blog.mkacg.com/rss/%E6%88%AA%E5%B1%8F2025-05-14%2009.42.52.png" />&lt;img alt="容器内打开" src="https://blog.mkacg.com/rss/%E6%88%AA%E5%B1%8F2025-05-14%2009.43.05.png" />&lt;p>之后等待 VSCode 完成 server 的下载，就会开始执行 &lt;code>onCreateCommand&lt;/code> 和 &lt;code>postCreateCommand&lt;/code>。&lt;/p>&lt;p>启动完毕后就会开始安装插件，是一个正常的工作环境了。&lt;/p>&lt;img alt="容器内打开" src="https://blog.mkacg.com/rss/%E6%88%AA%E5%B1%8F2025-05-14%2009.58.06.png" />&lt;img alt="容器内打开" src="https://blog.mkacg.com/rss/%E6%88%AA%E5%B1%8F2025-05-14%2009.58.29.png" />&lt;p>打开终端，可以看到是 root 用户，但 podman 做了 uid 映射，实际目录的权限会是用户自身，不必担心。&lt;/p>&lt;img alt="容器内打开" src="https://blog.mkacg.com/rss/%E6%88%AA%E5%B1%8F2025-05-14%2010.02.33.png" />&lt;img alt="容器内打开" src="https://blog.mkacg.com/rss/%E6%88%AA%E5%B1%8F2025-05-14%2010.03.02.png" />&lt;h2 id="Dockerfile">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Dockerfile" title="Dockerfile">&lt;/a>Dockerfile&lt;/h2>&lt;p>通常情况下应当使用 image，这样能确保所有人的环境是一致的，但仍然有时候想在 Dockerfile 里提前准备好一些事情，而不是在容器启动后进行配置，只需要把 image 字段改成 build。&lt;/p>&lt;figure class="highlight json">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="attr">&amp;quot;build&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">&amp;#123;&lt;/span> &lt;span class="attr">&amp;quot;dockerfile&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;Dockerfile&amp;quot;&lt;/span> &lt;span class="punctuation">&amp;#125;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>这种情况更推荐放在 &lt;code>.devcontainer&lt;/code> 目录。在这个目录下，可以放置 Dockerfile 和其他配置文件，以便在构建容器时使用。&lt;/p>&lt;figure class="highlight dockerfile">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="keyword">FROM&lt;/span> --platform=$TARGETPLATFORM hub.deepin.com/kwin-devcontainer/linuxdeepin/eagle:base&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">ARG&lt;/span> USERNAME=&lt;span class="keyword">user&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">ARG&lt;/span> UID=&lt;span class="number">1000&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">ARG&lt;/span> GID=&lt;span class="number">1000&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">ARG&lt;/span> UOS_VERSION=&lt;span class="number">1073&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">RUN&lt;/span>&lt;span class="language-bash"> groupadd --gid &lt;span class="variable">$GID&lt;/span> &lt;span class="variable">$USERNAME&lt;/span> &amp;amp;&amp;amp; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> useradd --uid &lt;span class="variable">$UID&lt;/span> --gid &lt;span class="variable">$GID&lt;/span> -m &lt;span class="variable">$USERNAME&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">RUN&lt;/span>&lt;span class="language-bash"> apt-get update &amp;amp;&amp;amp; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> apt-get install -y \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> curl \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> jq \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> &amp;amp;&amp;amp; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> apt-get dist-upgrade -y &amp;amp;&amp;amp; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> apt-get clean &amp;amp;&amp;amp; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> &lt;span class="built_in">rm&lt;/span> -rf /var/lib/apt/lists/*&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">RUN&lt;/span>&lt;span class="language-bash"> apt-get update &amp;amp;&amp;amp; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> apt-get install -y \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> &lt;span class="built_in">sudo&lt;/span> \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> clangd-13 \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> apt-utils \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> apt-transport-https \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> ca-certificates \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> curl \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> gnupg \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> lsb-release \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> &amp;amp;&amp;amp; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-13 13 &amp;amp;&amp;amp; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> &lt;span class="built_in">echo&lt;/span> &lt;span class="string">&amp;quot;&lt;span class="variable">$USERNAME&lt;/span> ALL=(ALL) NOPASSWD:ALL&amp;quot;&lt;/span> &amp;gt;&amp;gt; /etc/sudoers &amp;amp;&amp;amp; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> apt-get dist-upgrade -y &amp;amp;&amp;amp; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> apt-get clean &amp;amp;&amp;amp; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> &lt;span class="built_in">rm&lt;/span> -rf /var/lib/apt/lists/*&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">USER&lt;/span> $USERNAME&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>json 也稍微修改了一下，使用 Dockerfile，并且使用参数控制一些构建过程，并且增加了本地用户。&lt;/p>&lt;figure class="highlight json">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;name&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;UOS Dev Container&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;remoteUser&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;lxz&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;build&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;dockerfile&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;Dockerfile&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;args&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;UOS_VERSION&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;1073&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;USERNAME&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;lxz&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">&amp;#125;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;customizations&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;vscode&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;extensions&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">[&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;llvm-vs-code-extensions.vscode-clangd&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;donjayamanne.git-extension-pack&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;donjayamanne.githistory&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;theqtcompany.qt-cpp-pack&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;eamodio.gitlens&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;ms-vscode.cmake-tools&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;go2sh.cmake-integration-vscode&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;josetr.cmake-language-support-vscode&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;VisualStudioExptTeam.vscodeintellicode&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;VisualStudioExptTeam.intellicode-api-usage-examples&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;esbenp.prettier-vscode&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;saoudrizwan.claude-dev&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">]&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">&amp;#125;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;runArgs&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">[&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;--rm&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;--network=host&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;--privileged&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;--env&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;XDG_RUNTIME_DIR=$&amp;#123;env:XDG_RUNTIME_DIR&amp;#125;&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;-v&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;$&amp;#123;env:SSH_AUTH_SOCK&amp;#125;:/tmp/ssh-agent.socket&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;-e&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;SSH_AUTH_SOCK=/tmp/ssh-agent.socket&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">]&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;remoteEnv&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;XDG_RUNTIME_DIR&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;$&amp;#123;env:XDG_RUNTIME_DIR&amp;#125;&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;PODMAN_USERNS&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;host&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">&amp;#125;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;postCreateCommand&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;sudo apt update &amp;amp;&amp;amp; sudo apt build-dep -y . &amp;amp;&amp;amp; sudo apt install -y qt5-default qtwayland5-private-dev&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="punctuation">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;blockquote>&lt;p>&lt;a href="https://code.visualstudio.com/docs/devcontainers/create-dev-container">https://code.visualstudio.com/docs/devcontainers/create-dev-container&lt;/a>&lt;/p>&lt;/blockquote></description><author>justforlxz</author></item><item><title>resolve Maximum number of clients reached</title><link>https://blog.mkacg.com/2025/04/22/resolve-Maximum-number-of-clients-reached/</link><pubDate>Tue, 22 Apr 2025 02:00:42 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2025/04/22/resolve-Maximum-number-of-clients-reached/</guid><description>&lt;p>最近遇到了两起 X11 下客户端报 &lt;code>Maximum number of clients reached&lt;/code> 的错误，并且无法连接到 Xorg。&lt;/p>&lt;p>搜到了一个排查方法，执行&lt;/p>&lt;figure class="highlight bash">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="built_in">sudo&lt;/span> ss -x src &lt;span class="string">&amp;quot;*/tmp/.X11-unix/*&amp;quot;&lt;/span> | grep -Eo &lt;span class="string">&amp;quot;[0-9]+\s*$&amp;quot;&lt;/span> | &lt;span class="keyword">while&lt;/span> &lt;span class="built_in">read&lt;/span> port&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">do&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">sudo&lt;/span> ss -p -x | grep -w &lt;span class="variable">$port&lt;/span> | grep -v X11-unix&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">done&lt;/span> | grep -Eo &lt;span class="string">&amp;#x27;&amp;quot;.+&amp;quot;&amp;#x27;&lt;/span> | &lt;span class="built_in">sort&lt;/span> | &lt;span class="built_in">uniq&lt;/span> -c | &lt;span class="built_in">sort&lt;/span> -rn&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>这将列出每个进程及其打开的 X 连接数，按连接数降序排列。&lt;/p>&lt;figure class="highlight bash">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">13 &lt;span class="string">&amp;quot;dde-session-dae&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> 5 &lt;span class="string">&amp;quot;fcitx&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> 3 &lt;span class="string">&amp;quot;kwin_x11&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> 3 &lt;span class="string">&amp;quot;kglobalaccel5&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> 2 &lt;span class="string">&amp;quot;cpis-panel-serv&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> 1 &lt;span class="string">&amp;quot;uos-activator&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> 1 &lt;span class="string">&amp;quot;startdde&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;blockquote>&lt;p>&lt;a href="https://snapoverflow.com/ubuntu/question/4499/how-can-i-diagnose-debug-maximum-number-of-clients-reached-x-errors/?utm_source=blog.mkacg.com">https://snapoverflow.com/ubuntu/question/4499/how-can-i-diagnose-debug-maximum-number-of-clients-reached-x-errors/&lt;/a>&lt;/p>&lt;/blockquote></description><author>justforlxz</author></item><item><title>deepin 23.1 DDE 移植简要指南</title><link>https://deepin-community.github.io/sig-dde-porting/posts/dde-v23.1-porting-guide/?utm_source=atom_feed</link><pubDate>Wed, 16 Apr 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/dde-v23.1-porting-guide/</guid><description>&lt;p>&lt;em>编辑的话请把自己的名字加到作者名单里&lt;/em>&lt;/p>
&lt;p>deepin 23.1 现已发布。为了方便各个其它发行版的包维护者可以更方便的移植 DDE 到对应的发行版，这里提供一篇简要的移植指南，用以描述常见的移植问题和解决方案。&lt;/p>
&lt;blockquote>
&lt;p>下面对项目名称的称呼均以 GitHub 对应的原始仓库名为准。
&amp;#123;.note}&lt;/p>&lt;/blockquote>
&lt;h2 id="概览">概览&lt;/h2>
&lt;p>对于 DDE 本次更新并未包含大规模的结构调整，而是比较存催的缺陷修复为主的更新，对于比较值得注意的事项将会列在下方。对于 deepin 23 的注意事项，可参见 deepin 23 正式版发布时的移植指南文章所给出的说明。&lt;/p>
&lt;p>由于 DDE 涉及到的各个组件项目的版本间互相影响，我们强烈建议移植人员参照 deepin 23.1 正式版所使用的包版本进行打包（也务必遵循依赖顺序打包）。下面会对主要的部分进行详细说明。&lt;/p>
&lt;p>下面给出的版本号信息供打包移植时参考。若您需要获取 ISO 镜像中使用的确切软件版本列表，请挂载 ISO 后参阅 &lt;code>LIVE/FILESYSTEM.MANIFEST&lt;/code> (也可能是 &lt;code>LIVE/FILESYS0.MAN&lt;/code>）路径对应的文件的内容。&lt;/p>
&lt;h2 id="主要组件">主要组件&lt;/h2>
&lt;h3 id="dtk-与-dtk6">DTK 与 DTK6&lt;/h3>
&lt;p>DTK 是 DDE 组件与应用的基础依赖，适用于 RC 的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>dtkcommon&lt;/td>
&lt;td>5.7.5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtklog&lt;/td>
&lt;td>0.0.2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkcore&lt;/td>
&lt;td>5.7.5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkgui&lt;/td>
&lt;td>5.7.5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkwidget&lt;/td>
&lt;td>5.7.5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkdeclarative&lt;/td>
&lt;td>5.7.5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5integration&lt;/td>
&lt;td>5.7.5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5platform-plugins&lt;/td>
&lt;td>5.7.5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6log&lt;/td>
&lt;td>0.0.2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6core&lt;/td>
&lt;td>6.0.25&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6gui&lt;/td>
&lt;td>6.0.25&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6widget&lt;/td>
&lt;td>6.0.25&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6declarative&lt;/td>
&lt;td>6.0.25&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6integration&lt;/td>
&lt;td>6.0.25&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6platform-plugins&lt;/td>
&lt;td>6.0.25&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>除新增的 dtklog 外，本次 DTK 版本号以及相对应的平台插件等版本号均已对齐，可直接参照打包。&lt;/p>
&lt;p>deepin-kwin wayland 功能已经废弃，未来将由 treeland 替代。目前 dwayland 包已经不再使用，依赖此包的应用比如 qt5platform-plugins，不应该继续编译依赖 dwayland 的功能，可参照 &lt;a href="https://github.com/linuxdeepin/developer-center/issues/7217">linuxdeepin/developer-center#7217&lt;/a> 打对应的 patch 规避。&lt;/p>
&lt;p>目前，使用 dtk6 的正式组件有 dde-application-manager，dde-launchpad 与 dde-shell。需要注意，deepin 23 环境中 dde-shell 的托盘组件 dde-tray-loader 仍然需要使用 qt5。&lt;/p>
&lt;h3 id="dde-主要组件">DDE 主要组件&lt;/h3>
&lt;p>下面仅涉及变化较大或影响较广的组件。其余未涉及的组件可正常参照最新 tag 进行打包与移植。&lt;/p>
&lt;p>下面涉及到的组件的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>deepin-osconfig&lt;/td>
&lt;td>2024.08.06&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-app-services&lt;/td>
&lt;td>1.0.27&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-session&lt;/td>
&lt;td>1.2.13&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-manager&lt;/td>
&lt;td>1.2.27&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-tray-loader&lt;/td>
&lt;td>1.0.10&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-shell&lt;/td>
&lt;td>1.0.10&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launchpad&lt;/td>
&lt;td>1.0.11&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-wizard&lt;/td>
&lt;td>0.1.10&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>deepin-wayland-protocols&lt;/td>
&lt;td>1.10.0.28&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>deepin-kwin&lt;/td>
&lt;td>5.27.2.213&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launcher&lt;/td>
&lt;td>被 dde-launchpad 取代，不再使用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-dock&lt;/td>
&lt;td>被 dde-shell 取代，不再使用&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="dde-application-manager">dde-application-manager&lt;/h3>
&lt;p>此组件现已使用主干分支最新版本（当前为 &lt;code>1.2.27&lt;/code>）。请注意，较早的主干版本（例如 &lt;code>1.2.26&lt;/code>）在 deepin 23 环境存在一些已知行为问题，故移植最新的 deepin 23 DDE 时，请至少使用 &lt;code>1.2.27&lt;/code> 版本。&lt;/p>
&lt;h3 id="dde-session-shell">dde-session-shell&lt;/h3>
&lt;p>尽管此组件不存在架构性质层面的较大调整，但涉及到打包移植相关的注意事项。此组件由于主干分支的研发需求，对仓库进行过迁移到。当前 GitHub 上的 linuxdeepin/dde-session-shell 仓库历史已与之前不同。故如果你需要适用于 deepin 23 的此仓库的完整历史，请转到 &lt;a href="https://github.com/linuxdeepin/dde-session-shell-snipe">dde-session-shell-snipe&lt;/a>。所有原始仓库的提交历史以及 tag 均可在这个仓库中找到（实质是仓库重命名后新建了与原名的同名仓库）。&lt;/p>
&lt;p>（注：相关请参见&lt;a href="https://www.freelists.org/post/deepin-devel/githubddesessionshellddesessionshell">此邮件列表存档&lt;/a>）&lt;/p>
&lt;h2 id="qt-69-编译问题">Qt 6.9 编译问题&lt;/h2>
&lt;ul>
&lt;li>dtk：23 版本 dtk 没有适配 qt 6.9,可尝试使用最新 tag,或者参考 Arch linux 打包提供的 patch&lt;/li>
&lt;li>dde-shell：需要 &lt;a href="https://github.com/linuxdeepin/dde-shell/pull/1091">https://github.com/linuxdeepin/dde-shell/pull/1091&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>如果遇到 qmlsc 崩溃问题，见 &lt;a href="https://bugreports.qt.io/browse/QTBUG-135885">QTBUG-135885&lt;/a> 和 &lt;a href="https://bugreports.qt.io/browse/QTBUG-135288">QTBUG-135885&lt;/a>，需要为 qtdeclarative 增加以下 patch：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/qt/qtdeclarative/commit/d1aa2e8466bab73c3e4d120356238b482b55f02a.patch">https://github.com/qt/qtdeclarative/commit/d1aa2e8466bab73c3e4d120356238b482b55f02a.patch&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="技术预览组件">技术预览组件&lt;/h2>
&lt;p>原本涉及的技术预览组件在 23 至 23.1 的这个阶段均无较大进展，故不再于此罗列。&lt;/p>
&lt;h2 id="获取移植帮助">获取移植帮助&lt;/h2>
&lt;p>如果您希望得到移植相关的帮助，请考虑加入我们 DDE 移植小组的在线交流群（下列房间有桥接，任选其一即可），一起展开相关的交流：&lt;/p>
&lt;ul>
&lt;li>Matrix 群：&lt;code>#dde-port:deepin.org&lt;/code>&lt;/li>
&lt;li>Telegram 群：&lt;a href="https://t.me/ddeport">https://t.me/ddeport&lt;/a>&lt;/li>
&lt;/ul></description><author>DDE 移植兴趣小组</author></item><item><title>deepin 25 alpha DDE 移植简要指南</title><link>https://deepin-community.github.io/sig-dde-porting/posts/deepin-25-alpha-dde-porting-guide/?utm_source=atom_feed</link><pubDate>Fri, 21 Mar 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/deepin-25-alpha-dde-porting-guide/</guid><description>&lt;p>&lt;em>编辑的话请把自己的名字加到作者名单里&lt;/em>&lt;/p>
&lt;p>即将发布（你阅读到这个文章的时候可能已经发布了）的 deepin 25 alpha 将会包含对应的新版 DDE。为了方便各个其它发行版的包维护者可以更方便的移植 DDE 到对应的发行版，这里提供一篇简要的移植指南，用以描述常见的移植问题和解决方案。&lt;/p>
&lt;blockquote>
&lt;p>下面对项目名称的称呼均以 GitHub 对应的原始仓库名为准。
&amp;#123;.note}&lt;/p>&lt;/blockquote>
&lt;h2 id="概览">概览&lt;/h2>
&lt;p>相对于 deepin 25 preview，在 deepin 25 alpha 中并不存在较大幅的架构调整，而是以缺陷修复以及完善之前尚未完善但计划涵盖在最终版本的组件（例如 QML 版控制中心）作为研发的重心。同时，我们也对 Qt、DTK 进行了更多完善，以供 DDE 组件以及 Treeland 能够更好的运行。&lt;/p>
&lt;p>由于这些项目的版本间互相影响，我们建议移植人员参照 deepin 25 alpha 所使用的包版本进行打包，下面会对主要的部分进行详细说明。&lt;/p>
&lt;p>需要注意的是，由于此文章编写时间早于版本发布时间，故最终版本镜像中使用的版本可能高于下面列出的版本。我们尽可能确保此文章的准确性，但若您需要获取 ISO 镜像中使用的确切软件版本列表，请挂载 ISO 后参阅 &lt;code>LIVE/FILESYS&amp;#123;T,0,1}.MAN&lt;/code>/&lt;code>live/filesystem.manifest&lt;/code> 路径对应的文件的内容。&lt;/p>
&lt;h2 id="主要组件">主要组件&lt;/h2>
&lt;h3 id="dtk-与-dtk6">DTK 与 DTK6&lt;/h3>
&lt;p>DTK 是 DDE 组件与应用的基础依赖，适用于 RC 的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>dtkcommon&lt;/td>
&lt;td>5.7.12&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtklog&lt;/td>
&lt;td>0.0.2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkcore&lt;/td>
&lt;td>5.7.12&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkgui&lt;/td>
&lt;td>5.7.12&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkwidget&lt;/td>
&lt;td>5.7.12&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkdeclarative&lt;/td>
&lt;td>5.7.12&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5integration&lt;/td>
&lt;td>5.7.12&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5platform-plugins&lt;/td>
&lt;td>5.7.7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6core&lt;/td>
&lt;td>6.0.32.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6gui&lt;/td>
&lt;td>6.0.32&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6widget&lt;/td>
&lt;td>6.0.32&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6declarative&lt;/td>
&lt;td>6.0.32.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6integration&lt;/td>
&lt;td>6.0.32&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6platform-plugins&lt;/td>
&lt;td>6.0.32&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>除 dtklog 以及 dtk6 的 core 于 declarative 外，本次 DTK 版本号以及相对应的平台插件等版本号均已对齐，可直接参照打包。&lt;/p>
&lt;p>关于 qt5platform-plugins，现有的 dwayland 插件可能对非 DDE 环境（例如 KDE）的 wayland 用户存在影响，可参照 &lt;a href="https://github.com/linuxdeepin/developer-center/issues/7217">linuxdeepin/developer-center#7217&lt;/a> 打对应的 patch 规避影响。&lt;/p>
&lt;h3 id="dde-主要组件">DDE 主要组件&lt;/h3>
&lt;p>下面仅涉及变化较大或影响较广的组件。其余未涉及的组件可正常参照最新 tag 进行打包与移植。&lt;/p>
&lt;p>由于 deepin 25 preview 仍在持续开发过程中，故较多组件采取了 x.99.z 的版本号策略。此外，一般情况下，此类 tag 并不会实际以 git tag 的形式存在，而只会体现在 &lt;code>debian/changlog&lt;/code> 文件中。下面涉及到的此类版本号将会在版本发布前后补充对应的 git tag。&lt;/p>
&lt;p>下面涉及到的组件的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>dde-session&lt;/td>
&lt;td>1.99.11&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-manager&lt;/td>
&lt;td>1.2.26&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-shell&lt;/td>
&lt;td>1.99.28&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launchpad&lt;/td>
&lt;td>1.99.9&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-tray-loader&lt;/td>
&lt;td>1.99.19&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-wizard&lt;/td>
&lt;td>0.1.13&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-clipboard&lt;/td>
&lt;td>6.1.6&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launcher&lt;/td>
&lt;td>被 dde-launchpad 取代，不再使用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-dock&lt;/td>
&lt;td>被 dde-shell 取代，不再使用&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="dde-application-manager">dde-application-manager&lt;/h4>
&lt;p>由于涉及到诸多关于应用识别的改善，故建议总是使用最新版本。&lt;/p>
&lt;h4 id="dde-shell">dde-shell&lt;/h4>
&lt;p>dde-shell 旨在将 DDE 桌面环境插件化与模块化，降低开发难度，使各个组件的替换变得更加容易，并且提供更好的桌面环境集成支持。alpha 阶段相比 preview 阶段集中在缺陷的修复上，并未涵盖太多的结构调整和新特性。对于 alpha 以及更早版本的变化，请阅读之前的博客文章。&lt;/p>
&lt;p>为保障 dde-shell 在 Qt 6.8.0 或 6.8.1 的环境可以正常运行（即使是X11环境下），若 ，则 &lt;strong>必须&lt;/strong> 给 qtwayland 打下面的 patch：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://codereview.qt-project.org/c/qt/qtwayland/+/603556">https://codereview.qt-project.org/c/qt/qtwayland/+/603556&lt;/a> （&lt;a href="https://github.com/qt/qtwayland/commit/070414dd4155e13583e5e8b16bed1a5b68d32910">此 patch 已合入 Qt 上游&lt;/a>，涵盖在 &lt;code>Qt Wayland &amp;gt;= 6.8.2&lt;/code> 版本中）&lt;/li>
&lt;/ul>
&lt;p>另外，dde-shell 在 alpha 中为修正一个特定问题所包含的一个变更依赖另一个 Qt Wayland 的 patch：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/deepin-community/qt6-wayland/pull/12">https://github.com/deepin-community/qt6-wayland/pull/12&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>若你所移植的目标发行版不接受此补丁，则可考虑对 dde-shell 项目 revert 于此相关的对应 commit：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/dde-shell/commit/77cac3d5a346728f6678eedc77fce404f0ffd8b6">https://github.com/linuxdeepin/dde-shell/commit/77cac3d5a346728f6678eedc77fce404f0ffd8b6&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="dde-launchpad">dde-launchpad&lt;/h4>
&lt;p>dde-launchpad 现仅支持以 dde-shell 插件的形式被最终用户使用。因而，打包 dde-launchpad 现需要先打包 dde-shell，并确保用户最终使用的是 dde-shell。&lt;/p>
&lt;h4 id="dde-session">dde-session&lt;/h4>
&lt;p>需要注意的是，我们已在 deepin 23 beta3 起放弃了对 deepin-kwin wayland 的支持，DDE 后续所有 wayland 相关的支持均由 treeland 提供。请参见后续的 Treeland 段落。&lt;/p>
&lt;p>下面涉及到的组件的版本参照如下。对于位于非 linuxdeepin 组织的软件包，此处一并给出了组织名：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>vioken/waylib&lt;/td>
&lt;td>0.6.13&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>vioken/qwlroots&lt;/td>
&lt;td>0.5.3&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>treeland&lt;/td>
&lt;td>0.5.20&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ddm&lt;/td>
&lt;td>0.1.10&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="treeland-环境">Treeland 环境&lt;/h2>
&lt;p>Treeland 环境相较于 deepin 23 阶段有了较多的提升，不过由于 Treeland 迭代开发过程中我们对 Qt 以及 wlroots 进行了诸多完善，故 Treeland 对 Qt 以及 wlroots 等组件有较高的版本要求，以及可能需要应用一些额外的 patch。&lt;/p>
&lt;h3 id="ddm">DDM&lt;/h3>
&lt;p>尽管 DDM 目前是基本功能可用状态，DDM 目前仍相对而言不够稳定。对于打包移植而言，建议采用其他DM来启动用户级的treeland。&lt;/p>
&lt;p>对于其它 DM，只需要打包时安装 &lt;code>usr/share/wayland-sessions/treeland-user.desktop&lt;/code> 即可。&lt;/p>
&lt;h3 id="qt-补丁">Qt 补丁&lt;/h3>
&lt;p>下述假定您的发行版使用的 Qt 版本为 Qt 6.8.2。&lt;/p>
&lt;p>如果你在 Treeland 下遇到小 launchpad 无法输入中文的问题，可以打下面的 patch，但是该 patch 目前尚未进行完整测试，可能存在一些问题。&lt;/p>
&lt;p>&lt;a href="https://codereview.qt-project.org/c/qt/qtbase/+/611940">https://codereview.qt-project.org/c/qt/qtbase/+/611940&lt;/a>&lt;/p>
&lt;p>另外，如果你的发行版所附的 Qt 6.8 版本并未更新至 Qt 6.8.2，则可能需要打三个额外的补丁，可参见 &lt;a href="https://deepin-community.github.io/sig-dde-porting/posts/dde-qt6.8-porting-guide/">DDE Qt 6.8 适配说明（针对 Qt 6.8.0）&lt;/a> 以及 &lt;a href="https://deepin-community.github.io/sig-dde-porting/posts/deepin-25-preview-dde-porting-guide/#qt-%E8%A1%A5%E4%B8%81">deepin 25 preview DDE 移植简要指南（针对 Qt 6.8.1）&lt;/a>。&lt;/p>
&lt;h2 id="获取移植帮助">获取移植帮助&lt;/h2>
&lt;p>如果您希望得到移植相关的帮助，请考虑加入我们 DDE 移植小组的在线交流群（下列房间有桥接，任选其一即可），一起展开相关的交流：&lt;/p>
&lt;ul>
&lt;li>Matrix 群：&lt;code>#dde-port:deepin.org&lt;/code>&lt;/li>
&lt;li>Telegram 群：&lt;a href="https://t.me/ddeport">https://t.me/ddeport&lt;/a>&lt;/li>
&lt;/ul></description><author>DDE 移植兴趣小组</author></item><item><title>deepin 25 preview DDE 移植简要指南</title><link>https://deepin-community.github.io/sig-dde-porting/posts/deepin-25-preview-dde-porting-guide/?utm_source=atom_feed</link><pubDate>Thu, 16 Jan 2025 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/deepin-25-preview-dde-porting-guide/</guid><description>&lt;p>&lt;em>编辑的话请把自己的名字加到作者名单里&lt;/em>&lt;/p>
&lt;p>即将发布（你阅读到这个文章的时候可能已经发布了）的 deepin 25 preview 将会包含对应的新版 DDE。为了方便各个其它发行版的包维护者可以更方便的移植 DDE 到对应的发行版，这里提供一篇简要的移植指南，用以描述常见的移植问题和解决方案。&lt;/p>
&lt;blockquote>
&lt;p>下面对项目名称的称呼均以 GitHub 对应的原始仓库名为准。
&amp;#123;.note}&lt;/p>&lt;/blockquote>
&lt;h2 id="概览">概览&lt;/h2>
&lt;p>相对于 deepin 23，在 deepin 25 中，包括桌面、通知中心在内的大部分旧的 DDE 桌面组件已转化为 dde-shell 插件形式，以供更好的跨显示环境兼容性。包括控制中心在内的组件也已开始提供全新设计以及基于 QML 的全新界面。同时，我们也对 Qt、DTK 进行了更多完善，以供 DDE 组件以及 Treeland 能够更好的运行。&lt;/p>
&lt;p>由于这些项目的版本间互相影响，我们建议移植人员参照 deepin 25 preview 所使用的包版本进行打包，下面会对主要的部分进行详细说明。&lt;/p>
&lt;p>需要注意的是，由于此文章编写时间早于版本发布时间，故最终版本镜像中使用的版本可能高于下面列出的版本。我们尽可能确保此文章的准确性，但若您需要获取 ISO 镜像中使用的确切软件版本列表，请挂载 ISO 后参阅 &lt;code>LIVE/FILESYS&amp;#123;0,1}.MAN&lt;/code>/&lt;code>live/filesystem.manifest&lt;/code> 路径对应的文件的内容。&lt;/p>
&lt;h2 id="主要组件">主要组件&lt;/h2>
&lt;h3 id="dtk-与-dtk6">DTK 与 DTK6&lt;/h3>
&lt;p>DTK 是 DDE 组件与应用的基础依赖，适用于 RC 的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>dtkcommon&lt;/td>
&lt;td>5.7.7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtklog&lt;/td>
&lt;td>0.0.2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkcore&lt;/td>
&lt;td>5.7.7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkgui&lt;/td>
&lt;td>5.7.7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkwidget&lt;/td>
&lt;td>5.7.7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkdeclarative&lt;/td>
&lt;td>5.7.7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5integration&lt;/td>
&lt;td>5.7.7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5platform-plugins&lt;/td>
&lt;td>5.7.7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6core&lt;/td>
&lt;td>6.0.27&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6gui&lt;/td>
&lt;td>6.0.27&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6widget&lt;/td>
&lt;td>6.0.27&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6declarative&lt;/td>
&lt;td>6.0.27&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6integration&lt;/td>
&lt;td>6.0.27&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6platform-plugins&lt;/td>
&lt;td>6.0.27&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>除 dtklog 外，本次 DTK 版本号以及相对应的平台插件等版本号均已对齐，可直接参照打包。&lt;/p>
&lt;p>关于 qt5platform-plugins，现有的 dwayland 插件可能对非 DDE 环境（例如 KDE）的 wayland 用户存在影响，可参照 &lt;a href="https://github.com/linuxdeepin/developer-center/issues/7217">linuxdeepin/developer-center#7217&lt;/a> 打对应的 patch 规避影响。&lt;/p>
&lt;h3 id="dde-主要组件">DDE 主要组件&lt;/h3>
&lt;p>下面仅涉及变化较大或影响较广的组件。其余未涉及的组件可正常参照最新 tag 进行打包与移植。&lt;/p>
&lt;p>由于 deepin 25 preview 仍在持续开发过程中，故较多组件采取了 x.99.z 的版本号策略。此外，一般情况下，此类 tag 并不会实际以 git tag 的形式存在，而只会体现在 &lt;code>debian/changlog&lt;/code> 文件中。下面涉及到的此类版本号将会在版本发布前后补充对应的 git tag。&lt;/p>
&lt;p>下面涉及到的组件的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>dde-session&lt;/td>
&lt;td>1.99.7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-manager&lt;/td>
&lt;td>1.2.23&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-shell&lt;/td>
&lt;td>1.99.19&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launchpad&lt;/td>
&lt;td>1.99.5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-tray-loader&lt;/td>
&lt;td>1.99.12&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-wizard&lt;/td>
&lt;td>0.1.11&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-clipboard&lt;/td>
&lt;td>6.1.4&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launcher&lt;/td>
&lt;td>被 dde-launchpad 取代，不再使用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-dock&lt;/td>
&lt;td>被 dde-shell 取代，不再使用&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="dde-application-manager">dde-application-manager&lt;/h4>
&lt;p>由于涉及到诸多关于应用识别的改善，故建议总是使用最新版本。&lt;/p>
&lt;h4 id="dde-shell">dde-shell&lt;/h4>
&lt;p>dde-shell 旨在将 DDE 桌面环境插件化与模块化，降低开发难度，使各个组件的替换变得更加容易，并且提供更好的桌面环境集成支持。preview 阶段，dde-shell 已经可以满足原计划的部分目标。现 DDE 环境下，dde-shell 已取代 dde-dock 来负责管理整个 dock 区域、 dde-launchpad 提供了对应的 dde-shell 插件用以展示启动器相关的界面、原 dde-session-ui 中的通知中心部分也转到了 dde-shell 中，且转用了新的界面设计。&lt;/p>
&lt;p>关于 shell 的服务启动方面，为了方便故障排查，dde-shell 从原本的单进程转为了两个进程（分别提供桌面和任务栏两个部分）。另外，shell项目的任务栏部分在此阶段也配合 dde-application-manager 对应用识别的准确度进行了诸多完善。若仍有发现应用错误识别和错误分组的问题，欢迎及时反馈。&lt;/p>
&lt;p>为保障dde-shell在Qt6.8之后的环境可以正常运行（即使是X11环境下），&lt;strong>必须&lt;/strong>给qtwayland打下面的patch：&lt;/p>
&lt;p>&lt;a href="https://codereview.qt-project.org/c/qt/qtwayland/+/603556">https://codereview.qt-project.org/c/qt/qtwayland/+/603556&lt;/a>&lt;/p>
&lt;h4 id="dde-launchpad">dde-launchpad&lt;/h4>
&lt;p>dde-launchpad 现仅支持以 dde-shell 插件的形式被最终用户使用。因而，打包 dde-launchpad 现需要先打包 dde-shell，并确保用户最终使用的是 dde-shell。&lt;/p>
&lt;h4 id="dde-session">dde-session&lt;/h4>
&lt;p>需要注意的是，我们已在 deepin 23 beta3 起放弃了对 deepin-kwin wayland 的支持，DDE 后续所有 wayland 相关的支持均由 treeland 提供。请参见后续的 Treeland 段落。&lt;/p>
&lt;p>下面涉及到的组件的版本参照如下。对于位于非 linuxdeepin 组织的软件包，此处一并给出了组织名：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>vioken/waylib&lt;/td>
&lt;td>0.6.10&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>vioken/qwlroots&lt;/td>
&lt;td>0.5.2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>treeland&lt;/td>
&lt;td>0.5.17&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ddm&lt;/td>
&lt;td>0.1.9&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="treeland-环境">Treeland 环境&lt;/h2>
&lt;p>Treeland 环境相较于 deepin 23 阶段有了较多的提升，不过由于 Treeland 迭代开发过程中我们对 Qt 以及 wlroots 进行了诸多完善，故 Treeland 对 Qt 以及 wlroots 等组件有较高的版本要求，以及可能需要应用一些额外的 patch。&lt;/p>
&lt;h3 id="ddm">DDM&lt;/h3>
&lt;p>尽管 DDM 目前是基本功能可用状态，DDM 目前仍相对而言不够稳定。对于打包移植而言，建议采用其他DM来启动用户级的treeland。&lt;/p>
&lt;p>对于其它 DM，只需要打包时安装 &lt;code>usr/share/wayland-sessions/treeland-user.desktop&lt;/code> 即可。&lt;/p>
&lt;h3 id="qt-补丁">Qt 补丁&lt;/h3>
&lt;p>下述假定您的发行版使用的 Qt 版本为 Qt 6.8.1。&lt;/p>
&lt;p>为保障 dde-shell 在 Treeland 上可以正常运行，需要打下面的 patch，否则可能会出现 dde-shell 崩溃的情况。&lt;/p>
&lt;p>&lt;a href="https://codereview.qt-project.org/c/qt/qtbase/+/607654">https://codereview.qt-project.org/c/qt/qtbase/+/607654&lt;/a>&lt;/p>
&lt;p>如果你在 Treeland 下遇到小 launchpad 无法输入中文的问题，可以打下面的 patch，但是该 patch 目前尚未进行完整测试，可能存在一些问题。&lt;/p>
&lt;p>&lt;a href="https://codereview.qt-project.org/c/qt/qtbase/+/611940">https://codereview.qt-project.org/c/qt/qtbase/+/611940&lt;/a>&lt;/p>
&lt;p>另外，如果你的发行版所附的 Qt 6.8 版本并未更新至 Qt 6.8.1，则可能需要打两个额外的补丁，可参见 &lt;a href="https://deepin-community.github.io/sig-dde-porting/posts/dde-qt6.8-porting-guide/">DDE Qt 6.8 适配说明&lt;/a>。&lt;/p>
&lt;h2 id="获取移植帮助">获取移植帮助&lt;/h2>
&lt;p>如果您希望得到移植相关的帮助，请考虑加入我们 DDE 移植小组的在线交流群（下列房间有桥接，任选其一即可），一起展开相关的交流：&lt;/p>
&lt;ul>
&lt;li>Matrix 群：&lt;code>#dde-port:deepin.org&lt;/code>&lt;/li>
&lt;li>Telegram 群：&lt;a href="https://t.me/ddeport">https://t.me/ddeport&lt;/a>&lt;/li>
&lt;/ul></description><author>DDE 移植兴趣小组</author></item><item><title>使用 distrobox 和 nix 加速 UOS 开发</title><link>https://blog.mkacg.com/2025/01/02/use-docker-and-distrobox-and-nix-to-develop-uos/</link><pubDate>Thu, 02 Jan 2025 08:18:00 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2025/01/02/use-docker-and-distrobox-and-nix-to-develop-uos/</guid><description>&lt;p>UOS 这仓库有多鬼畜，就不用我多说了，开发的时候每次都要重装一个不同版本的系统也挺麻烦，比如 1060、1063、1070、1070u1。。。&lt;/p>&lt;p>所以我就想到，我可以用构建用的 rootfs 加上仓库，整出来一个基础环境，然后通过 distrobox 来启动，这样随时都可以快速准备一个环境。&lt;/p>&lt;p>平时我用的开发工具就 neovim 和 vscode 居多，所以再配合上 nix 把 neovim 和一些 UOS 缺少的工具都装上，想想都是美滋滋。&lt;/p>&lt;p>说干就干!&lt;/p>&lt;h2 id="构建-Docker-镜像">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#构建-Docker-镜像" title="构建 Docker 镜像">&lt;/a>构建 Docker 镜像&lt;/h2>&lt;p>由于 UOS 有多个不同架构的支持，肯定不能绑死到一个平台上，我使用 docker buildx 来构建多架构镜像。rootfs 我就不公开了，直接上 Dockerfile。&lt;/p>&lt;figure class="highlight dockerfile">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="keyword">FROM&lt;/span> --platform=$TARGETPLATFORM scratch&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">ARG&lt;/span> TARGETARCH&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">ARG&lt;/span> UOS_VERSION&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">ADD&lt;/span>&lt;span class="language-bash"> buster-&lt;span class="variable">$&amp;#123;TARGETARCH&amp;#125;&lt;/span>-1050update4.tgz /&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">RUN&lt;/span>&lt;span class="language-bash"> &lt;span class="built_in">echo&lt;/span> &lt;span class="string">&amp;quot;deb [trusted=yes] http://pools.uniontech.com/desktop-professional &lt;span class="variable">$&amp;#123;UOS_VERSION:-eagle/1070&amp;#125;&lt;/span> main contrib non-free&amp;quot;&lt;/span> &amp;gt; /etc/apt/sources.list&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">RUN&lt;/span>&lt;span class="language-bash"> apt-get update &amp;amp;&amp;amp; apt-get install -y \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> apt-utils \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> apt-transport-https \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> ca-certificates \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> curl \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> gnupg \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> lsb-release \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> &amp;amp;&amp;amp; apt-get dist-upgrade -y \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> &amp;amp;&amp;amp; apt-get clean \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-bash"> &amp;amp;&amp;amp; &lt;span class="built_in">rm&lt;/span> -rf /var/lib/apt/lists/*&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">CMD&lt;/span>&lt;span class="language-bash"> [ &lt;span class="string">&amp;quot;bash&amp;quot;&lt;/span> ]&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>这里为了方便我构建，我将仓库的名称使用变量控制了。&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">docker buildx build --platform=linux/amd64 -t linuxdeepin/1063:base --build-arg UOS_VERSION=eagle/1063 . --load&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>只需要控制 label 和 仓库名称，就能快速的创建出一个可用的 docker 镜像。使用 &lt;code>--load&lt;/code> 参数构建完以后直接导入镜像，可以在 &lt;code>docker images&lt;/code> 中看到。&lt;/p>&lt;h2 id="创建-Distrobox-环境">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#创建-Distrobox-环境" title="创建 Distrobox 环境">&lt;/a>创建 Distrobox 环境&lt;/h2>&lt;p>现在有个可用的 docker 镜像，就可以使用 distrobox 创建环境了。&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">distrobox create --image linuxdeepin/1063:base --name 1063 --volume /nix:/nix:rw --additional-flags &amp;quot;--pids-limit -1&amp;quot;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>安装 nix 的过程请参考上一篇文章，这里是把 nix 挂载到环境里了。&lt;/p>&lt;p>运行以后等待一会儿，就看到创建成功了。&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">Creating &amp;#x27;1063&amp;#x27; using image linuxdeepin/1063:base [ OK ]&lt;/span>&lt;br />&lt;span class="line">Distrobox &amp;#x27;1063&amp;#x27; successfully created.&lt;/span>&lt;br />&lt;span class="line">To enter, run:&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">distrobox enter 1063&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">Successfully copied 2.05kB to /tmp/1063.os-release&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>提醒我们用 &lt;code>distrobox enter 1063&lt;/code> 进入环境。&lt;/p>&lt;h2 id="运行">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#运行" title="运行">&lt;/a>运行&lt;/h2>&lt;p>运行的话没啥问题，只是需要设置一下用户密码，不知道是不是 distrobox 改变策略了，以前应该是首次进入的时候就提醒设置密码，现在必须手动先运行一下 &lt;code>passwd&lt;/code>，不然 sudo 之类的命令是不能用的。&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">❯ distrobox enter 1063&lt;/span>&lt;br />&lt;span class="line">Starting container... [ OK ]&lt;/span>&lt;br />&lt;span class="line">Installing basic packages... [ OK ]&lt;/span>&lt;br />&lt;span class="line">Setting up devpts mounts... [ OK ]&lt;/span>&lt;br />&lt;span class="line">Setting up read-only mounts... [ OK ]&lt;/span>&lt;br />&lt;span class="line">Setting up read-write mounts... [ OK ]&lt;/span>&lt;br />&lt;span class="line">Setting up host&amp;#x27;s sockets integration... [ OK ]&lt;/span>&lt;br />&lt;span class="line">Integrating host&amp;#x27;s themes, icons, fonts... [ OK ]&lt;/span>&lt;br />&lt;span class="line">Setting up distrobox profile... [ OK ]&lt;/span>&lt;br />&lt;span class="line">Setting up sudo... [ OK ]&lt;/span>&lt;br />&lt;span class="line">Setting up user groups... [ OK ]&lt;/span>&lt;br />&lt;span class="line">Setting up user&amp;#x27;s group list... [ OK ]&lt;/span>&lt;br />&lt;span class="line">Adding user... [ OK ]&lt;/span>&lt;br />&lt;span class="line">Ensuring user&amp;#x27;s access... [ OK ]&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">Container Setup Complete!&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure></description><author>justforlxz</author></item><item><title>Treeland Part.1 如何实现一个基于 wlroots 的合成器</title><link>https://blog.mkacg.com/2024/11/09/treeland-part1-how-to-implement-a-wlroots-based-compositor/</link><pubDate>Sat, 09 Nov 2024 04:00:00 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2024/11/09/treeland-part1-how-to-implement-a-wlroots-based-compositor/</guid><description>&lt;aside>😀 Treeland 是一个基于 wlroots 和 Qt 的 Wayland 合成器并且支持多用户共用合成器。&lt;/aside>&lt;h2 id="名词概念">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#名词概念" title="名词概念">&lt;/a>名词概念&lt;/h2>&lt;h3 id="Qt">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Qt" title="Qt">&lt;/a>Qt&lt;/h3>&lt;p>Qt（&amp;#x2f;ˈkjuːt&amp;#x2f;，发音同“cute”）是一个跨平台的C++应用程序开发框架。广泛用于开发GUI程序，这种情况下又被称为部件工具箱。也可用于开发非GUI程序，例如控制台工具和服务器。&lt;/p>&lt;h3 id="wlroots">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#wlroots" title="wlroots">&lt;/a>wlroots&lt;/h3>&lt;p>用于构建 &lt;a href="https://wayland.freedesktop.org/">Wayland&lt;/a> 合成器的模块化工具集，简化了约 60,000 行代码的开发工作。&lt;/p>&lt;ul>&lt;li>提供抽象底层显示和输入的后端，支持 KMS&amp;#x2f;DRM、libinput、Wayland、X11 等，可动态创建和销毁。&lt;/li>&lt;li>实现多种 Wayland 接口，支持协议扩展，促进合成器标准化。&lt;/li>&lt;li>提供通用合成器组件，如物理空间输出管理。&lt;/li>&lt;li>集成 Xwayland 抽象，简化 X11 窗口管理。&lt;/li>&lt;li>提供渲染器抽象，支持简单和自定义渲染需求。&lt;/li>&lt;/ul>&lt;h3 id="seat">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#seat" title="seat">&lt;/a>seat&lt;/h3>&lt;p>由分配给特定工作场景的所有硬件设备组成。它至少包含一个图形设备，通常还有键盘和鼠标。此外，它可能包括摄像头、声卡等设备。座位由座位名称标识，这是一个短字符串（不超过64个字符），以”seat”四个字符开头，后跟至少一个a-zA-Z0-9范围内的字符，或”_”和”-“。这种命名方式适合用于文件名。座位名称可能是稳定的，也可能不稳定，如果座位再次可用，其名称可以重复使用。&lt;/p>&lt;h3 id="RHI">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#RHI" title="RHI">&lt;/a>RHI&lt;/h3>&lt;p>RHI 是 &lt;code>Renderer Hardware Interface&lt;/code>（渲染硬件接口）的缩写，是一套对硬件的抽象，在上层只需要设置参数，底层具体使用的是 OpenGL、Vulkan、DX12 还是 Metal 哪套接口，我们是不必关心的。&lt;/p>&lt;p>Qt6 提供了 QRHI，为 Qt 程序提供了底层的硬件抽象，这样上层的 QtQuick 组件在执行 GPU 渲染时，就可以自动调用对应的驱动接口。&lt;/p>&lt;h3 id="QPA">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#QPA" title="QPA">&lt;/a>QPA&lt;/h3>&lt;p>Qt 平台抽象（QPA）是 Qt 中的核心平台抽象层。&lt;/p>&lt;p>QPA 的 API 可通过类前缀”QPlatform*”识别，用于实现 &lt;a href="https://doc.qt.io/qt-6/qtgui-index.html">Qt GUI&lt;/a> 中的高级类。例如，&lt;code>QPlatformWindow&lt;/code> 用于窗口系统集成，而 &lt;code>QPlatformTheme&lt;/code> 和 &lt;code>QStyleHint&lt;/code> 则用于深层次的平台主题和集成。&lt;/p>&lt;h2 id="基本工作流程">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#基本工作流程" title="基本工作流程">&lt;/a>基本工作流程&lt;/h2>&lt;p>&lt;code>Treeland&lt;/code> 使用 &lt;code>QQuickWindow&lt;/code> 作为渲染的根，这样在 &lt;code>Treeland&lt;/code> 里开发时，就如同开发一个普通 Qt 程序一样，先创建一个 Window，在 Window 内创建 Qt 控件，使用 QEvent 处理各种事件。&lt;/p>&lt;p>那么 Treeland 是如何实现这件事的呢？&lt;/p>&lt;p>&lt;code>QQuickWindow&lt;/code> 的私有类提供了自定义 &lt;code>QQuickGraphicsDevice&lt;/code> 对象的接口，而 &lt;code>QQuickGraphicsDevice&lt;/code> 可以使用 &lt;code>fromOpenGLContext&lt;/code> 和 &lt;code>fromPhyicalDevice&lt;/code> 创建新的对象，那么 &lt;code>Treeland&lt;/code> 只需要继承 &lt;code>QQuickWindow&lt;/code>，并从 &lt;code>wlroots&lt;/code> 获取 OpenGL context 和 phyical device，就可以将 Qt QuickWindow 的渲染，嫁接到 &lt;code>wlroots&lt;/code> 上。&lt;/p>&lt;p>通过将 &lt;code>wlroots&lt;/code> 的渲染上下文与 Qt 的渲染上下文进行结合，可以将 &lt;code>wlroots&lt;/code> 渲染的图形结果嵌入到 Qt 应用程序的渲染流程中，可以直接使用 &lt;code>wlroots&lt;/code> 提供的图形资源和设备对象，如物理设备（&lt;code>phdev&lt;/code>）、逻辑设备（&lt;code>dev&lt;/code>）和队列族（&lt;code>queue_family&lt;/code>），以减少不必要的上下文切换和资源拷贝。这样，Qt 就可以利用 &lt;code>wlroots&lt;/code> 提供的渲染能力，同时能够继续使用 Qt 的渲染框架和 API。&lt;/p>&lt;p>之后在 Qt QPA 中将屏幕信息，以及输入信息转换成 Qt 内部对象，从而利用 Qt 自身的事件循环等机制继续处理。&lt;/p>&lt;h2 id="Qt-QPA">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Qt-QPA" title="Qt QPA">&lt;/a>Qt QPA&lt;/h2>&lt;p>QPA 为 Qt 提供了跨平台的接口抽象能力，我们可以提供自己的 QPA 插件来为 Qt 程序提供新的能力，例如将 &lt;code>wlroots&lt;/code> 的输入事件转换成 Qt 内部事件。&lt;/p>&lt;h3 id="输入事件处理">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#输入事件处理" title="输入事件处理">&lt;/a>输入事件处理&lt;/h3>&lt;img alt="Treeland 处理底层事件与上层事件的流程" src="https://blog.mkacg.com/rss/waylib.png" />&lt;p>Treeland 处理底层事件与上层事件的流程&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">&lt;span class="type">bool&lt;/span> &lt;span class="title">WOutputRenderWindow::event&lt;/span>&lt;span class="params">(QEvent *event)&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_D&lt;/span>(WOutputRenderWindow);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (event-&amp;gt;&lt;span class="built_in">type&lt;/span>() == doRenderEventType) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> QCoreApplication::&lt;span class="built_in">removePostedEvents&lt;/span>(&lt;span class="keyword">this&lt;/span>, doRenderEventType);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">d_func&lt;/span>()-&amp;gt;&lt;span class="built_in">doRender&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (QW::RenderWindow::&lt;span class="built_in">beforeDisposeEventFilter&lt;/span>(&lt;span class="keyword">this&lt;/span>, event)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> event-&amp;gt;&lt;span class="built_in">accept&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> QW::RenderWindow::&lt;span class="built_in">afterDisposeEventFilter&lt;/span>(&lt;span class="keyword">this&lt;/span>, event);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">bool&lt;/span> isAccepted = QQuickWindow::&lt;span class="built_in">event&lt;/span>(event);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (QW::RenderWindow::&lt;span class="built_in">afterDisposeEventFilter&lt;/span>(&lt;span class="keyword">this&lt;/span>, event))&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> isAccepted;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>在 &lt;code>WOutputRenderWindow&lt;/code> 的事件处理中，会额外调用下 seat 的事件过滤器，确保合成器可以拦截掉一部分事件，例如将一部分按键拦截下来，不发送给客户端。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">&lt;span class="type">bool&lt;/span> &lt;span class="title">QWlrootsRenderWindow::beforeDisposeEventFilter&lt;/span>&lt;span class="params">(QEvent *event)&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (event-&amp;gt;&lt;span class="built_in">isInputEvent&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> ie = &lt;span class="built_in">static_cast&lt;/span>&amp;lt;QInputEvent*&amp;gt;(event);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> device = WInputDevice::&lt;span class="built_in">from&lt;/span>(ie-&amp;gt;&lt;span class="built_in">device&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(device);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(device-&amp;gt;&lt;span class="built_in">seat&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> lastActiveCursor = device-&amp;gt;&lt;span class="built_in">seat&lt;/span>()-&amp;gt;&lt;span class="built_in">cursor&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> device-&amp;gt;&lt;span class="built_in">seat&lt;/span>()-&amp;gt;&lt;span class="built_in">filterEventBeforeDisposeStage&lt;/span>(&lt;span class="built_in">window&lt;/span>(), ie);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">false&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>这段代码展示了转换输入设备的功能，判断输入设备的类型，创建对应的 &lt;code>QInputDevice&lt;/code> 对象。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">QPointer&amp;lt;QInputDevice&amp;gt; &lt;span class="title">QWlrootsIntegration::addInputDevice&lt;/span>&lt;span class="params">(WInputDevice *device, &lt;span class="type">const&lt;/span> QString &amp;amp;seatName)&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> QPointer&amp;lt;QInputDevice&amp;gt; qtdev;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> qwDevice = device-&amp;gt;&lt;span class="built_in">handle&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> QString name = QString::&lt;span class="built_in">fromUtf8&lt;/span>(qwDevice-&amp;gt;&lt;span class="built_in">handle&lt;/span>()-&amp;gt;name);&lt;/span>&lt;br />&lt;span class="line"> qint64 systemId = &lt;span class="built_in">reinterpret_cast&lt;/span>&amp;lt;qint64&amp;gt;(device);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">switch&lt;/span> (qwDevice-&amp;gt;&lt;span class="built_in">handle&lt;/span>()-&amp;gt;type) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">case&lt;/span> WLR_INPUT_DEVICE_KEYBOARD: &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> qtdev = &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">QInputDevice&lt;/span>(name, systemId, QInputDevice::DeviceType::Keyboard, seatName);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">break&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">case&lt;/span> WLR_INPUT_DEVICE_POINTER: &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> qtdev = &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">QPointingDevice&lt;/span>(name, systemId, QInputDevice::DeviceType::TouchPad, QPointingDevice::PointerType::Generic,&lt;/span>&lt;br />&lt;span class="line"> QInputDevice::Capability::Position | QInputDevice::Capability::Hover&lt;/span>&lt;br />&lt;span class="line"> | QInputDevice::Capability::Scroll | QInputDevice::Capability::MouseEmulation,&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="number">10&lt;/span>, &lt;span class="number">32&lt;/span>, seatName, &lt;span class="built_in">QPointingDeviceUniqueId&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">break&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">case&lt;/span> WLR_INPUT_DEVICE_TOUCH: &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> qtdev = &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">QPointingDevice&lt;/span>(name, systemId, QInputDevice::DeviceType::TouchScreen, QPointingDevice::PointerType::Finger,&lt;/span>&lt;br />&lt;span class="line"> QInputDevice::Capability::Position | QInputDevice::Capability::Area | QInputDevice::Capability::MouseEmulation,&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="number">10&lt;/span>, &lt;span class="number">32&lt;/span>, seatName, &lt;span class="built_in">QPointingDeviceUniqueId&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">break&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">case&lt;/span> WLR_INPUT_DEVICE_TABLET_TOOL: &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> qtdev = &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">QPointingDevice&lt;/span>(name, systemId, QInputDevice::DeviceType::Stylus, QPointingDevice::PointerType::Pen,&lt;/span>&lt;br />&lt;span class="line"> QInputDevice::Capability::XTilt | QInputDevice::Capability::YTilt | QInputDevice::Capability::Pressure,&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="number">1&lt;/span>, &lt;span class="number">32&lt;/span>, seatName, &lt;span class="built_in">QPointingDeviceUniqueId&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">break&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">case&lt;/span> WLR_INPUT_DEVICE_TABLET_PAD: &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> pad = &lt;span class="built_in">wlr_tablet_pad_from_input_device&lt;/span>(qwDevice-&amp;gt;&lt;span class="built_in">handle&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> qtdev = &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">QPointingDevice&lt;/span>(name, systemId, QInputDevice::DeviceType::TouchPad, QPointingDevice::PointerType::Pen,&lt;/span>&lt;br />&lt;span class="line"> QInputDevice::Capability::Position | QInputDevice::Capability::Hover | QInputDevice::Capability::Pressure,&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="number">1&lt;/span>, pad-&amp;gt;button_count, seatName, &lt;span class="built_in">QPointingDeviceUniqueId&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">break&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">case&lt;/span> WLR_INPUT_DEVICE_SWITCH: &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> qtdev = &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">QInputDevice&lt;/span>(name, systemId, QInputDevice::DeviceType::Keyboard, seatName);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">break&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (qtdev) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> device-&amp;gt;&lt;span class="built_in">setQtDevice&lt;/span>(qtdev);&lt;/span>&lt;br />&lt;span class="line"> QWindowSystemInterface::&lt;span class="built_in">registerInputDevice&lt;/span>(qtdev);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (qtdev-&amp;gt;&lt;span class="built_in">type&lt;/span>() == QInputDevice::DeviceType::Mouse || qtdev-&amp;gt;&lt;span class="built_in">type&lt;/span>() == QInputDevice::DeviceType::TouchPad) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> primaryQtDevice = QPointingDevice::&lt;span class="built_in">primaryPointingDevice&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!WInputDevice::&lt;span class="built_in">from&lt;/span>(primaryQtDevice)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// Ensure the primary pointing device is the WInputDevice&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> pd = &lt;span class="built_in">const_cast&lt;/span>&amp;lt;QPointingDevice*&amp;gt;(primaryQtDevice);&lt;/span>&lt;br />&lt;span class="line"> pd-&amp;gt;&lt;span class="built_in">setParent&lt;/span>(&lt;span class="literal">nullptr&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">delete&lt;/span> pd;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(WInputDevice::&lt;span class="built_in">from&lt;/span>(QPointingDevice::&lt;span class="built_in">primaryPointingDevice&lt;/span>()));&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &lt;span class="keyword">if&lt;/span> (qtdev-&amp;gt;&lt;span class="built_in">type&lt;/span>() == QInputDevice::DeviceType::Keyboard) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> primaryQtDevice = QInputDevice::&lt;span class="built_in">primaryKeyboard&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!WInputDevice::&lt;span class="built_in">from&lt;/span>(primaryQtDevice)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// Ensure the primary keyboard device is the WInputDevice&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> pd = &lt;span class="built_in">const_cast&lt;/span>&amp;lt;QInputDevice*&amp;gt;(primaryQtDevice);&lt;/span>&lt;br />&lt;span class="line"> pd-&amp;gt;&lt;span class="built_in">setParent&lt;/span>(&lt;span class="literal">nullptr&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">delete&lt;/span> pd;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(WInputDevice::&lt;span class="built_in">from&lt;/span>(QInputDevice::&lt;span class="built_in">primaryKeyboard&lt;/span>()));&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> qtdev;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h3 id="客户端事件">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#客户端事件" title="客户端事件">&lt;/a>客户端事件&lt;/h3>&lt;p>在 Treeland 还有一种事件需要处理，当用户点击一个窗口，合成器需要告知客户端哪个坐标点击了。或者使用键盘进行输入时，需要告知客户端输入的内容。&lt;/p>&lt;p>首先，Treeland 会标记一个窗口成为激活窗口，设置给 seat，这样 &lt;code>wlroots&lt;/code> 就知道哪个窗口此时拥有焦点。&lt;/p>&lt;p>之后当键盘发生输入事件时，Treeland 没有过滤掉按键事件，或者是放行某些按键，这些剩余的输入事件就会在 wseat 的 sendEvent 中，发送给激活的客户端。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="comment">// for keyboard event&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;span class="keyword">inline&lt;/span> &lt;span class="type">bool&lt;/span> &lt;span class="title">doNotifyKey&lt;/span>&lt;span class="params">(WInputDevice *device, &lt;span class="type">uint32_t&lt;/span> keycode, &lt;span class="type">uint32_t&lt;/span> state, &lt;span class="type">uint32_t&lt;/span> timestamp)&lt;/span> &lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!&lt;span class="built_in">keyboardFocusSurface&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">false&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">q_func&lt;/span>()-&amp;gt;&lt;span class="built_in">setKeyboard&lt;/span>(device);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">/* Send modifiers to the client. */&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">this&lt;/span>-&amp;gt;&lt;span class="built_in">handle&lt;/span>()-&amp;gt;&lt;span class="built_in">keyboard_notify_key&lt;/span>(timestamp, keycode, state);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h3 id="屏幕信息">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#屏幕信息" title="屏幕信息">&lt;/a>屏幕信息&lt;/h3>&lt;p>在 QPA 中还对 &lt;code>WOutput&lt;/code> 进行了封装 &lt;code>QWlrootsScreen&lt;/code>。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">QWlrootsScreen *&lt;span class="title">QWlrootsIntegration::addScreen&lt;/span>&lt;span class="params">(WOutput *output)&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> m_screens &amp;lt;&amp;lt; &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">QWlrootsScreen&lt;/span>(output);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (&lt;span class="built_in">isMaster&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> QWindowSystemInterface::&lt;span class="built_in">handleScreenAdded&lt;/span>(m_screens.&lt;span class="built_in">last&lt;/span>());&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (m_placeholderScreen) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> QWindowSystemInterface::&lt;span class="built_in">handleScreenRemoved&lt;/span>(m_placeholderScreen.&lt;span class="built_in">release&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_UNUSED&lt;/span>(&lt;span class="keyword">new&lt;/span> &lt;span class="built_in">QScreen&lt;/span>(m_screens.&lt;span class="built_in">last&lt;/span>()))&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> m_screens.&lt;span class="built_in">last&lt;/span>()-&amp;gt;&lt;span class="built_in">initialize&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> output-&amp;gt;&lt;span class="built_in">setScreen&lt;/span>(m_screens.&lt;span class="built_in">last&lt;/span>());&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> m_screens.&lt;span class="built_in">last&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>&lt;code>QWlrootsScreen&lt;/code> 继承自 &lt;code>QPlatformScreen&lt;/code>，做的事情是将部分参数进行转换，例如physicalSize、devicePixelRatio、DPI等，之后通过 &lt;code>QWindowSystemInterface::handleScreenAdded&lt;/code> 将创建好的 &lt;code>QWlrootsScreen&lt;/code> 添加进 Qt 内。&lt;/p>&lt;h2 id="Qt-RHI">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Qt-RHI" title="Qt RHI">&lt;/a>Qt RHI&lt;/h2>&lt;p>摘抄一段来自 waylib 中初始化 Qt RHI 的代码&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">&lt;span class="type">bool&lt;/span> &lt;span class="title">WOutputRenderWindowPrivate::initRCWithRhi&lt;/span>&lt;span class="params">()&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">W_Q&lt;/span>(WOutputRenderWindow);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> QQuickRenderControlPrivate *rcd = QQuickRenderControlPrivate::&lt;span class="built_in">get&lt;/span>(&lt;span class="built_in">rc&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> QSGRhiSupport *rhiSupport = QSGRhiSupport::&lt;span class="built_in">instance&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="comment">// sanity check for Vulkan&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta">#&lt;span class="keyword">ifdef&lt;/span> ENABLE_VULKAN_RENDER&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (rhiSupport-&amp;gt;&lt;span class="built_in">rhiBackend&lt;/span>() == QRhi::Vulkan) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> vkInstance.&lt;span class="built_in">reset&lt;/span>(&lt;span class="keyword">new&lt;/span> &lt;span class="built_in">QVulkanInstance&lt;/span>());&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> phdev = &lt;span class="built_in">wlr_vk_renderer_get_physical_device&lt;/span>(m_renderer-&amp;gt;&lt;span class="built_in">handle&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> dev = &lt;span class="built_in">wlr_vk_renderer_get_device&lt;/span>(m_renderer-&amp;gt;&lt;span class="built_in">handle&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> queue_family = &lt;span class="built_in">wlr_vk_renderer_get_queue_family&lt;/span>(m_renderer-&amp;gt;&lt;span class="built_in">handle&lt;/span>());&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta">#&lt;span class="keyword">if&lt;/span> QT_VERSION &amp;gt; QT_VERSION_CHECK(6, 6, 0)&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> instance = &lt;span class="built_in">wlr_vk_renderer_get_instance&lt;/span>(m_renderer-&amp;gt;&lt;span class="built_in">handle&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> vkInstance-&amp;gt;&lt;span class="built_in">setVkInstance&lt;/span>(instance);&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta">#&lt;span class="keyword">endif&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// vkInstance-&amp;gt;setExtensions(fromCStyleList(vkRendererAttribs.extension_count, vkRendererAttribs.extensions));&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// vkInstance-&amp;gt;setLayers(fromCStyleList(vkRendererAttribs.layer_count, vkRendererAttribs.layers));&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> vkInstance-&amp;gt;&lt;span class="built_in">setApiVersion&lt;/span>(&amp;#123;&lt;span class="number">1&lt;/span>, &lt;span class="number">1&lt;/span>, &lt;span class="number">0&lt;/span>&amp;#125;);&lt;/span>&lt;br />&lt;span class="line"> vkInstance-&amp;gt;&lt;span class="built_in">create&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> q-&amp;gt;&lt;span class="built_in">setVulkanInstance&lt;/span>(vkInstance.&lt;span class="built_in">data&lt;/span>());&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> gd = QQuickGraphicsDevice::&lt;span class="built_in">fromDeviceObjects&lt;/span>(phdev, dev, queue_family);&lt;/span>&lt;br />&lt;span class="line"> q-&amp;gt;&lt;span class="built_in">setGraphicsDevice&lt;/span>(gd);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta">#&lt;span class="keyword">endif&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (rhiSupport-&amp;gt;&lt;span class="built_in">rhiBackend&lt;/span>() == QRhi::OpenGLES2) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(&lt;span class="built_in">wlr_renderer_is_gles2&lt;/span>(m_renderer-&amp;gt;&lt;span class="built_in">handle&lt;/span>()));&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> egl = &lt;span class="built_in">wlr_gles2_renderer_get_egl&lt;/span>(m_renderer-&amp;gt;&lt;span class="built_in">handle&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> display = &lt;span class="built_in">wlr_egl_get_display&lt;/span>(egl);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> context = &lt;span class="built_in">wlr_egl_get_context&lt;/span>(egl);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">this&lt;/span>-&amp;gt;glContext = &lt;span class="keyword">new&lt;/span> QW::&lt;span class="built_in">OpenGLContext&lt;/span>(display, context, &lt;span class="built_in">rc&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">bool&lt;/span> ok = &lt;span class="keyword">this&lt;/span>-&amp;gt;glContext-&amp;gt;&lt;span class="built_in">create&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!ok)&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">false&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> q-&amp;gt;&lt;span class="built_in">setGraphicsDevice&lt;/span>(QQuickGraphicsDevice::&lt;span class="built_in">fromOpenGLContext&lt;/span>(&lt;span class="keyword">this&lt;/span>-&amp;gt;glContext));&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">false&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> QOffscreenSurface *offscreenSurface = &lt;span class="keyword">new&lt;/span> QW::&lt;span class="built_in">OffscreenSurface&lt;/span>(&lt;span class="literal">nullptr&lt;/span>, q);&lt;/span>&lt;br />&lt;span class="line"> offscreenSurface-&amp;gt;&lt;span class="built_in">create&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> QSGRhiSupport::RhiCreateResult result = rhiSupport-&amp;gt;&lt;span class="built_in">createRhi&lt;/span>(q, offscreenSurface);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!result.rhi) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">qWarning&lt;/span>(&lt;span class="string">&amp;quot;WOutput::initRhi: Failed to initialize QRhi&amp;quot;&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">false&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> rcd-&amp;gt;rhi = result.rhi;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// Ensure the QQuickRenderControl don&amp;#x27;t reinit the RHI&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> rcd-&amp;gt;ownRhi = &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!&lt;span class="built_in">rc&lt;/span>()-&amp;gt;&lt;span class="built_in">initialize&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">false&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> rcd-&amp;gt;ownRhi = result.own;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(rcd-&amp;gt;rhi == result.rhi);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(!swapchain);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>先获取 &lt;code>QSGRhiSupport&lt;/code> 及相关控制对象。&lt;/p>&lt;p>判断 RHI backend 的类型，需要适配 &lt;code>vulkan&lt;/code>、&lt;code>gles&lt;/code>等。&lt;/p>&lt;p>从 &lt;code>wlroots&lt;/code> 获取物理设备等参数，使用 &lt;code>QQuickGraphicsDevice::fromDeviceObjects&lt;/code> 创建 Qt 的 &lt;code>QQuickGraphicsDevice&lt;/code>。&lt;/p>&lt;p>render window的私有类是继承自 &lt;code>QQuickWindowPrivate&lt;/code>，只需要将获取到的 &lt;code>QQuickGraphicsDevice&lt;/code> 设置给 &lt;code>QQuickWindowPrivate::setGraphicsDevice&lt;/code> 即可。&lt;/p>&lt;p>之后创建一个离屏渲染表面，用于 RHI 的初始化。&lt;/p>&lt;h2 id="Qt-Viewport">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Qt-Viewport" title="Qt Viewport">&lt;/a>Qt Viewport&lt;/h2>&lt;p>在 Qt 中，想要查看或者渲染一个组件，需要使用 Viewport 组件，俗称照相机。&lt;/p>&lt;p>视口（Viewport）是一个可观察的多边形区域，只有 Viewport 范围内的画面才能显示到屏幕上。&lt;/p>&lt;p>&lt;code>wlroots&lt;/code> 中的 &lt;strong>Viewport&lt;/strong> 是一个与 &lt;code>Wayland&lt;/code> 显示协议相关的概念，主要用于定义渲染输出在屏幕上的显示区域。它允许在渲染时对显示内容进行缩放、裁剪或平移，以适应不同的分辨率和显示需求。&lt;/p>&lt;p>Treeland 使用 &lt;code>WOutputViewport&lt;/code> 提供 Viewport 功能，使用 &lt;code>wlroots&lt;/code> 的 &lt;code>wlr_output&lt;/code> 中的屏幕信息，对画面进行矩阵变换，这里会涉及到屏幕的缩放、DPI等参数。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">QMatrix4x4 &lt;span class="title">WOutputViewport::renderMatrix&lt;/span>&lt;span class="params">()&lt;/span> &lt;span class="type">const&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> QMatrix4x4 renderMatrix;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (&lt;span class="keyword">auto&lt;/span> customTransform = &lt;span class="built_in">viewportTransform&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> customTransform-&amp;gt;&lt;span class="built_in">applyTo&lt;/span>(&amp;amp;renderMatrix);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &lt;span class="keyword">if&lt;/span> (&lt;span class="built_in">parentItem&lt;/span>() &amp;amp;&amp;amp; !&lt;span class="built_in">ignoreViewport&lt;/span>() &amp;amp;&amp;amp; &lt;span class="built_in">input&lt;/span>() != &lt;span class="keyword">this&lt;/span>) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> d = QQuickItemPrivate::&lt;span class="built_in">get&lt;/span>(&lt;span class="built_in">const_cast&lt;/span>&amp;lt;WOutputViewport*&amp;gt;(&lt;span class="keyword">this&lt;/span>));&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> viewportMatrix = d-&amp;gt;&lt;span class="built_in">itemNode&lt;/span>()-&amp;gt;&lt;span class="built_in">matrix&lt;/span>().&lt;span class="built_in">inverted&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (&lt;span class="keyword">auto&lt;/span> inputItem = &lt;span class="built_in">input&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> QMatrix4x4 matrix = QQuickItemPrivate::&lt;span class="built_in">get&lt;/span>(&lt;span class="built_in">parentItem&lt;/span>())-&amp;gt;&lt;span class="built_in">itemToWindowTransform&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> matrix *= QQuickItemPrivate::&lt;span class="built_in">get&lt;/span>(inputItem)-&amp;gt;&lt;span class="built_in">windowToItemTransform&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> renderMatrix = viewportMatrix * matrix.&lt;span class="built_in">inverted&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123; &lt;span class="comment">// the input item is window&amp;#x27;s contentItem&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> pd = QQuickItemPrivate::&lt;span class="built_in">get&lt;/span>(&lt;span class="built_in">parentItem&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> QMatrix4x4 parentMatrix = pd-&amp;gt;&lt;span class="built_in">itemToWindowTransform&lt;/span>().&lt;span class="built_in">inverted&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> renderMatrix = viewportMatrix * parentMatrix;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> renderMatrix;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>&lt;code>WOutputViewport&lt;/code> 提供了 Viewport 所需的所有参数，变换矩阵、源几何大小、目标几何大小等信息。&lt;/p>&lt;p>在 &lt;code>WOutputRenderWindow&lt;/code> 的事件中，判断如果是渲染的事件，就执行渲染。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">&lt;span class="type">bool&lt;/span> &lt;span class="title">WOutputRenderWindow::event&lt;/span>&lt;span class="params">(QEvent *event)&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_D&lt;/span>(WOutputRenderWindow);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (event-&amp;gt;&lt;span class="built_in">type&lt;/span>() == doRenderEventType) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> QCoreApplication::&lt;span class="built_in">removePostedEvents&lt;/span>(&lt;span class="keyword">this&lt;/span>, doRenderEventType);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">d_func&lt;/span>()-&amp;gt;&lt;span class="built_in">doRender&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (QW::RenderWindow::&lt;span class="built_in">beforeDisposeEventFilter&lt;/span>(&lt;span class="keyword">this&lt;/span>, event)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> event-&amp;gt;&lt;span class="built_in">accept&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> QW::RenderWindow::&lt;span class="built_in">afterDisposeEventFilter&lt;/span>(&lt;span class="keyword">this&lt;/span>, event);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">bool&lt;/span> isAccepted = QQuickWindow::&lt;span class="built_in">event&lt;/span>(event);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (QW::RenderWindow::&lt;span class="built_in">afterDisposeEventFilter&lt;/span>(&lt;span class="keyword">this&lt;/span>, event))&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> isAccepted;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>在 doRender 中，遍历所有的 Output，执行 beginRender，然后执行 Output 的渲染。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">&lt;span class="type">void&lt;/span> &lt;span class="title">WOutputRenderWindowPrivate::doRender&lt;/span>&lt;span class="params">(&lt;span class="type">const&lt;/span> QList&amp;lt;OutputHelper *&amp;gt; &amp;amp;outputs,&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="params">&lt;span class="function"> &lt;span class="type">bool&lt;/span> forceRender, &lt;span class="type">bool&lt;/span> doCommit)&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(rendererList.&lt;span class="built_in">isEmpty&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(!inRendering);&lt;/span>&lt;br />&lt;span class="line"> inRendering = &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">W_Q&lt;/span>(WOutputRenderWindow);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">for&lt;/span> (OutputLayer *layer : std::&lt;span class="built_in">as_const&lt;/span>(layers)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> layer-&amp;gt;&lt;span class="built_in">beforeRender&lt;/span>(q);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">rc&lt;/span>()-&amp;gt;&lt;span class="built_in">polishItems&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (QSGRendererInterface::&lt;span class="built_in">isApiRhiBased&lt;/span>(WRenderHelper::&lt;span class="built_in">getGraphicsApi&lt;/span>()))&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">rc&lt;/span>()-&amp;gt;&lt;span class="built_in">beginFrame&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">rc&lt;/span>()-&amp;gt;&lt;span class="built_in">sync&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">QQuickAnimatorController_advance&lt;/span>(animationController.&lt;span class="built_in">get&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> Q_EMIT q-&amp;gt;&lt;span class="built_in">beforeRendering&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">runAndClearJobs&lt;/span>(&amp;amp;beforeRenderingJobs);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> needsCommit = &lt;span class="built_in">doRenderOutputs&lt;/span>(outputs, forceRender);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> Q_EMIT q-&amp;gt;&lt;span class="built_in">afterRendering&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">runAndClearJobs&lt;/span>(&amp;amp;afterRenderingJobs);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (QSGRendererInterface::&lt;span class="built_in">isApiRhiBased&lt;/span>(WRenderHelper::&lt;span class="built_in">getGraphicsApi&lt;/span>()))&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">rc&lt;/span>()-&amp;gt;&lt;span class="built_in">endFrame&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (doCommit) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">for&lt;/span> (&lt;span class="keyword">auto&lt;/span> i : std::&lt;span class="built_in">as_const&lt;/span>(needsCommit)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">bool&lt;/span> ok = i.first-&amp;gt;&lt;span class="built_in">commit&lt;/span>(i.second);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (i.second-&amp;gt;&lt;span class="built_in">currentBuffer&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> i.second-&amp;gt;&lt;span class="built_in">endRender&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> i.first-&amp;gt;&lt;span class="built_in">resetState&lt;/span>(ok);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">resetGlState&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// On Intel&amp;amp;Nvidia multi-GPU environment, wlroots using Intel card do render for all&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// outputs, and blit nvidia&amp;#x27;s output buffer in drm_connector_state_update_primary_fb,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// the &amp;#x27;blit&amp;#x27; behavior will make EGL context to Nvidia renderer. So must done current&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// OpenGL context here in order to ensure QtQuick always make EGL context to Intel&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// renderer before next frame.&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (glContext)&lt;/span>&lt;br />&lt;span class="line"> glContext-&amp;gt;&lt;span class="built_in">doneCurrent&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> inRendering = &lt;span class="literal">false&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> Q_EMIT q-&amp;gt;&lt;span class="built_in">renderEnd&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">qw_buffer *&lt;span class="title">WBufferRenderer::beginRender&lt;/span>&lt;span class="params">(&lt;span class="type">const&lt;/span> QSize &amp;amp;pixelSize, qreal devicePixelRatio,&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="params">&lt;span class="function"> &lt;span class="type">uint32_t&lt;/span> format, RenderFlags flags)&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(!state.buffer);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(m_output);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (pixelSize.&lt;span class="built_in">isEmpty&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">nullptr&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="function">Q_EMIT &lt;span class="title">beforeRendering&lt;/span>&lt;span class="params">()&lt;/span>&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> m_damageRing.&lt;span class="built_in">set_bounds&lt;/span>(pixelSize.&lt;span class="built_in">width&lt;/span>(), pixelSize.&lt;span class="built_in">height&lt;/span>());&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// configure swapchain&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (flags.&lt;span class="built_in">testFlag&lt;/span>(RenderFlag::DontConfigureSwapchain)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> renderFormat = &lt;span class="built_in">pickFormat&lt;/span>(m_output-&amp;gt;&lt;span class="built_in">renderer&lt;/span>(), format);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!renderFormat) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">qWarning&lt;/span>(&lt;span class="string">&amp;quot;wlr_renderer doesn&amp;#x27;t support format 0x%s&amp;quot;&lt;/span>, &lt;span class="built_in">drmGetFormatName&lt;/span>(format));&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">nullptr&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!m_swapchain || &lt;span class="built_in">QSize&lt;/span>(m_swapchain-&amp;gt;&lt;span class="built_in">handle&lt;/span>()-&amp;gt;width, m_swapchain-&amp;gt;&lt;span class="built_in">handle&lt;/span>()-&amp;gt;height) != pixelSize&lt;/span>&lt;br />&lt;span class="line"> || m_swapchain-&amp;gt;&lt;span class="built_in">handle&lt;/span>()-&amp;gt;format.format != renderFormat-&amp;gt;format) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (m_swapchain)&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">delete&lt;/span> m_swapchain;&lt;/span>&lt;br />&lt;span class="line"> m_swapchain = qw_swapchain::&lt;span class="built_in">create&lt;/span>(m_output-&amp;gt;&lt;span class="built_in">allocator&lt;/span>()-&amp;gt;&lt;span class="built_in">handle&lt;/span>(), pixelSize.&lt;span class="built_in">width&lt;/span>(), pixelSize.&lt;span class="built_in">height&lt;/span>(), renderFormat);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &lt;span class="keyword">if&lt;/span> (flags.&lt;span class="built_in">testFlag&lt;/span>(RenderFlag::UseCursorFormats)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">bool&lt;/span> ok = m_output-&amp;gt;&lt;span class="built_in">configureCursorSwapchain&lt;/span>(pixelSize, format, &amp;amp;m_swapchain);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!ok)&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">nullptr&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">bool&lt;/span> ok = m_output-&amp;gt;&lt;span class="built_in">configurePrimarySwapchain&lt;/span>(pixelSize, format, &amp;amp;m_swapchain,&lt;/span>&lt;br />&lt;span class="line"> !flags.&lt;span class="built_in">testFlag&lt;/span>(DontTestSwapchain));&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!ok)&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">nullptr&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// &lt;span class="doctag">TODO:&lt;/span> Support scanout buffer of wlr_surface(from WSurfaceItem)&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">int&lt;/span> bufferAge;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> wbuffer = m_swapchain-&amp;gt;&lt;span class="built_in">acquire&lt;/span>(&amp;amp;bufferAge);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!wbuffer)&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">nullptr&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> buffer = qw_buffer::&lt;span class="built_in">from&lt;/span>(wbuffer);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!m_renderHelper)&lt;/span>&lt;br />&lt;span class="line"> m_renderHelper = &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">WRenderHelper&lt;/span>(m_output-&amp;gt;&lt;span class="built_in">renderer&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> m_renderHelper-&amp;gt;&lt;span class="built_in">setSize&lt;/span>(pixelSize);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> wd = QQuickWindowPrivate::&lt;span class="built_in">get&lt;/span>(&lt;span class="built_in">window&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(wd-&amp;gt;renderControl);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> lastRT = m_renderHelper-&amp;gt;&lt;span class="built_in">lastRenderTarget&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> rt = m_renderHelper-&amp;gt;&lt;span class="built_in">acquireRenderTarget&lt;/span>(wd-&amp;gt;renderControl, buffer);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (rt.&lt;span class="built_in">isNull&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> buffer-&amp;gt;&lt;span class="built_in">unlock&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">nullptr&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> rtd = QQuickRenderTargetPrivate::&lt;span class="built_in">get&lt;/span>(&amp;amp;rt);&lt;/span>&lt;br />&lt;span class="line"> QSGRenderTarget sgRT;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (rtd-&amp;gt;type == QQuickRenderTargetPrivate::Type::PaintDevice) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> sgRT.paintDevice = rtd-&amp;gt;u.paintDevice;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(rtd-&amp;gt;type == QQuickRenderTargetPrivate::Type::RhiRenderTarget);&lt;/span>&lt;br />&lt;span class="line"> sgRT.rt = rtd-&amp;gt;u.rhiRt;&lt;/span>&lt;br />&lt;span class="line"> sgRT.cb = wd-&amp;gt;redirect.commandBuffer;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(sgRT.cb);&lt;/span>&lt;br />&lt;span class="line"> sgRT.rpDesc = rtd-&amp;gt;u.rhiRt-&amp;gt;&lt;span class="built_in">renderPassDescriptor&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta">#&lt;span class="keyword">ifndef&lt;/span> QT_NO_OPENGL&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (wd-&amp;gt;rhi-&amp;gt;&lt;span class="built_in">backend&lt;/span>() == QRhi::OpenGLES2) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> glRT = &lt;span class="built_in">QRHI_RES&lt;/span>(QGles2TextureRenderTarget, rtd-&amp;gt;u.rhiRt);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(glRT-&amp;gt;framebuffer &amp;gt;= &lt;span class="number">0&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> glContext = QOpenGLContext::&lt;span class="built_in">currentContext&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(glContext);&lt;/span>&lt;br />&lt;span class="line"> QOpenGLContextPrivate::&lt;span class="built_in">get&lt;/span>(glContext)-&amp;gt;defaultFboRedirect = glRT-&amp;gt;framebuffer;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta">#&lt;span class="keyword">endif&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> state.flags = flags;&lt;/span>&lt;br />&lt;span class="line"> state.context = wd-&amp;gt;context;&lt;/span>&lt;br />&lt;span class="line"> state.pixelSize = pixelSize;&lt;/span>&lt;br />&lt;span class="line"> state.devicePixelRatio = devicePixelRatio;&lt;/span>&lt;br />&lt;span class="line"> state.bufferAge = bufferAge;&lt;/span>&lt;br />&lt;span class="line"> state.lastRT = lastRT;&lt;/span>&lt;br />&lt;span class="line"> state.buffer = buffer;&lt;/span>&lt;br />&lt;span class="line"> state.renderTarget = rt;&lt;/span>&lt;br />&lt;span class="line"> state.sgRenderTarget = sgRT;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> buffer;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">QVector&amp;lt;std::pair&amp;lt;OutputHelper*, WBufferRenderer*&amp;gt;&amp;gt;&lt;/span>&lt;br />&lt;span class="line">WOutputRenderWindowPrivate::&lt;span class="built_in">doRenderOutputs&lt;/span>(&lt;span class="type">const&lt;/span> QList&amp;lt;OutputHelper*&amp;gt; &amp;amp;outputs, &lt;span class="type">bool&lt;/span> forceRender)&lt;/span>&lt;br />&lt;span class="line">&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> QVector&amp;lt;OutputHelper*&amp;gt; renderResults;&lt;/span>&lt;br />&lt;span class="line"> renderResults.&lt;span class="built_in">reserve&lt;/span>(outputs.&lt;span class="built_in">size&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">for&lt;/span> (OutputHelper *helper : std::&lt;span class="built_in">as_const&lt;/span>(outputs)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (&lt;span class="built_in">Q_LIKELY&lt;/span>(!forceRender)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!helper-&amp;gt;&lt;span class="built_in">renderable&lt;/span>()&lt;/span>&lt;br />&lt;span class="line"> || &lt;span class="built_in">Q_UNLIKELY&lt;/span>(!WOutputViewportPrivate::&lt;span class="built_in">get&lt;/span>(helper-&amp;gt;&lt;span class="built_in">output&lt;/span>())-&amp;gt;&lt;span class="built_in">renderable&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> || !helper-&amp;gt;&lt;span class="built_in">output&lt;/span>()-&amp;gt;&lt;span class="built_in">output&lt;/span>()-&amp;gt;&lt;span class="built_in">isEnabled&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">continue&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!helper-&amp;gt;&lt;span class="built_in">contentIsDirty&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (helper-&amp;gt;&lt;span class="built_in">needsFrame&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> renderResults.&lt;span class="built_in">append&lt;/span>(helper);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">continue&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(helper-&amp;gt;&lt;span class="built_in">output&lt;/span>()-&amp;gt;&lt;span class="built_in">output&lt;/span>()-&amp;gt;&lt;span class="built_in">scale&lt;/span>() &amp;lt;= helper-&amp;gt;&lt;span class="built_in">output&lt;/span>()-&amp;gt;&lt;span class="built_in">devicePixelRatio&lt;/span>());&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> &lt;span class="keyword">auto&lt;/span> &amp;amp;format = helper-&amp;gt;&lt;span class="built_in">qwoutput&lt;/span>()-&amp;gt;&lt;span class="built_in">handle&lt;/span>()-&amp;gt;render_format;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> &lt;span class="keyword">auto&lt;/span> renderMatrix = helper-&amp;gt;&lt;span class="built_in">output&lt;/span>()-&amp;gt;&lt;span class="built_in">renderMatrix&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// maybe using the other WOutputViewport&amp;#x27;s QSGTextureProvider&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!helper-&amp;gt;&lt;span class="built_in">output&lt;/span>()-&amp;gt;&lt;span class="built_in">depends&lt;/span>().&lt;span class="built_in">isEmpty&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">updateDirtyNodes&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> qw_buffer *buffer = helper-&amp;gt;&lt;span class="built_in">beginRender&lt;/span>(helper-&amp;gt;&lt;span class="built_in">bufferRenderer&lt;/span>(), helper-&amp;gt;&lt;span class="built_in">output&lt;/span>()-&amp;gt;&lt;span class="built_in">output&lt;/span>()-&amp;gt;&lt;span class="built_in">size&lt;/span>(), format,&lt;/span>&lt;br />&lt;span class="line"> WBufferRenderer::RedirectOpenGLContextDefaultFrameBufferObject);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(buffer == helper-&amp;gt;&lt;span class="built_in">bufferRenderer&lt;/span>()-&amp;gt;&lt;span class="built_in">currentBuffer&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (buffer) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> helper-&amp;gt;&lt;span class="built_in">render&lt;/span>(helper-&amp;gt;&lt;span class="built_in">bufferRenderer&lt;/span>(), &lt;span class="number">0&lt;/span>, renderMatrix,&lt;/span>&lt;br />&lt;span class="line"> helper-&amp;gt;&lt;span class="built_in">output&lt;/span>()-&amp;gt;&lt;span class="built_in">effectiveSourceRect&lt;/span>(),&lt;/span>&lt;br />&lt;span class="line"> helper-&amp;gt;&lt;span class="built_in">output&lt;/span>()-&amp;gt;&lt;span class="built_in">targetRect&lt;/span>(),&lt;/span>&lt;br />&lt;span class="line"> helper-&amp;gt;&lt;span class="built_in">output&lt;/span>()-&amp;gt;&lt;span class="built_in">preserveColorContents&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> renderResults.&lt;span class="built_in">append&lt;/span>(helper);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> QVector&amp;lt;std::pair&amp;lt;OutputHelper*, WBufferRenderer*&amp;gt;&amp;gt; needsCommit;&lt;/span>&lt;br />&lt;span class="line"> needsCommit.&lt;span class="built_in">reserve&lt;/span>(renderResults.&lt;span class="built_in">size&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">for&lt;/span> (&lt;span class="keyword">auto&lt;/span> helper : std::&lt;span class="built_in">as_const&lt;/span>(renderResults)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> bufferRenderer = helper-&amp;gt;&lt;span class="built_in">afterRender&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (bufferRenderer)&lt;/span>&lt;br />&lt;span class="line"> needsCommit.&lt;span class="built_in">append&lt;/span>(&amp;#123;helper, bufferRenderer&amp;#125;);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> rendererList.&lt;span class="built_in">clear&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> needsCommit;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">&lt;span class="type">void&lt;/span> &lt;span class="title">WBufferRenderer::render&lt;/span>&lt;span class="params">(&lt;span class="type">int&lt;/span> sourceIndex, &lt;span class="type">const&lt;/span> QMatrix4x4 &amp;amp;renderMatrix,&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="params">&lt;span class="function"> &lt;span class="type">const&lt;/span> QRectF &amp;amp;sourceRect, &lt;span class="type">const&lt;/span> QRectF &amp;amp;targetRect,&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="params">&lt;span class="function"> &lt;span class="type">bool&lt;/span> preserveColorContents)&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(state.buffer);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> &lt;span class="keyword">auto&lt;/span> &amp;amp;source = m_sourceList.&lt;span class="built_in">at&lt;/span>(sourceIndex);&lt;/span>&lt;br />&lt;span class="line"> QSGRenderer *renderer = &lt;span class="built_in">ensureRenderer&lt;/span>(sourceIndex, state.context);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> wd = QQuickWindowPrivate::&lt;span class="built_in">get&lt;/span>(&lt;span class="built_in">window&lt;/span>());&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> qreal devicePixelRatio = state.devicePixelRatio;&lt;/span>&lt;br />&lt;span class="line"> state.renderer = renderer;&lt;/span>&lt;br />&lt;span class="line"> state.worldTransform = renderMatrix;&lt;/span>&lt;br />&lt;span class="line"> renderer-&amp;gt;&lt;span class="built_in">setDevicePixelRatio&lt;/span>(devicePixelRatio);&lt;/span>&lt;br />&lt;span class="line"> renderer-&amp;gt;&lt;span class="built_in">setDeviceRect&lt;/span>(&lt;span class="built_in">QRect&lt;/span>(&lt;span class="built_in">QPoint&lt;/span>(&lt;span class="number">0&lt;/span>, &lt;span class="number">0&lt;/span>), state.pixelSize));&lt;/span>&lt;br />&lt;span class="line"> renderer-&amp;gt;&lt;span class="built_in">setRenderTarget&lt;/span>(state.sgRenderTarget);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> &lt;span class="keyword">auto&lt;/span> viewportRect = &lt;span class="built_in">scaleToRect&lt;/span>(targetRect, devicePixelRatio);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> softwareRenderer = &lt;span class="built_in">dynamic_cast&lt;/span>&amp;lt;QSGSoftwareRenderer*&amp;gt;(renderer);&lt;/span>&lt;br />&lt;span class="line"> &amp;#123; &lt;span class="comment">// before render&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (softwareRenderer) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// because software renderer don&amp;#x27;t supports viewportRect,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// so use transform to simulation.&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> &lt;span class="keyword">auto&lt;/span> mapTransform = &lt;span class="built_in">inputMapToOutput&lt;/span>(sourceRect, targetRect,&lt;/span>&lt;br />&lt;span class="line"> state.pixelSize, state.devicePixelRatio);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!mapTransform.&lt;span class="built_in">isIdentity&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> state.worldTransform = mapTransform * state.worldTransform;&lt;/span>&lt;br />&lt;span class="line"> state.worldTransform.&lt;span class="built_in">optimize&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> image = &lt;span class="built_in">getImageFrom&lt;/span>(state.renderTarget);&lt;/span>&lt;br />&lt;span class="line"> image-&amp;gt;&lt;span class="built_in">setDevicePixelRatio&lt;/span>(devicePixelRatio);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// &lt;span class="doctag">TODO:&lt;/span> Should set to QSGSoftwareRenderer, but it&amp;#x27;s not support specify matrix.&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// If transform is changed, it will full repaint.&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (&lt;span class="built_in">isRootItem&lt;/span>(source.source)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> rootTransform = QQuickItemPrivate::&lt;span class="built_in">get&lt;/span>(wd-&amp;gt;contentItem)-&amp;gt;&lt;span class="built_in">itemNode&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (rootTransform-&amp;gt;&lt;span class="built_in">matrix&lt;/span>() != state.worldTransform)&lt;/span>&lt;br />&lt;span class="line"> rootTransform-&amp;gt;&lt;span class="built_in">setMatrix&lt;/span>(state.worldTransform);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> t = state.worldTransform.&lt;span class="built_in">toTransform&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (t.&lt;span class="built_in">type&lt;/span>() &amp;gt; QTransform::TxTranslate) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> (image-&amp;gt;&lt;span class="keyword">operator&lt;/span> QImage &amp;amp;()).&lt;span class="built_in">fill&lt;/span>(renderer-&amp;gt;&lt;span class="built_in">clearColor&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> softwareRenderer-&amp;gt;&lt;span class="built_in">markDirty&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">applyTransform&lt;/span>(softwareRenderer, t);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> state.worldTransform.&lt;span class="built_in">optimize&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">bool&lt;/span> flipY = wd-&amp;gt;rhi ? !wd-&amp;gt;rhi-&amp;gt;&lt;span class="built_in">isYUpInNDC&lt;/span>() : &lt;span class="literal">false&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (state.renderTarget.&lt;span class="built_in">mirrorVertically&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> flipY = !flipY;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (viewportRect.&lt;span class="built_in">isValid&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> QRect vr = viewportRect;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (flipY)&lt;/span>&lt;br />&lt;span class="line"> vr.&lt;span class="built_in">moveTop&lt;/span>(-vr.&lt;span class="built_in">y&lt;/span>() + state.pixelSize.&lt;span class="built_in">height&lt;/span>() - vr.&lt;span class="built_in">height&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> renderer-&amp;gt;&lt;span class="built_in">setViewportRect&lt;/span>(vr);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> renderer-&amp;gt;&lt;span class="built_in">setViewportRect&lt;/span>(&lt;span class="built_in">QRect&lt;/span>(&lt;span class="built_in">QPoint&lt;/span>(&lt;span class="number">0&lt;/span>, &lt;span class="number">0&lt;/span>), state.pixelSize));&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> QRectF rect = sourceRect;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!rect.&lt;span class="built_in">isValid&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> rect = &lt;span class="built_in">QRectF&lt;/span>(&lt;span class="built_in">QPointF&lt;/span>(&lt;span class="number">0&lt;/span>, &lt;span class="number">0&lt;/span>), &lt;span class="built_in">QSizeF&lt;/span>(state.pixelSize) / devicePixelRatio);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> &lt;span class="type">float&lt;/span> left = rect.&lt;span class="built_in">x&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> &lt;span class="type">float&lt;/span> right = rect.&lt;span class="built_in">x&lt;/span>() + rect.&lt;span class="built_in">width&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">float&lt;/span> bottom = rect.&lt;span class="built_in">y&lt;/span>() + rect.&lt;span class="built_in">height&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">float&lt;/span> top = rect.&lt;span class="built_in">y&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (flipY)&lt;/span>&lt;br />&lt;span class="line"> std::&lt;span class="built_in">swap&lt;/span>(top, bottom);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> QMatrix4x4 matrix;&lt;/span>&lt;br />&lt;span class="line"> matrix.&lt;span class="built_in">ortho&lt;/span>(left, right, bottom, top, &lt;span class="number">1&lt;/span>, &lt;span class="number">-1&lt;/span>);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> QMatrix4x4 projectionMatrix, projectionMatrixWithNativeNDC;&lt;/span>&lt;br />&lt;span class="line"> projectionMatrix = matrix * state.worldTransform;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (wd-&amp;gt;rhi &amp;amp;&amp;amp; !wd-&amp;gt;rhi-&amp;gt;&lt;span class="built_in">isYUpInNDC&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> std::&lt;span class="built_in">swap&lt;/span>(top, bottom);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> matrix.&lt;span class="built_in">setToIdentity&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> matrix.&lt;span class="built_in">ortho&lt;/span>(left, right, bottom, top, &lt;span class="number">1&lt;/span>, &lt;span class="number">-1&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> projectionMatrixWithNativeNDC = matrix * state.worldTransform;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> renderer-&amp;gt;&lt;span class="built_in">setProjectionMatrix&lt;/span>(projectionMatrix);&lt;/span>&lt;br />&lt;span class="line"> renderer-&amp;gt;&lt;span class="built_in">setProjectionMatrixWithNativeNDC&lt;/span>(projectionMatrixWithNativeNDC);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> textureRT = &lt;span class="built_in">static_cast&lt;/span>&amp;lt;QRhiTextureRenderTarget*&amp;gt;(state.sgRenderTarget.rt);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (preserveColorContents) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> textureRT-&amp;gt;&lt;span class="built_in">setFlags&lt;/span>(textureRT-&amp;gt;&lt;span class="built_in">flags&lt;/span>() | QRhiTextureRenderTarget::PreserveColorContents);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> textureRT-&amp;gt;&lt;span class="built_in">setFlags&lt;/span>(textureRT-&amp;gt;&lt;span class="built_in">flags&lt;/span>() &amp;amp; ~QRhiTextureRenderTarget::PreserveColorContents);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> state.context-&amp;gt;&lt;span class="built_in">renderNextFrame&lt;/span>(renderer);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &amp;#123; &lt;span class="comment">// after render&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!softwareRenderer) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// &lt;span class="doctag">TODO:&lt;/span> get damage area from QRhi renderer&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> m_damageRing.&lt;span class="built_in">add_whole&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// ###: maybe Qt bug? Before executing QRhi::endOffscreenFrame, we may&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// use the same QSGRenderer for multiple drawings. This can lead to&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// rendering the same content for different QSGRhiRenderTarget instances&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// when using the RhiGles backend. Additionally, considering that the&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// result of the current drawing may be needed when drawing the next&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// sourceIndex, we should let the RHI (Rendering Hardware Interface)&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// complete the results of this drawing here to ensure the current&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// drawing result is available for use.&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> wd-&amp;gt;rhi-&amp;gt;&lt;span class="built_in">finish&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> currentImage = &lt;span class="built_in">getImageFrom&lt;/span>(state.renderTarget);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(currentImage &amp;amp;&amp;amp; currentImage == softwareRenderer-&amp;gt;m_rt.paintDevice);&lt;/span>&lt;br />&lt;span class="line"> currentImage-&amp;gt;&lt;span class="built_in">setDevicePixelRatio&lt;/span>(&lt;span class="number">1.0&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> &lt;span class="keyword">auto&lt;/span> scaleTF = QTransform::&lt;span class="built_in">fromScale&lt;/span>(devicePixelRatio, devicePixelRatio);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> &lt;span class="keyword">auto&lt;/span> scaledFlushRegion = scaleTF.&lt;span class="built_in">map&lt;/span>(softwareRenderer-&amp;gt;&lt;span class="built_in">flushRegion&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> PixmanRegion scaledFlushDamage;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">bool&lt;/span> ok = WTools::&lt;span class="built_in">toPixmanRegion&lt;/span>(scaledFlushRegion, scaledFlushDamage);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(ok);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> PixmanRegion damage;&lt;/span>&lt;br />&lt;span class="line"> m_damageRing.&lt;span class="built_in">get_buffer_damage&lt;/span>(state.bufferAge, damage);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (viewportRect.&lt;span class="built_in">isValid&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> QRect imageRect = (currentImage-&amp;gt;&lt;span class="keyword">operator&lt;/span> &lt;span class="type">const&lt;/span> QImage &amp;amp;()).&lt;span class="built_in">rect&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="function">QRegion &lt;span class="title">invalidRegion&lt;/span>&lt;span class="params">(imageRect)&lt;/span>&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> invalidRegion -= viewportRect;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!scaledFlushRegion.&lt;span class="built_in">isEmpty&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> invalidRegion &amp;amp;= scaledFlushRegion;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!invalidRegion.&lt;span class="built_in">isEmpty&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="function">QPainter &lt;span class="title">pa&lt;/span>&lt;span class="params">(currentImage)&lt;/span>&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">for&lt;/span> (&lt;span class="type">const&lt;/span> &lt;span class="keyword">auto&lt;/span> r : std::&lt;span class="built_in">as_const&lt;/span>(invalidRegion))&lt;/span>&lt;br />&lt;span class="line"> pa.&lt;span class="built_in">fillRect&lt;/span>(r, softwareRenderer-&amp;gt;&lt;span class="built_in">clearColor&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!damage.&lt;span class="built_in">isEmpty&lt;/span>() &amp;amp;&amp;amp; state.lastRT.first != state.buffer &amp;amp;&amp;amp; !state.lastRT.second.&lt;span class="built_in">isNull&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> image = &lt;span class="built_in">getImageFrom&lt;/span>(state.lastRT.second);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(image);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(image-&amp;gt;&lt;span class="built_in">size&lt;/span>() == state.pixelSize);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// &lt;span class="doctag">TODO:&lt;/span> Don&amp;#x27;t use the previous render target, we can get the damage region of QtQuick&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// before QQuickRenderControl::render for qw_damage_ring, and add dirty region to&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// QSGAbstractSoftwareRenderer to force repaint the damage region of current render target.&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="function">QPainter &lt;span class="title">pa&lt;/span>&lt;span class="params">(currentImage)&lt;/span>&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> PixmanRegion remainderDamage;&lt;/span>&lt;br />&lt;span class="line"> ok = &lt;span class="built_in">pixman_region32_subtract&lt;/span>(remainderDamage, damage, scaledFlushDamage);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(ok);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">int&lt;/span> count = &lt;span class="number">0&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> rects = &lt;span class="built_in">pixman_region32_rectangles&lt;/span>(remainderDamage, &amp;amp;count);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">for&lt;/span> (&lt;span class="type">int&lt;/span> i = &lt;span class="number">0&lt;/span>; i &amp;lt; count; ++i) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> r = rects[i];&lt;/span>&lt;br />&lt;span class="line"> pa.&lt;span class="built_in">drawImage&lt;/span>(r.x1, r.y1, *image, r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!&lt;span class="built_in">isRootItem&lt;/span>(source.source))&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">applyTransform&lt;/span>(softwareRenderer, state.worldTransform.&lt;span class="built_in">inverted&lt;/span>().&lt;span class="built_in">toTransform&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> m_damageRing.&lt;span class="built_in">add&lt;/span>(scaledFlushDamage);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (&lt;span class="keyword">auto&lt;/span> dr = &lt;span class="built_in">qobject_cast&lt;/span>&amp;lt;QSGDefaultRenderContext*&amp;gt;(state.context)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> QRhiResourceUpdateBatch *resourceUpdates = wd-&amp;gt;rhi-&amp;gt;&lt;span class="built_in">nextResourceUpdateBatch&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> dr-&amp;gt;&lt;span class="built_in">currentFrameCommandBuffer&lt;/span>()-&amp;gt;&lt;span class="built_in">resourceUpdate&lt;/span>(resourceUpdates);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (&lt;span class="built_in">shouldCacheBuffer&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">wTextureProvider&lt;/span>()-&amp;gt;&lt;span class="built_in">setBuffer&lt;/span>(state.buffer);&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>处理完画面以后，如果需要上屏画面，就调用 commit 把画面送到屏幕上。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">&lt;span class="type">bool&lt;/span> &lt;span class="title">OutputHelper::commit&lt;/span>&lt;span class="params">(WBufferRenderer *buffer)&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (&lt;span class="built_in">output&lt;/span>()-&amp;gt;&lt;span class="built_in">offscreen&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!buffer || !buffer-&amp;gt;&lt;span class="built_in">currentBuffer&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(!&lt;span class="keyword">this&lt;/span>-&amp;gt;&lt;span class="built_in">buffer&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> WOutputHelper::&lt;span class="built_in">commit&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">setBuffer&lt;/span>(buffer-&amp;gt;&lt;span class="built_in">currentBuffer&lt;/span>());&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (m_lastCommitBuffer == buffer) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (&lt;span class="built_in">pixman_region32_not_empty&lt;/span>(&amp;amp;buffer-&amp;gt;&lt;span class="built_in">damageRing&lt;/span>()-&amp;gt;&lt;span class="built_in">handle&lt;/span>()-&amp;gt;current))&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">setDamage&lt;/span>(&amp;amp;buffer-&amp;gt;&lt;span class="built_in">damageRing&lt;/span>()-&amp;gt;&lt;span class="built_in">handle&lt;/span>()-&amp;gt;current);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> m_lastCommitBuffer = buffer;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> WOutputHelper::&lt;span class="built_in">commit&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>还会判断是否有硬件加速（GPU），会优先使用硬件来加速计算过程。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> state.worldTransform.&lt;span class="built_in">optimize&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">bool&lt;/span> flipY = wd-&amp;gt;rhi ? !wd-&amp;gt;rhi-&amp;gt;&lt;span class="built_in">isYUpInNDC&lt;/span>() : &lt;span class="literal">false&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (state.renderTarget.&lt;span class="built_in">mirrorVertically&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> flipY = !flipY;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (viewportRect.&lt;span class="built_in">isValid&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> QRect vr = viewportRect;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (flipY)&lt;/span>&lt;br />&lt;span class="line"> vr.&lt;span class="built_in">moveTop&lt;/span>(-vr.&lt;span class="built_in">y&lt;/span>() + state.pixelSize.&lt;span class="built_in">height&lt;/span>() - vr.&lt;span class="built_in">height&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> renderer-&amp;gt;&lt;span class="built_in">setViewportRect&lt;/span>(vr);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> renderer-&amp;gt;&lt;span class="built_in">setViewportRect&lt;/span>(&lt;span class="built_in">QRect&lt;/span>(&lt;span class="built_in">QPoint&lt;/span>(&lt;span class="number">0&lt;/span>, &lt;span class="number">0&lt;/span>), state.pixelSize));&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> QRectF rect = sourceRect;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!rect.&lt;span class="built_in">isValid&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> rect = &lt;span class="built_in">QRectF&lt;/span>(&lt;span class="built_in">QPointF&lt;/span>(&lt;span class="number">0&lt;/span>, &lt;span class="number">0&lt;/span>), &lt;span class="built_in">QSizeF&lt;/span>(state.pixelSize) / devicePixelRatio);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> &lt;span class="type">float&lt;/span> left = rect.&lt;span class="built_in">x&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> &lt;span class="type">float&lt;/span> right = rect.&lt;span class="built_in">x&lt;/span>() + rect.&lt;span class="built_in">width&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">float&lt;/span> bottom = rect.&lt;span class="built_in">y&lt;/span>() + rect.&lt;span class="built_in">height&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">float&lt;/span> top = rect.&lt;span class="built_in">y&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (flipY)&lt;/span>&lt;br />&lt;span class="line"> std::&lt;span class="built_in">swap&lt;/span>(top, bottom);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> QMatrix4x4 matrix;&lt;/span>&lt;br />&lt;span class="line"> matrix.&lt;span class="built_in">ortho&lt;/span>(left, right, bottom, top, &lt;span class="number">1&lt;/span>, &lt;span class="number">-1&lt;/span>);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> QMatrix4x4 projectionMatrix, projectionMatrixWithNativeNDC;&lt;/span>&lt;br />&lt;span class="line"> projectionMatrix = matrix * state.worldTransform;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (wd-&amp;gt;rhi &amp;amp;&amp;amp; !wd-&amp;gt;rhi-&amp;gt;&lt;span class="built_in">isYUpInNDC&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> std::&lt;span class="built_in">swap&lt;/span>(top, bottom);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> matrix.&lt;span class="built_in">setToIdentity&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> matrix.&lt;span class="built_in">ortho&lt;/span>(left, right, bottom, top, &lt;span class="number">1&lt;/span>, &lt;span class="number">-1&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> projectionMatrixWithNativeNDC = matrix * state.worldTransform;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> renderer-&amp;gt;&lt;span class="built_in">setProjectionMatrix&lt;/span>(projectionMatrix);&lt;/span>&lt;br />&lt;span class="line"> renderer-&amp;gt;&lt;span class="built_in">setProjectionMatrixWithNativeNDC&lt;/span>(projectionMatrixWithNativeNDC);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> textureRT = &lt;span class="built_in">static_cast&lt;/span>&amp;lt;QRhiTextureRenderTarget*&amp;gt;(state.sgRenderTarget.rt);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (preserveColorContents) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> textureRT-&amp;gt;&lt;span class="built_in">setFlags&lt;/span>(textureRT-&amp;gt;&lt;span class="built_in">flags&lt;/span>() | QRhiTextureRenderTarget::PreserveColorContents);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> textureRT-&amp;gt;&lt;span class="built_in">setFlags&lt;/span>(textureRT-&amp;gt;&lt;span class="built_in">flags&lt;/span>() &amp;amp; ~QRhiTextureRenderTarget::PreserveColorContents);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h2 id="Surface-渲染">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Surface-渲染" title="Surface 渲染">&lt;/a>Surface 渲染&lt;/h2>&lt;p>在 Treeland 中，为 Surface 创建了 &lt;code>WSurfaceItem&lt;/code>，用于表示一个窗口，并创建了 &lt;code>WSurfaceContent&lt;/code> 作为 &lt;code>WSurfaceItem&lt;/code> 的 delegate。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">&lt;span class="type">void&lt;/span> &lt;span class="title">WSurfaceItemPrivate::initForDelegate&lt;/span>&lt;span class="params">()&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_Q&lt;/span>(WSurfaceItem);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> std::unique_ptr&amp;lt;QQuickItem&amp;gt; newContentContainer;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!delegate) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (&lt;span class="built_in">getItemContent&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">Q_ASSERT&lt;/span>(!delegateIsDirty);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> delegateIsDirty = &lt;span class="literal">false&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> contentItem = &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">WSurfaceItemContent&lt;/span>(q);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (surface)&lt;/span>&lt;br />&lt;span class="line"> contentItem-&amp;gt;&lt;span class="built_in">setSurface&lt;/span>(surface);&lt;/span>&lt;br />&lt;span class="line"> contentItem-&amp;gt;&lt;span class="built_in">setCacheLastBuffer&lt;/span>(!surfaceFlags.&lt;span class="built_in">testFlag&lt;/span>(WSurfaceItem::DontCacheLastBuffer));&lt;/span>&lt;br />&lt;span class="line"> contentItem-&amp;gt;&lt;span class="built_in">setSmooth&lt;/span>(q-&amp;gt;&lt;span class="built_in">smooth&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> contentItem-&amp;gt;&lt;span class="built_in">setLive&lt;/span>(!q-&amp;gt;&lt;span class="built_in">flags&lt;/span>().&lt;span class="built_in">testFlag&lt;/span>(WSurfaceItem::NonLive));&lt;/span>&lt;br />&lt;span class="line"> QObject::&lt;span class="built_in">connect&lt;/span>(q, &amp;amp;WSurfaceItem::smoothChanged, contentItem, &amp;amp;WSurfaceItemContent::setSmooth);&lt;/span>&lt;br />&lt;span class="line"> newContentContainer.&lt;span class="built_in">reset&lt;/span>(contentItem);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &lt;span class="keyword">if&lt;/span> (delegateIsDirty) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> obj = delegate-&amp;gt;&lt;span class="built_in">createWithInitialProperties&lt;/span>(&amp;#123;&amp;#123;&lt;span class="string">&amp;quot;surface&amp;quot;&lt;/span>, QVariant::&lt;span class="built_in">fromValue&lt;/span>(q)&amp;#125;&amp;#125;, &lt;span class="built_in">qmlContext&lt;/span>(q));&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!obj) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">qWarning&lt;/span>() &amp;lt;&amp;lt; &lt;span class="string">&amp;quot;Failed on create surface item from delegate, error mssage:&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &amp;lt;&amp;lt; delegate-&amp;gt;&lt;span class="built_in">errorString&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> delegateIsDirty = &lt;span class="literal">false&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> contentItem = &lt;span class="built_in">qobject_cast&lt;/span>&amp;lt;QQuickItem*&amp;gt;(obj);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!contentItem)&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">qFatal&lt;/span>() &amp;lt;&amp;lt; &lt;span class="string">&amp;quot;SurfaceItem&amp;#x27;s delegate must is Item&amp;quot;&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> newContentContainer.&lt;span class="built_in">reset&lt;/span>(&lt;span class="keyword">new&lt;/span> &lt;span class="built_in">QQuickItem&lt;/span>(q));&lt;/span>&lt;br />&lt;span class="line"> QQmlEngine::&lt;span class="built_in">setObjectOwnership&lt;/span>(contentItem, QQmlEngine::CppOwnership);&lt;/span>&lt;br />&lt;span class="line"> contentItem-&amp;gt;&lt;span class="built_in">setParent&lt;/span>(newContentContainer.&lt;span class="built_in">get&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> contentItem-&amp;gt;&lt;span class="built_in">setParentItem&lt;/span>(newContentContainer.&lt;span class="built_in">get&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!newContentContainer)&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> newContentContainer-&amp;gt;&lt;span class="built_in">setZ&lt;/span>(&lt;span class="built_in">qreal&lt;/span>(WSurfaceItem::ZOrder::ContentItem));&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (contentContainer) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> newContentContainer-&amp;gt;&lt;span class="built_in">setPosition&lt;/span>(contentContainer-&amp;gt;&lt;span class="built_in">position&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> newContentContainer-&amp;gt;&lt;span class="built_in">setSize&lt;/span>(contentContainer-&amp;gt;&lt;span class="built_in">size&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> newContentContainer-&amp;gt;&lt;span class="built_in">setTransformOrigin&lt;/span>(contentContainer-&amp;gt;&lt;span class="built_in">transformOrigin&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> newContentContainer-&amp;gt;&lt;span class="built_in">setScale&lt;/span>(contentContainer-&amp;gt;&lt;span class="built_in">scale&lt;/span>());&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> contentContainer-&amp;gt;&lt;span class="built_in">disconnect&lt;/span>(q);&lt;/span>&lt;br />&lt;span class="line"> contentContainer-&amp;gt;&lt;span class="built_in">deleteLater&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> contentContainer = newContentContainer.&lt;span class="built_in">release&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">updateEventItem&lt;/span>(&lt;span class="literal">false&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">updateBoundingRect&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (eventItem)&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">updateEventItemGeometry&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> Q_EMIT q-&amp;gt;&lt;span class="built_in">contentItemChanged&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>之后当 &lt;code>WSurfaceItem&lt;/code> 需要更新画面时，就能调用 &lt;code>updatePaintNode&lt;/code> 更新渲染。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">QSGNode *&lt;span class="title">WSurfaceItemContent::updatePaintNode&lt;/span>&lt;span class="params">(QSGNode *oldNode, UpdatePaintNodeData *)&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">W_D&lt;/span>(WSurfaceItemContent);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> tp = &lt;span class="built_in">wTextureProvider&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (d-&amp;gt;live || !tp-&amp;gt;&lt;span class="built_in">texture&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> texture = d-&amp;gt;surface ? d-&amp;gt;surface-&amp;gt;&lt;span class="built_in">handle&lt;/span>()-&amp;gt;&lt;span class="built_in">get_texture&lt;/span>() : &lt;span class="literal">nullptr&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (texture) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> tp-&amp;gt;&lt;span class="built_in">setTexture&lt;/span>(qw_texture::&lt;span class="built_in">from&lt;/span>(texture), d-&amp;gt;buffer.&lt;span class="built_in">get&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> tp-&amp;gt;&lt;span class="built_in">setBuffer&lt;/span>(d-&amp;gt;buffer.&lt;span class="built_in">get&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!tp-&amp;gt;&lt;span class="built_in">texture&lt;/span>() || &lt;span class="built_in">width&lt;/span>() &amp;lt;= &lt;span class="number">0&lt;/span> || &lt;span class="built_in">height&lt;/span>() &amp;lt;= &lt;span class="number">0&lt;/span>) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">delete&lt;/span> oldNode;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">nullptr&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> node = &lt;span class="built_in">static_cast&lt;/span>&amp;lt;QSGImageNode*&amp;gt;(oldNode);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (&lt;span class="built_in">Q_UNLIKELY&lt;/span>(!node)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> node = &lt;span class="built_in">window&lt;/span>()-&amp;gt;&lt;span class="built_in">createImageNode&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> node-&amp;gt;&lt;span class="built_in">setOwnsTexture&lt;/span>(&lt;span class="literal">false&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> QSGNode *fpnode = &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">WSGRenderFootprintNode&lt;/span>(&lt;span class="keyword">this&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> node-&amp;gt;&lt;span class="built_in">appendChildNode&lt;/span>(fpnode);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> texture = tp-&amp;gt;&lt;span class="built_in">texture&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> node-&amp;gt;&lt;span class="built_in">setTexture&lt;/span>(texture);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> QRectF textureGeometry = d-&amp;gt;bufferSourceBox;&lt;/span>&lt;br />&lt;span class="line"> node-&amp;gt;&lt;span class="built_in">setSourceRect&lt;/span>(textureGeometry);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="function">&lt;span class="type">const&lt;/span> QRectF &lt;span class="title">targetGeometry&lt;/span>&lt;span class="params">(d-&amp;gt;ignoreBufferOffset ? QPointF() : d-&amp;gt;bufferOffset, size())&lt;/span>&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> node-&amp;gt;&lt;span class="built_in">setRect&lt;/span>(targetGeometry);&lt;/span>&lt;br />&lt;span class="line"> node-&amp;gt;&lt;span class="built_in">setFiltering&lt;/span>(&lt;span class="built_in">smooth&lt;/span>() ? QSGTexture::Linear : QSGTexture::Nearest);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> node;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>而使用 delegate 的目的是为了能让多个 &lt;code>WSurfaceItem&lt;/code> 使用相同的窗口画面，例如某些场景需要临时创建一个窗口的分身，窗口切换列表、多任务视图等。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">QSGTextureProvider *&lt;span class="title">WSurfaceItemContent::textureProvider&lt;/span>&lt;span class="params">()&lt;/span> &lt;span class="type">const&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (QQuickItem::&lt;span class="built_in">isTextureProvider&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> QQuickItem::&lt;span class="built_in">textureProvider&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="built_in">wTextureProvider&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">WSGTextureProvider *&lt;span class="title">WSurfaceItemContent::wTextureProvider&lt;/span>&lt;span class="params">()&lt;/span> &lt;span class="type">const&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">W_DC&lt;/span>(WSurfaceItemContent);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> w = &lt;span class="built_in">qobject_cast&lt;/span>&amp;lt;WOutputRenderWindow*&amp;gt;(d-&amp;gt;window);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!w || !d-&amp;gt;&lt;span class="built_in">sceneGraphRenderContext&lt;/span>() || QThread::&lt;span class="built_in">currentThread&lt;/span>() != d-&amp;gt;&lt;span class="built_in">sceneGraphRenderContext&lt;/span>()-&amp;gt;&lt;span class="built_in">thread&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">qWarning&lt;/span>(&lt;span class="string">&amp;quot;WQuickCursor::textureProvider: can only be queried on the rendering thread of an WOutputRenderWindow&amp;quot;&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">nullptr&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!d-&amp;gt;textureProvider) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> d-&amp;gt;textureProvider = &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">WSGTextureProvider&lt;/span>(w);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (d-&amp;gt;surface) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (&lt;span class="keyword">auto&lt;/span> texture = d-&amp;gt;surface-&amp;gt;&lt;span class="built_in">handle&lt;/span>()-&amp;gt;&lt;span class="built_in">get_texture&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> d-&amp;gt;textureProvider-&amp;gt;&lt;span class="built_in">setTexture&lt;/span>(qw_texture::&lt;span class="built_in">from&lt;/span>(texture), d-&amp;gt;buffer.&lt;span class="built_in">get&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> d-&amp;gt;textureProvider-&amp;gt;&lt;span class="built_in">setBuffer&lt;/span>(d-&amp;gt;buffer.&lt;span class="built_in">get&lt;/span>());&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> d-&amp;gt;textureProvider;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>Treeland 使用 &lt;code>WQuickTextureProxy&lt;/code> 创建窗口的代理显示，而其中就是获取 &lt;code>WSurfaceItem&lt;/code> 的 textureProvider。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">QSGTextureProvider *&lt;span class="title">WQuickTextureProxy::textureProvider&lt;/span>&lt;span class="params">()&lt;/span> &lt;span class="type">const&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (QQuickItem::&lt;span class="built_in">isTextureProvider&lt;/span>())&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> QQuickItem::&lt;span class="built_in">textureProvider&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">W_DC&lt;/span>(WQuickTextureProxy);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!d-&amp;gt;sourceItem)&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">nullptr&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> d-&amp;gt;sourceItem-&amp;gt;&lt;span class="built_in">textureProvider&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>这样多个 proxy 就可以显示同一个窗口的内容，比 QML 的 &lt;code>ShaderEffectSource&lt;/code> 效率更高。&lt;/p>&lt;h2 id="结尾">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#结尾" title="结尾">&lt;/a>结尾&lt;/h2>&lt;p>上述仅仅是 Treeland 实现 Qt 和 wlroots 缝合的一部分流程，实际上对事件的处理就十分复杂，不止键盘输入，还需要处理光标、触控、触摸等其他设备。还有光标的绘制也需要区分硬光标和软光标，渲染画面时的硬件加速及软件实现等。&lt;/p>&lt;p>后续准备写一下光标相关的处理，以及还没介绍 Treeland 的画面是怎么绘制的。&lt;/p>&lt;h2 id="相关文档">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#相关文档" title="相关文档">&lt;/a>相关文档&lt;/h2>&lt;aside>💡&lt;p>Qt QPA &lt;a href="https://doc.qt.io/qt-6/qpa.html">https://doc.qt.io/qt-6/qpa.html&lt;/a>&lt;br />QOffscreenSurface &lt;a href="https://doc.qt.io/qt-6/qoffscreensurface.html">https://doc.qt.io/qt-6/qoffscreensurface.html&lt;/a>&lt;/p>&lt;/aside></description><author>justforlxz</author></item><item><title>Treeland：DDE 进步的阶梯</title><link>https://blog.mkacg.com/2024/11/01/treeland-go-go-go/</link><pubDate>Fri, 01 Nov 2024 04:00:00 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2024/11/01/treeland-go-go-go/</guid><description>&lt;h1 id="回顾">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#回顾" title="回顾">&lt;/a>回顾&lt;/h1>&lt;p>DDE 在 v15 时期，使用 Mutter 作为带合成器的窗管，以及 Metacity 这种不带合成器的窗管，一个是在高性能设备上使用，一个是为低性能设备上使用。&lt;/p>&lt;p>在 V20 时期，DDE 更换 KWin 当窗口管理器，由于 KWin 自带有关闭合成器的模式，所以 DDE 也放弃了 Metacity 作为备用窗管的选项。&lt;/p>&lt;h2 id="Mutter">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Mutter" title="Mutter">&lt;/a>Mutter&lt;/h2>&lt;p>Mutter 是 GNOME 开发的带有合成器功能的窗口管理器。&lt;/p>&lt;img alt="Mutter" src="https://blog.mkacg.com/rss/image.png" />&lt;p>Mutter&lt;/p>&lt;h2 id="Metacity">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Metacity" title="Metacity">&lt;/a>Metacity&lt;/h2>&lt;p>一个不带有合成器功能的窗口管理器。&lt;/p>&lt;img alt="Metacity" src="https://blog.mkacg.com/rss/image%201.png" />&lt;p>Metacity&lt;/p>&lt;h2 id="KWin">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#KWin" title="KWin">&lt;/a>KWin&lt;/h2>&lt;p>KWin 是 KDE 开发的，具有动态切换渲染后端，动态开关合成器功能的窗口管理器。&lt;/p>&lt;img alt="KWin" src="https://blog.mkacg.com/rss/image%202.png" />&lt;p>KWin&lt;/p>&lt;h1 id="需求和指标">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#需求和指标" title="需求和指标">&lt;/a>需求和指标&lt;/h1>&lt;p>在一个面向用户的产品中，拥有友好的界面是一个非常重要的事情，所以设计师给了一大堆非常好看的界面交互设计。&lt;/p>&lt;p>但更多的动画效果，以及更多的组件交互，通常就要求使用更多的资源，更多的内存，更高的功耗。&lt;/p>&lt;p>设计师说要动画华丽，要动画流畅，要优秀的用户体验。&lt;/p>&lt;p>架构师说要低资源占用，低内存，不能卡。&lt;/p>&lt;p>虽说一切目标都是为了用户体验，但两个大指标在实现上竟然是冲突的，那么如何平衡二者，就需要研发献祭一些头发了。&lt;/p>&lt;h1 id="对比">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#对比" title="对比">&lt;/a>对比&lt;/h1>&lt;p>在Linux下目前有两个技术栈，一个是历史悠久的X11，另一个是较新的 Wayland。&lt;/p>&lt;p>接下来，跟随我一起来对比两个技术。&lt;/p>&lt;h2 id="X11">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#X11" title="X11">&lt;/a>X11&lt;/h2>&lt;h3 id="架构">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#架构" title="架构">&lt;/a>架构&lt;/h3>&lt;p>X11采用了比较老的客户端-服务器架构。应用程序通过X服务器与硬件设备进行通信，这种架构非常灵活，支持网络传输，使得远程显示变得可能。然而，X11协议复杂且过时，在很多方面不再符合现代的图形需求。&lt;/p>&lt;h3 id="性能">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#性能" title="性能">&lt;/a>性能&lt;/h3>&lt;p>由于其设计较老，X11在一些现代场景下的性能表现不佳。它需要依赖很多扩展和补丁才能实现现代图形效果，例如合成管理器（Compositor）等。这些叠加的复杂性导致了额外的性能开销。&lt;/p>&lt;h3 id="安全性">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#安全性" title="安全性">&lt;/a>安全性&lt;/h3>&lt;p>X11的安全性问题比较明显，因为所有应用程序都可以访问彼此的窗口信息。这意味着一个恶意程序可以读取或干扰其他程序的输入输出。虽然可以通过一些扩展和工具加强安全性，但这并不是X11的设计初衷。&lt;/p>&lt;h3 id="可扩展性和兼容性">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#可扩展性和兼容性" title="可扩展性和兼容性">&lt;/a>&lt;strong>可扩展性和兼容性&lt;/strong>&lt;/h3>&lt;p>由于存在了几十年，X11有非常广泛的应用支持和兼容性。许多老的应用程序和桌面环境仍然依赖于X11协议，尤其是在远程桌面和某些专业领域。&lt;/p>&lt;h3 id="输入设备支持">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#输入设备支持" title="输入设备支持">&lt;/a>&lt;strong>输入设备支持&lt;/strong>&lt;/h3>&lt;p>由于其历史悠久，X11对各种输入设备的支持相当成熟。但随着新硬件和新输入技术的出现，X11的设计显得有些不灵活，特别是在多点触控和手势支持方面。&lt;/p>&lt;h3 id="远程桌面和网络透明性">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#远程桌面和网络透明性" title="远程桌面和网络透明性">&lt;/a>&lt;strong>远程桌面和网络透明性&lt;/strong>&lt;/h3>&lt;p>X11的一个优势是其天然的网络透明性。它支持通过网络将显示内容远程传输，这是很多专业领域（如科学计算、服务器管理）中非常有用的功能。&lt;/p>&lt;h2 id="Wayland">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Wayland" title="Wayland">&lt;/a>Wayland&lt;/h2>&lt;h3 id="架构-1">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#架构-1" title="架构">&lt;/a>架构&lt;/h3>&lt;p>Wayland采用的是更简单的设计，去掉了X服务器的复杂层次。消除了很多中间环节，提高了效率和响应速度。Wayland专注于本地显示，不像X11那样直接支持远程显示。&lt;/p>&lt;h3 id="性能-1">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#性能-1" title="性能">&lt;/a>性能&lt;/h3>&lt;p>Wayland的设计更加现代化，它简化了渲染过程，减少了中间层，性能提升显著。特别是在动画和窗口操作方面，Wayland通常比X11更为流畅。&lt;/p>&lt;h3 id="安全性-1">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#安全性-1" title="安全性">&lt;/a>安全性&lt;/h3>&lt;p>Wayland在设计时考虑了安全性。应用程序之间的隔离更强，应用程序无法访问其他程序的图像或输入输出，这显著提高了系统的整体安全性。&lt;/p>&lt;h3 id="可扩展性和兼容性-1">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#可扩展性和兼容性-1" title="可扩展性和兼容性">&lt;/a>&lt;strong>可扩展性和兼容性&lt;/strong>&lt;/h3>&lt;p>尽管Wayland逐渐被主流桌面环境（如GNOME、KDE等）支持，但其应用生态仍然不如X11广泛。在某些老旧或专业的应用场景中，可能需要通过兼容层（如XWayland）来运行基于X11的应用程序。&lt;/p>&lt;h3 id="输入设备支持-1">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#输入设备支持-1" title="输入设备支持">&lt;/a>&lt;strong>输入设备支持&lt;/strong>&lt;/h3>&lt;p>Wayland针对现代硬件进行了优化，尤其是在多点触控、触摸板手势等方面有更好的支持。此外，它对显示器的DPI缩放、刷新率等也有更灵活的处理。&lt;/p>&lt;h3 id="远程桌面和网络透明性-1">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#远程桌面和网络透明性-1" title="远程桌面和网络透明性">&lt;/a>&lt;strong>远程桌面和网络透明性&lt;/strong>&lt;/h3>&lt;p>Wayland默认没有X11那样的网络透明性功能。这意味着原生的远程桌面功能比较有限，尽管可以通过一些第三方工具或协议（如VNC、RDP）实现远程桌面，但这不是Wayland的核心功能。&lt;/p>&lt;h2 id="总结">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#总结" title="总结">&lt;/a>总结&lt;/h2>&lt;p>总结下来，X11已经进入维护阶段，不再进行大幅更新。随着时间的推移，开发者和社区的注意力逐渐转移到Wayland上，X11可能会逐步淡出主流桌面环境。Wayland正在逐步成为Linux桌面的标准。随着越来越多的应用程序和桌面环境转向支持Wayland，其生态系统正在不断成熟和扩展。&lt;/p>&lt;h1 id="架构设计">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#架构设计" title="架构设计">&lt;/a>架构设计&lt;/h1>&lt;p>从技术层面上，我们认为是时候更新技术方案了，曾经的X11+窗口管理器+合成器的模式，灵活但不满足需求，Wayland从底层就将三者融合在了一起，并且更新画面是以每幅完整的画面作为基础的，这确保了画面不会因为不同窗口更新界面的时机不一致导致画面撕裂。&lt;/p>&lt;p>更重要的一点，自研的窗口管理器，它是以实现DDE的需求为目的的，这是第三方窗口管理器不能比拟的。通常使用第三方的项目时，都要进行大量破坏性的调整，导致fork后的项目无法和上游同步，不能及时获取更新和修复，并且第三方的项目已经发展了很长时间，内部有许多DDE用不上、甚至冲突的功能，都需要进行大量调整，更加剧了维护成本。&lt;/p>&lt;p>所以 DDE 决定开发一个新的窗口管理器——Treeland。&lt;/p>&lt;p>Treeland在底层使用 wlroots 作为 Wayland 的基础库，不修改 wlroots 的代码，也就意味着可以随时同步上游进度，获得新的功能与修复。上层使用 Qt，可以充分利用公司内大量的 Qt 开发者，不再需要一直有专人负责特定项目，让DDE的技术栈更加统一。&lt;/p>&lt;img alt="image" src="https://blog.mkacg.com/rss/image%203.png" />&lt;aside>&lt;p>该图描述了 Treeland 在整体上会使用哪些项目或接口完成功能。&lt;/p>&lt;/aside>&lt;img alt="Treeland 结构图" src="https://blog.mkacg.com/rss/v25-charter-treeland-treeland.drawio-2.png" />&lt;p>Treeland 结构图&lt;/p>&lt;p>简单介绍一些 Treeland 里涉及的重要项目。&lt;/p>&lt;ul>&lt;li>&lt;p>QWlroots&lt;/p>&lt;p> wlroots 的 Qt 绑定，将 wayland 信号转换成 Qt 风格的信号。&lt;/p>&lt;/li>&lt;li>&lt;p>Waylib&lt;/p>&lt;p> 将 wlroots 中的组件封装成 QtQuick 对象，使用 QPA 为 Treeland 提供事件转换与分发。&lt;/p>&lt;/li>&lt;li>&lt;p>DtkDeclarative&lt;/p>&lt;p> DTK 的 QtQuick 组件，封装了大部分 DTK style 的控件。&lt;/p>&lt;/li>&lt;/ul>&lt;p>在 Waylib中，会使用到 Qt 的 QPA 功能，将 wlroots 作为一个新的平台来处理一部分功能。&lt;/p>&lt;p>QPA 在 Treeland 中有着举足轻重的地位，来自系统底层的事件会先进入到 Waylib中，在 Waylib 里将事件转换成 Qt 内部事件，发送给上层。这样 Treeland 就可以在 QtQuick 中确定用户的点击位置、按键事件等行为。当用户点击的是窗口时，Treeland 还会通过 Waylib 生成一个事件，通过 seat 的接口发送给客户端，完成界面交互。&lt;/p>&lt;img alt="Treeland 处理底层事件与上层事件的流程" src="https://blog.mkacg.com/rss/waylib.png" />&lt;p>Treeland 处理底层事件与上层事件的流程&lt;/p>&lt;h2 id="界面效果与优化">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#界面效果与优化" title="界面效果与优化">&lt;/a>界面效果与优化&lt;/h2>&lt;p>Treeland 作为一个窗口管理器，最重要的功能还是对窗口的管理及显示效果的控制，Treeland 所有的窗口都带有圆角和阴影，以及一些窗口模糊效果。&lt;/p>&lt;h3 id="圆角">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#圆角" title="圆角">&lt;/a>圆角&lt;/h3>&lt;p>DDE及deepin社区应用都大量采用了窗口圆角的设计，由合成器提供圆角裁剪可以带来更加统一的界面设计。&lt;/p>&lt;img alt="红色为QtQuick圆角&amp;#x2F;黄色为Treeland圆角" src="https://blog.mkacg.com/rss/image%204.png" />&lt;p>红色为QtQuick圆角&amp;#x2f;黄色为Treeland圆角&lt;/p>&lt;p>QtQuick 圆角是由 Rectangle 组件提供的，它只能同时对四个角进行操作（红色块）。但 DTK 程序具备异形窗口的能力，所以 Treeland 提供了自己的裁圆角控件（黄色块）。&lt;br />新的造型算法、几何顶点数量比 QtQuick 原生的 Rectangle 减少50%，GPU顶点渲染和三角细分性能提升100%。&lt;br />采用新的抗锯齿算法，提高了GPU片元着色器性能，相比于普通 4xMSAA 抗锯齿算法，计算量减少1&amp;#x2f;4。&lt;/p>&lt;h3 id="模糊">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#模糊" title="模糊">&lt;/a>模糊&lt;/h3>&lt;img alt="QtQuick 模糊控件" src="https://blog.mkacg.com/rss/image%205.png" />&lt;p>QtQuick 模糊控件&lt;/p>&lt;p>QtQuick 的模糊组件仅支持对控件自身进行模糊，这并不符合 Treeland 的需求。&lt;/p>&lt;img alt="Treeland 模糊控件" src="https://blog.mkacg.com/rss/image%206.png" />&lt;p>Treeland 模糊控件&lt;/p>&lt;p>Treeland 重新实现了模糊组件，能从显存里获取组件下方图像数据，再使用融合的模糊算法优化性能。&lt;/p>&lt;h3 id="阴影">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#阴影" title="阴影">&lt;/a>阴影&lt;/h3>&lt;img alt="image" src="https://blog.mkacg.com/rss/image%207.png" />&lt;p>Qt Quick 有一个 BorderImage 组件，它能以九宫格的方式，四个角保持不变，四边和中间的部分拉伸，来达到在组件底部充当装饰的效果。并通过 ImageProvider 的机制，手动控制图片资源的创建。&lt;/p>&lt;p>Treeland 使用 BorderImage 作为窗口阴影贴图，通过 ImageProvider 手动创建贴图材质，在相同大小下可复用同一份材质。&lt;/p>&lt;h3 id="动画">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#动画" title="动画">&lt;/a>动画&lt;/h3>&lt;p>Treeland 直接使用 QtQuick 提供的动画组件，来为界面提供动画效果。&lt;/p>&lt;p>使用 State 和 Transition 为组件定义属性变化，例如窗口最大化和还原，两个 State 切换会触发不同的 Transition 执行属性变化，在属性变化时，使用 QtQuick 的动画组件完成动画播放。&lt;/p>&lt;h2 id="多用户">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#多用户" title="多用户">&lt;/a>多用户&lt;/h2>&lt;p>Treeland 作为解决方案的一部分，目的之一就是多用户共用合成器。在经典模式下，不同用户的切换需要在 tty 层面前端程序转移控制权，每个用户独占一个 tty 进行画面上屏。但切换tty所需的工作量不小，这导致切换时屏幕的缓冲区被不同的程序覆盖，给人的观感就是屏幕闪烁了一下，甚至是黑屏一会儿。&lt;/p>&lt;h3 id="DDM">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#DDM" title="DDM">&lt;/a>DDM&lt;/h3>&lt;img alt="image" src="https://blog.mkacg.com/rss/image%208.png" />&lt;img alt="image" src="https://blog.mkacg.com/rss/image%209.png" />&lt;p>通过上图可以看出，LightDM模式下，每个用户拥有完整的一套进程组，都需要运行窗管、任务栏、文件管理器等。不同的用户会单独占用一个 tty，那么用户切换时，必然伴随着底层 DRM 以及显卡驱动等操作的切换，带来的结果就是会看到闪黑屏，而且两个用户都要跑一个锁屏界面来维持”假装是同一个界面“，也带来了跨用户进程的信息同步难题。&lt;/p>&lt;p>而 DDM 和 Treeland 重新设计了工作流程，将 Treeland 单独抽离出来，每个用户都通过相同的一套机制将窗口画面发送给 Treeland，而 Treeland 负责最终的画面合成以及上屏。&lt;/p>&lt;p>带来的好处显而易见，内存方面节省了窗管、锁屏等进程，切换用户也不会有黑屏闪烁，状态也不用想办法同步了。&lt;/p>&lt;img alt="一个简化的 DDM 与 Treeland 的多用户登录流程" src="https://blog.mkacg.com/rss/greeter.png" />&lt;p>一个简化的 DDM 与 Treeland 的多用户登录流程&lt;/p>&lt;h3 id="与-systemd-的集成">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#与-systemd-的集成" title="与 systemd 的集成">&lt;/a>与 systemd 的集成&lt;/h3>&lt;p>DDE 的每一个用户会话，都已切换至 systemd 服务，而非所有进程都挂载到会话入口的服务上。这样做有很多好处，包括快速重启桌面环境，而非注销再登录。远程桌面的铺垫，会话的启动不再局限于本地会话。&lt;/p>&lt;p>在 Treeland 模式下，DDE 会加载一个单独的服务，用于为用户会话注入显示环境变量。该服务使用 systemd 提供的 socket 机制实行懒加载，当 DDE 需要显示窗口时，即时向 Treeland 注册，完成用户显示服务的初始化。当 Treeland 崩溃重启时，该服务也会等待 Treeland 启动完成，并再次连接回 Treeland，确保用户侧的窗口能正常显示。&lt;/p>&lt;img alt="多用户登录时的基本流程" src="https://blog.mkacg.com/rss/treeland.png" />&lt;p>多用户登录时的基本流程&lt;/p>&lt;h1 id="总结-1">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#总结-1" title="总结">&lt;/a>总结&lt;/h1>&lt;p>本文介绍了深度操作系统（DDE）在窗口管理器方面的演进，从早期使用Mutter和Metacity，到后来采用KWin，最终决定开发自己的窗口管理器Treeland。Treeland基于wlroots和Qt技术栈，实现了更好的性能和更统一的技术框架。&lt;/p>&lt;p>本文概述了 Treeland 的技术架构、界面效果优化（如圆角、模糊、阴影等），以及其在多用户场景下的优势。此外，还介绍了Treeland与systemd的集成，展示了DDE在系统架构和用户体验方面的持续创新和改进。&lt;/p></description><author>justforlxz</author></item><item><title>DDE Qt 6.8 适配说明</title><link>https://deepin-community.github.io/sig-dde-porting/posts/dde-qt6.8-porting-guide/?utm_source=atom_feed</link><pubDate>Fri, 25 Oct 2024 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/dde-qt6.8-porting-guide/</guid><description>&lt;h1 id="dde-qt-68-适配说明">DDE Qt 6.8 适配说明&lt;/h1>
&lt;p>Qt 6.8 发布已经有一段时间了，各个发行版尝试移植 DDE 时发现包括 dde-shell 在内的几个组件存在比较明显的问题，DDE 小组进行了相关的紧急修复。由于 DDE 部分项目也在分叉维护的状态，为了方便各位移植人员有效进行移植，故在此罗列相关注意事项。&lt;/p>
&lt;p>&lt;em>注：笔者所测试的环境为 Arch Linux，下述为 2024/10/25 testing 仓库状态下的测试结论。若未另行说明，则下述涉及到的项目名称仍然使用了与 DDE 对应项目原始仓库的名称，而非各个发行版下的包名。&lt;/em>&lt;/p>
&lt;blockquote>
&lt;p>[!NOTE]
2024/11/06更新：对于 dde-launchpad、dde-tray-loader、dde-shell 目前均有新的维护分支版本，部分版本中已包含了下述中涉及到的一些 patch 的修复。本博客目前只更新了实机验证可用的新 tag 版本，但你也可以尝试未验证但位于维护分支的新 tag。&lt;/p>&lt;/blockquote>
&lt;h2 id="分支与-tag-说明">分支与 tag 说明&lt;/h2>
&lt;p>因维护需要，对于部分 DDE 组件（dde-shell、dde-launchpad、dde-tray-loader），我们对 deepin 23 所使用的分支创建了名为 &lt;code>release/beige&lt;/code> 的维护分支。也会在维护分支上打对应的维护更新用的 tag。&lt;/p>
&lt;p>由于 deepin 现阶段的提测流程需要对提测版本打 tag，故我们对主干（master）分支也会打 tag。为了在不与现行规范冲突的情况下尽可能表示区分，我们使用格式为 &lt;code>x.99.z&lt;/code> 的 tag 标记此版本是尚在开发中的版本。开发中的 tag 版本事实上在满足一定条件下也可供外部使用，但我们不保证 &lt;code>x.99.z&lt;/code> 中 z 位更新时的兼容性，故仍然建议优先使用 release/beige 上的
tag 版本。&lt;/p>
&lt;h2 id="qt-6-wayland">Qt 6 Wayland&lt;/h2>
&lt;p>由于 dde-shell 的托盘加载部分（dde-tray-loader）使用了 Wayland（即便是 x11 环境也如此）实现应用的嵌入，故对 Qt 6 的 wayland 组件存在依赖。有下述两个 Patch 需要应用到 Qt 6 Wayland 组件之上：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://codereview.qt-project.org/c/qt/qtwayland/+/598596">https://codereview.qt-project.org/c/qt/qtwayland/+/598596&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://codereview.qt-project.org/c/qt/qtwayland/+/599732">https://codereview.qt-project.org/c/qt/qtwayland/+/599732&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="dde-shell">dde-shell&lt;/h2>
&lt;h3 id="patch-说明">Patch 说明&lt;/h3>
&lt;p>升级至 Qt 6.8 后，dde-shell 可能存在面板无任何内容的情况，就于此问题，需要应用这个 patch：&lt;/p>
&lt;p>&lt;a href="https://github.com/linuxdeepin/dde-shell/commit/46871c83cf8ecfcf83bf2fb49e1f09af997eca96">https://github.com/linuxdeepin/dde-shell/commit/46871c83cf8ecfcf83bf2fb49e1f09af997eca96&lt;/a>&lt;/p>
&lt;h3 id="版本建议">版本建议&lt;/h3>
&lt;ul>
&lt;li>若目标发行版原本在使用 &lt;code>1.0.0&lt;/code> 版本，则建议至少更新到 &lt;code>1.0.2&lt;/code>&lt;/li>
&lt;li>&lt;code>1.0.3&lt;/code> 以上版本依赖 &lt;code>treeland-protocols&lt;/code> 项目，进行打包即可，建议对齐打包后至少更新 dde-shell 至 &lt;code>1.0.4&lt;/code>
&lt;ul>
&lt;li>由于 &lt;code>treeland-protocols&lt;/code> 更新了其 CMake 支持中目标名称的大小写，故你需要打这个小 patch （或者手动进行相应调整）：
&lt;a href="https://github.com/linuxdeepin/dde-shell/commit/b3f342c094354e4ba87ac1da4cf1a380556b2a3b">https://github.com/linuxdeepin/dde-shell/commit/b3f342c094354e4ba87ac1da4cf1a380556b2a3b&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>dde-shell&lt;/code> 主干分支存在 &lt;code>1.99.1&lt;/code>，但包括此版本在内的主干分支已不再在任务栏提供启动器图标，故需要配合启动器主干分支使用（启动器暂无 &lt;code>1.99.z&lt;/code> 版本）&lt;/li>
&lt;/ul>
&lt;p>tl;dr：建议打包 &lt;code>treeland-protocols&lt;/code> 后更新至至少 &lt;code>1.0.4&lt;/code>。&lt;/p>
&lt;h2 id="treeland-protocols">treeland-protocols&lt;/h2>
&lt;p>被 dde-shell &lt;code>1.0.3&lt;/code> 以上版本所依赖。&lt;/p>
&lt;h3 id="版本建议-1">版本建议&lt;/h3>
&lt;p>建议打包/更新至 &lt;code>0.4.1&lt;/code>。&lt;/p>
&lt;h2 id="dde-tray-loader">dde-tray-loader&lt;/h2>
&lt;h3 id="patch-说明-1">Patch 说明&lt;/h3>
&lt;p>任务栏托盘区域的弹出面板（例如点击时间组件后的面板）早期版本有位置不正确的问题，需要应用这个 patch： &lt;a href="https://github.com/linuxdeepin/dde-tray-loader/commit/664b093b6a913764fedbac9110927f26978aa8c9">https://github.com/linuxdeepin/dde-tray-loader/commit/664b093b6a913764fedbac9110927f26978aa8c9&lt;/a> 。最新版本（&lt;code>1.0.5&lt;/code> 起）中已经修正相关问题。&lt;/p>
&lt;h3 id="版本建议-2">版本建议&lt;/h3>
&lt;p>建议更新至 &lt;code>1.0.5&lt;/code>。&lt;/p>
&lt;h2 id="dde-launchpad">dde-launchpad&lt;/h2>
&lt;h3 id="patch-说明-2">Patch 说明&lt;/h3>
&lt;p>启动器的维护分支版本应该可以在无任何修改的情况下正常工作，尽管启动器小窗口模式的面板位置可能不对，但位置问题暂不计划在维护分支解决。&lt;/p>
&lt;p>启动器主干分支不存在上述问题，但主干分支暂无 &lt;code>1.99.z&lt;/code> tag。&lt;/p>
&lt;h3 id="版本建议-3">版本建议&lt;/h3>
&lt;p>在之前的移植过程中发现小窗口搜索结果界面可能存在显示错位问题，此问题已在 &lt;code>1.0.6&lt;/code> 修复，故建议更新至 &lt;code>1.0.6&lt;/code>。&lt;/p>
&lt;h2 id="dde-application-manager">dde-application-manager&lt;/h2>
&lt;h3 id="patch-说明-3">Patch 说明&lt;/h3>
&lt;p>不需要 patch。&lt;/p>
&lt;h3 id="版本建议-4">版本建议&lt;/h3>
&lt;p>一个 deepin 23 的所谓“特性”即，父进程启动的子进程一般会被识别归属为父进程，会导致例如在终端启动 vscode，打开的 vscode 窗口会和终端共用相同图标的问题。此问题已经在最新维护版本得到解决。直接更新dde-shell （&amp;gt;= 1.0.4） dde-application-manager(&amp;gt;=1.2.16)版本即刻解决。&lt;/p>
&lt;p>建议更新至 &lt;code>1.2.16&lt;/code>。&lt;/p></description><author>DDE 移植兴趣小组</author></item><item><title>DDE v23 发布说明</title><link>https://deepin-community.github.io/sig-dde/posts/dde-v23-release-note/?utm_source=atom_feed</link><pubDate>Wed, 14 Aug 2024 11:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde/posts/dde-v23-release-note/</guid><description>&lt;p>deepin v23 将于 2024 年 8 月 15 日发布，这里为大家简要描述本次更新中，DDE 所涉及的变更，以及我们的进一步计划。&lt;/p>
&lt;p>需注意，本文章是站在 DDE SIG 角度的，倾向于对 DDE 项目整体的技术内容进行描述，面向 DDE 开发者和对 DDE 开发感兴趣的读者，并非面向最终用户的特性概览文章。若您需要大众化的发布概览，请参阅 deepin 公众号、官方网站等提供的介绍文章。另外，如果你对 DDE 的移植感兴趣，请参阅&lt;a href="https://deepin-community.github.io/sig-dde-porting/posts/dde-v23-porting-guide/">另一篇侧重于移植相关事项的文章&lt;/a>。&lt;/p>
&lt;p>不同于 beta3 与 RC 所提供的发布说明，此次将整体介绍 DDE v23 相对 v20 的变化内容。&lt;/p>
&lt;h2 id="变化较大的默认组件">变化较大的默认组件&lt;/h2>
&lt;h3 id="dde-dock--dde-shell">&lt;code>dde-dock&lt;/code> → &lt;code>dde-shell&lt;/code>&lt;/h3>
&lt;p>虽然观感上对用户的差异是任务栏整体的变化，但 &lt;code>dde-shell&lt;/code> 项目所要承载的责任要远超于 &lt;code>dde-dock&lt;/code>。&lt;code>dde-shell&lt;/code> 旨在提供一个桌面环境级的外壳程序，使编写 DDE 桌面组件变得更轻松。例如它提供了允许你指定组件的层级关系、确定放置的屏幕位置等功能，并确保相应功能在 x11 与 Wayland 环境的表象均一致。这将使得桌面组件不必针对应用编写重复的代码来实现与桌面环境强相关的功能，并使得后续 x11 向 Wayland 切换变得更方便。当前，&lt;code>dde-shell&lt;/code> 面向用户呈现的唯一主要组件即任务栏，而预计在后续，OSD、通知中心、剪切板等组件也都会逐渐进行迁移，由 &lt;code>dde-shell&lt;/code> 统一管理。&lt;/p>
&lt;p>另外，&lt;code>dde-shell&lt;/code> 整体也从传统的 QtWidgets 项目变成了 QML 项目，这使得 GPU 加速可以被有效的利用，使得相应的界面交互、动效等更加流畅。关于“任务栏”这个 &lt;code>dde-shell&lt;/code> 组件，也存在了较大变化。为了避免插件崩溃时连带整个任务栏组件一起崩溃的问题发生，任务栏区域采用了内嵌 Wayland 合成器的解决方案实现了相关逻辑。&lt;/p>
&lt;h3 id="dde-launcher--dde-launchpad">&lt;code>dde-launcher&lt;/code> → &lt;code>dde-launchpad&lt;/code>&lt;/h3>
&lt;p>&lt;code>dde-launchpad&lt;/code> 事实上是第一个试水 QML 的 DDE 桌面组件，由于 &lt;code>dde-launcher&lt;/code> 存在大量的内部 model 状态维护不正确的问题以及界面问题，&lt;code>dde-launchpad&lt;/code> 则对其进行了整体重构并将整个界面改用 QML 技术进行构建。如 &lt;code>dde-shell&lt;/code> 一节中所述，这将使得整体的流畅度得以提升。&lt;/p>
&lt;p>此外，由于 DDE 计划对应用程序进行相关的权限管控，&lt;code>dde-launchpad&lt;/code> 也将应用程序列表的获取和管理从原本的 GIO 切换到了新的 &lt;code>dde-application-manager&lt;/code>。&lt;/p>
&lt;h3 id="dde-application-manager">&lt;code>dde-application-manager&lt;/code>&lt;/h3>
&lt;p>&lt;code>dde-application-manager&lt;/code> 是一个后台服务，为需要获取应用列表以及启动应用程序的组件（文管、启动器、任务栏等）提供与管理相关数据，为启动的应用与组件设置恰当的 cgroup、环境变量等信息。尽管当下而言 &lt;code>dde-application-manager&lt;/code> 并无特殊之处，但其为后续实现应用程序权限管控的计划提供了空间。&lt;/p>
&lt;h2 id="技术预览组件">技术预览组件&lt;/h2>
&lt;p>我们在 v23 的开发过程中引入了技术预览组件的概念，而原本位于技术预览组件的两个主要项目（启动器与 shell）均已离开了技术预览阶段，现以正式版的形式面向社区发布。而目前仍然位于技术预览阶段的项目即 TreeLand 与 deepin-im 了。&lt;/p>
&lt;p>为了使得 v23 顺利发布、尽早发布，我们在 RC 阶段即将精力完全投入在了现有组件的缺陷修复上，TreeLand 与 deepin-im 项目均暂无显著成果可供分享，后续我们会在恰当的时候为大家详细介绍这两个项目。&lt;/p>
&lt;h2 id="后续计划">后续计划&lt;/h2>
&lt;p>如之前计划所安排，dde-shell 现已走出技术预览并承载了任务栏的显示职责。dde-shell 的初衷之一是使桌面环境更加模块化，在后续，会有更多桌面组件成为 dde-shell 的一部分。我们也欢迎社区开发者开始尝试使用 dde-shell 编写一些自己觉得有趣的东西，并与我们讨论对 shell 相关设计与接口的体验，一同完善 dde-shell。如果你对这相关的话题感兴趣，欢迎加入 DDE SIG 的 Matrix 群聊 (&lt;code>#dde:matrix.org&lt;/code>) 之中来。&lt;/p>
&lt;p>接下来，wayland 会话支持也会变成主要目标，treeland 将会逐步继续完善，并在恰当的时机提供给大家。&lt;/p>
&lt;p>此外，为了方便非 deepin 发行版的用户和开发者使用 DDE，我们也仍然和上次一样提供了一篇移植注意事项博客。笔者也是 DDE 移植 SIG 的成员，对应的文章现已发布到 DDE 移植小组。如果您感兴趣，可&lt;a href="https://deepin-community.github.io/sig-dde-porting/posts/dde-v23-porting-guide/">在此阅读&lt;/a>。如果你本身在参与 DDE 的移植工作，那么也欢迎你加入 DDE 移植小组（&lt;code>#dde-port:deepin.org&lt;/code> 或 &lt;code>https://t.me/ddeport&lt;/code>）。&lt;/p>
&lt;p>最后，感谢你读到这里。如有任何问题，欢迎在我们的开发者群（&lt;code>#deepin-community:deepin.org&lt;/code>）进行讨论。&lt;/p></description><author>DDE 桌面环境小组</author></item><item><title>DDE v23 RC 发布说明</title><link>https://deepin-community.github.io/sig-dde/posts/dde-rc-release-note/?utm_source=atom_feed</link><pubDate>Wed, 15 May 2024 09:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde/posts/dde-rc-release-note/</guid><description>&lt;p>deepin v23 RC 将于 2024 年 5 月 15 日发布，这里为大家简要描述本次更新中，DDE 所涉及的变更，以及我们的进一步计划。&lt;/p>
&lt;p>需注意，本文章倾向于对 DDE 项目整体的技术内容进行描述，面向 DDE 开发者和对 DDE 开发感兴趣的读者，并非面向最终用户的特性概览文章。另外，如果你对 DDE 的移植感兴趣，请参阅&lt;a href="https://deepin-community.github.io/sig-dde-porting/posts/dde-v23rc-porting-guide/">另一篇侧重于移植相关事项的文章&lt;/a>。&lt;/p>
&lt;h2 id="变化较大的默认组件">变化较大的默认组件&lt;/h2>
&lt;p>相比 deepin v23 beta3 而言，在于 deepin v23 RC 发布的 DDE 中，有一个项目得到了较大规模的重构，并随 RC 默认提供给各位用户。即：&lt;/p>
&lt;ul>
&lt;li>dde-shell&lt;/li>
&lt;/ul>
&lt;p>dde-shell 旨在提供一个桌面环境级的外壳程序，使编写 DDE 桌面组件变得更轻松。这个项目在 beta3 阶段为技术预览状态，而现阶段，dde-shell 则走出了技术预览，并取代了原本的 dde-dock 项目来展示任务栏组件。&lt;/p>
&lt;p>此外，需要注意的是，dde-launchpad 项目也转而默认提供 dde-shell 插件的形式来提供启动器组件。&lt;/p>
&lt;h2 id="技术预览组件">技术预览组件&lt;/h2>
&lt;p>&lt;em>非技术用户请慎重启用技术预览功能&lt;/em>&lt;/p>
&lt;p>deepin v23 beta2 时，我们提供了一个需要用户手动安装的 dcc-insider-plugin 插件，称为技术预览插件。这个组件旨在帮助用户方便的测试 deepin 未来版本中计划提供但仍不稳定的系统组件。需要注意的是，为了加快版本迭代速度，使 v23 首个稳定版可以更快面向用户发布，故这个组件在 RC 阶段并未进行过较多测试，因而可能不会按预期行为工作，故我们目前不建议您使用此插件。&lt;/p>
&lt;ul>
&lt;li>treeland / ddm&lt;/li>
&lt;li>deepin-im&lt;/li>
&lt;/ul>
&lt;p>treeland 与 deepin-im 这两个组件在 beta3 到 RC 的阶段中并无太大显著变化。其中，treeland 项目原本提供了合成器与 DM 两个功能，而现在，treeland 与 DM 也进行了拆分，后者被拆分到了 ddm 仓库之中。&lt;/p>
&lt;h2 id="后续计划">后续计划&lt;/h2>
&lt;p>如之前计划所安排，dde-shell 现已走出技术预览，但 dde-shell 仍有很多需要进一步完善的地方。一些用户会注意到 dde-shell 版的任务栏缺失了一些原本 dde-dock 项目所提供的功能，此类特性会后续逐步补充上来。另外 dde-shell 也会继续向原本的目标迈进，我们会进一步将其他桌面组件转换为 dde-shell 插件的形式进行维护。使得桌面环境模块化之余，也为 wayland 支持作出更好的准备。&lt;/p>
&lt;p>另外，由于 dde-shell 的初衷之一是使桌面环境更加模块化，我们也欢迎社区开发者开始尝试使用 dde-shell 编写一些自己觉得有趣的东西，并与我们讨论对 shell 相关设计与接口的体验，一同完善 dde-shell。如果你对这相关的话题感兴趣，欢迎加入 DDE SIG 的 Matrix 群聊 (&lt;code>#dde:matrix.org&lt;/code>) 之中来。&lt;/p>
&lt;p>接下来，wayland 会话支持也会变成主要目标（尽管 v23 首个正式版仍然可能不会提供 wayland 会话支持），treeland 与 ddm 将会逐步继续完善，并在恰当的时机提供给大家。&lt;/p>
&lt;p>此外，由于 DDE 在 beta3 与 RC 的变化仍然较大，我们也仍然和上次一样提供了一篇移植注意事项博客。笔者也是 DDE 移植 SIG 的成员，对应的文章现已发布到 DDE 移植小组。如果您感兴趣，可&lt;a href="https://deepin-community.github.io/sig-dde-porting/posts/dde-v23rc-porting-guide/">在此阅读&lt;/a>。如果你本身在参与 DDE 的移植工作，那么也欢迎你加入 DDE 移植小组（&lt;code>#dde-port:deepin.org&lt;/code> 或 &lt;code>https://t.me/ddeport&lt;/code>）。&lt;/p>
&lt;p>最后，感谢你读到这里。如有任何问题，欢迎在我们的开发者群（&lt;code>#deepin-community:deepin.org&lt;/code>）进行讨论。&lt;/p></description><author>DDE 桌面环境小组</author></item><item><title>linux内存压缩技术</title><link>https://blog.deepin.org/posts/zram/</link><pubDate>Mon, 18 Mar 2024 05:50:31 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/zram/</guid><description>&lt;h1 id="技术背景">技术背景
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e6%8a%80%e6%9c%af%e8%83%8c%e6%99%af">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;p>顾名思义，内存压缩就是压缩内存，节省内存空间。相信对于搞技术的人来说，压缩这个词并不陌生，一想到这个词，我们首先想到的是压缩可以降低占用空间，使同样的空间可以存放更多的东西。
内存无论多大，总是会有不够用的时候，或者说我们总是想在尽量低的成本控制下，达到系统最优，这个时候还是有必要引入诸如内存压缩的功能来优化系统内存占用。当系统内存紧张的时候，会将文件页丢弃或回写回磁盘（如果是脏页），还可能会触发LMK杀进程进行内存回收。这些被回收的内存如果再次使用都需要重新从磁盘读取，而这个过程涉及到较多的IO操作。
就目前的技术而言，IO的速度远远慢于这RAM操作速度。因此，如果频繁地做IO操作，不仅影响flash使用寿命，还严重影响系统性能。内存压缩是一种让IO过程平滑过渡的做法, 即尽量减少由于内存紧张导致的IO，提升性能。&lt;/p>
&lt;p>&lt;img alt="image" src="https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/c6907def-2d6d-4d90-bb98-3acfd3f9ed43" />&lt;/p>
&lt;p>图：内存管理大体框架（内存压缩技术处于内存回收memory reclaim部分中）&lt;/p>
&lt;h1 id="主流内存压缩技术">主流内存压缩技术
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e4%b8%bb%e6%b5%81%e5%86%85%e5%ad%98%e5%8e%8b%e7%bc%a9%e6%8a%80%e6%9c%af">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;p>目前linux内核主流的内存压缩技术主要有3种：zSwap, zRAM, zCache。下面我们依次对几种方式进行一个简要的说明&lt;/p>
&lt;h2 id="zswap">zSwap
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#zswap">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>zSwap是在memory与flash之间的一层“cache”,当内存需要swap出去磁盘的时候，先通过压缩放到zSwap中去，zSwap空间按需增长。达到一定程度后则会按照LRU的顺序(前提是使用的内存分配方法需要支持LRU)将就最旧的page解压写入磁盘swap device，之后将当前的page压缩写入zSwap。&lt;/p>
&lt;p>zswap本身存在一些缺陷或问题:&lt;/p>
&lt;ol>
&lt;li>如果开启当zswap满交换出backing store的功能, 由于需要将zswap里的内存按LRU顺序解压再swap out, 这就要求内存分配器支持LRU功能。&lt;/li>
&lt;li>如果不开启当zswap满交换出backing store的功能, 和zRam是类似的。&lt;/li>
&lt;/ol>
&lt;h2 id="zrram">zRram
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#zrram">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>zRram即压缩的内存， 使用内存模拟block device的做法。实际不会写到块设备中去，只会压缩后写到模拟的块设备中，其实也就是还是在RAM中，只是通过压缩了。由于压缩和解压缩的速度远比读写IO好，因此在移动终端设备广泛被应用。zRam是基于RAM的block device, 一般swap priority会比较高。只有当其满，系统才会考虑其他的swap devices。当然这个优先级用户可以配置。&lt;/p>
&lt;p>zRram本身存在一些缺陷或问题:&lt;/p>
&lt;ol>
&lt;li>zRam大小是可灵活配置的, 那是不是配置越大越好呢? 如果不是,配置多大是最合适的呢?&lt;/li>
&lt;li>使用zRam可能会在低内存场景由于频繁的内存压缩导致kswapd进程占CPU高, 怎样改善?&lt;/li>
&lt;li>增大了zRam配置,对系统内存碎片是否有影响?&lt;/li>
&lt;/ol>
&lt;p>要利用好zRam功能, 并不是简单地配置了就OK了, 还需要对各种场景和问题都做好处理, 才能发挥最优的效果。&lt;/p>
&lt;h2 id="zcache">zCache
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#zcache">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>zCache是oracle提出的一种实现文件页压缩技术，也是memory与block dev之间的一层“cache”,与zswap比较接近，但zcache目前压缩的是文件页，而zSwap和zRAM压缩是匿名页。&lt;/p>
&lt;p>zcache本身存在一些缺陷或问题:&lt;/p>
&lt;ol>
&lt;li>有些文件页可能本身是压缩的内容, 这时可能无法再进行压缩了；&lt;/li>
&lt;li>zCache目前无法使用zsmalloc, 如果使用zbud,压缩率较低；&lt;/li>
&lt;li>使用的zbud/z3fold分配的内存是不可移动的, 需要关注内存碎片问题；&lt;/li>
&lt;/ol>
&lt;h1 id="内存压缩主流的内存分配器">内存压缩主流的内存分配器
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e5%86%85%e5%ad%98%e5%8e%8b%e7%bc%a9%e4%b8%bb%e6%b5%81%e7%9a%84%e5%86%85%e5%ad%98%e5%88%86%e9%85%8d%e5%99%a8">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;h2 id="zsmalloc">Zsmalloc
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#zsmalloc">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>zsmalloc是为ZRAM设计的一种内存分配器。内核已经有slub了，为什么还需要zsmalloc内存分配器？这是由内存压缩的场景和特点决定的。zsmalloc内存分配器期望在低内存的场景也能很好地工作，事实上，当需要压缩内存进行zsmalloc内存分配时，内存一般都比较紧张且内存碎片都比较严重了。如果使用slub分配， 很可能由于高阶内存分配不到而失败。另外，slub也可能导致内存碎片浪费比较严重，最坏情况下，当对象大小略大于PAGE_SIZE/2时，每个内存页接近一半的内存将被浪费。&lt;/p>
&lt;p>实测发现，anon pages的平均压缩比大约在1:3左右，所以compressed anon page size很多在1.2K左右。如果是Slub，为了分配大量1.2K的内存，可能内存浪费严重。zsmalloc分配器尝试将多个相同大小的对象存放在组合页（称为zspage）中，这个组合页不要求物理连续，从而提高内存的使用率。&lt;/p>
&lt;p>&lt;img alt="image" src="https://wiki.deepin.org/2024-3-14_56580.png" />&lt;/p>
&lt;p>需要注意的是, 当前zsmalloc不支持LRU功能, 旧版本内核分配的不可移动的页, 对内存碎片影响严重, 但最新版本内核已经是支持分配可移动类型内存了。&lt;/p>
&lt;h2 id="zbud">Zbud
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#zbud">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>zbud是一个专门为存储压缩page而设计的内存分配器。用于将2个objects存到1个单独的page中。zbud是可以支持LRU的, 但分配的内存是不可移动的。&lt;/p>
&lt;h2 id="z3fold">Z3fold
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#z3fold">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>z3fold是一个较新的内存分配器, 与zbud不同的是, 将3个objects存到1个单独的page中,也就是zbud内存利用率极限是1:2, z3fold极限是1:3。同样z3fold是可以支持LRU的, 但分配的内存是不可移动的。&lt;/p>
&lt;h1 id="内存压缩技术与内存分配器组合">内存压缩技术与内存分配器组合
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e5%86%85%e5%ad%98%e5%8e%8b%e7%bc%a9%e6%8a%80%e6%9c%af%e4%b8%8e%e5%86%85%e5%ad%98%e5%88%86%e9%85%8d%e5%99%a8%e7%bb%84%e5%90%88">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;p>结合上面zSwap / zRam /zCache的介绍, 与zsmalloc/zbud/z3fold分别怎样组合最合适呢?
下面总结了一下, 具体原因可以看上面介绍的时候各类型的特点。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>对比项&lt;/th>
&lt;th>zsmalloc&lt;/th>
&lt;th>zbud&lt;/th>
&lt;th>z3fold&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>zSwap（有实际swap device）&lt;/td>
&lt;td>×(不可用)&lt;/td>
&lt;td>√(可用)&lt;/td>
&lt;td>√(最佳)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>zSwap（无实际swap device）&lt;/td>
&lt;td>√(最佳)&lt;/td>
&lt;td>√(可用)&lt;/td>
&lt;td>√(可用)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>zRam&lt;/td>
&lt;td>√(最佳)&lt;/td>
&lt;td>√(可用)&lt;/td>
&lt;td>√(可用)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>zCache&lt;/td>
&lt;td>×(不可用)&lt;/td>
&lt;td>√(可用)&lt;/td>
&lt;td>√(最佳)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h1 id="zram技术原理">zRAM技术原理
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#zram%e6%8a%80%e6%9c%af%e5%8e%9f%e7%90%86">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;p>zRam内存压缩技术是目前移动终端广泛使用的内存压缩技术。&lt;/p>
&lt;h2 id="软件框架">软件框架
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e8%bd%af%e4%bb%b6%e6%a1%86%e6%9e%b6">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>下图展示了内存管理大体的框架， 内存压缩技术处于内存回收memory reclaim部分中。&lt;/p>
&lt;p>&lt;img alt="image" src="https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/c85953a2-c979-4b37-b87f-34148b61df6b" />&lt;/p>
&lt;p>再具体到zRam, 它的软件架构可以分为3部分：数据流操作，内存压缩算法 ，zram驱动。
&lt;img alt="image" src="https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/447b5c2e-1234-48e8-8af6-a979db5a14a8" />&lt;/p>
&lt;h2 id="实现原理">实现原理
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e5%ae%9e%e7%8e%b0%e5%8e%9f%e7%90%86">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>Zram内存压缩技术本质上就是以时间换空间。通过CPU压缩、解压缩的开销换取更大的可用内存空间。&lt;/p>
&lt;p>我们主要描述清楚下面这2个问题：
1.什么时候会进行内存压缩？
2.进行内存压缩/解压缩的流程是怎样的？&lt;/p>
&lt;p>进行内存压缩的时机：&lt;/p>
&lt;ol>
&lt;li>Kswapd场景：kswapd是内核内存回收线程， 当内存watermark低于low水线时会被唤醒工作， 其到内存watermark不小于high水线。&lt;/li>
&lt;li>Direct reclaim场景：内存分配过程进入slowpath, 进行直接行内存回收。
&lt;img alt="image" src="https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/289527b7-9055-4091-99c1-9363486d738e" />&lt;/li>
&lt;/ol>
&lt;p>下面是基于4.4内核理出的内存压缩、解压缩流程。
内存回收过程路径进行内存压缩。会将非活跃链表的页进行shrink, 如果是匿名页会进行pageout, 由此进行内存压缩存放到ZRAM中， 调用路径如下：&lt;/p>
&lt;p>&lt;img alt="image" src="https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/87b89832-6d46-4990-9f12-a815bd050cbe" />&lt;/p>
&lt;p>在匿名页换出到swap设备后， 访问页时， 产生页访问错误, 当发现“页表项不为空， 但页不在内存中”， 该页就是已换到swap区中，由此会开始将该页从swap区中重新读取， 如果是ZRAM， 则是解压缩的过程。调用路径如下：
&lt;img alt="image" src="https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/63700e9c-18cd-4dc2-843c-5d46eb3dbf81" />&lt;/p>
&lt;h2 id="内存压缩算法">内存压缩算法
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e5%86%85%e5%ad%98%e5%8e%8b%e7%bc%a9%e7%ae%97%e6%b3%95">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>目前比较主流的内存算法主要为LZ0, LZ4, ZSTD等。下面截取了几种算法在x86机器上的表现。各算法有各自特点， 有以压缩率高的， 有压缩/解压快的等， 具体要结合需求场景选择使用。&lt;/p>
&lt;p>&lt;img alt="image" src="https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/1dfeb35c-f283-4a91-8c67-730737ab9120" />&lt;/p>
&lt;h1 id="zram技术应用">zRAM技术应用
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#zram%e6%8a%80%e6%9c%af%e5%ba%94%e7%94%a8">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;p>本节描述一下在使用ZRAM常遇到的一些使用或配置，调试的方法。&lt;/p>
&lt;h2 id="如何配置开启zram">如何配置开启zRAM
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e5%a6%82%e4%bd%95%e9%85%8d%e7%bd%ae%e5%bc%80%e5%90%afzram">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="配置内存压缩算法">配置内存压缩算法
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e9%85%8d%e7%bd%ae%e5%86%85%e5%ad%98%e5%8e%8b%e7%bc%a9%e7%ae%97%e6%b3%95">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>下面例子配置压缩算法为lz4&lt;/p>
&lt;pre>&lt;code>echo lz4 &amp;gt; /sys/block/zram0/comp_algorithm
&lt;/code>&lt;/pre>&lt;h3 id="配置zram大小">配置ZRAM大小
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e9%85%8d%e7%bd%aezram%e5%a4%a7%e5%b0%8f">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>下面例子配置zram大小为2GB&lt;/p>
&lt;pre>&lt;code>echo 2147483648 &amp;gt; /sys/block/zram0/disksize
&lt;/code>&lt;/pre>&lt;h3 id="使能zram">使能zram
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e4%bd%bf%e8%83%bdzram">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>mkswap /dev/zram0&lt;/p>
&lt;p>swapon /dev/zram0&lt;/p>
&lt;h3 id="zram块设备个数设定">zRAM块设备个数设定
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#zram%e5%9d%97%e8%ae%be%e5%a4%87%e4%b8%aa%e6%95%b0%e8%ae%be%e5%ae%9a">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>如果是编译为内核模块，那么可以在内核模块加载的时候，添加参数：insmod zram.ko num_devices=4&lt;/p>
&lt;p>也可直接修改内核源代码，代码地址为：
/drivers/block/zram/zram_drv.c&lt;/p>
&lt;pre>&lt;code>/* Module params (documentation at end) */
static unsigned int num_devices = 1;
&lt;/code>&lt;/pre>&lt;p>修改num_devices为你想要的zram个数即可&lt;/p>
&lt;h3 id="压缩流的最大个数设定">压缩流的最大个数设定
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e5%8e%8b%e7%bc%a9%e6%b5%81%e7%9a%84%e6%9c%80%e5%a4%a7%e4%b8%aa%e6%95%b0%e8%ae%be%e5%ae%9a">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>这个是 3.15 版本及以后的 kernel 新加入的功能，3.15 版本之前的 zram 压缩都是使用一个压缩流（缓存 buffer 和算法私有部分）实现，每个写（压缩）操作都会独享压缩流，但是单压缩流如果出现数据奔溃或者卡住的现象，所有的写（压缩）操作将一直处于等待状态，这样效率非常低；而多压缩流的架构会让写（压缩）操作可以并行去执行，大大提高了压缩的效率和稳定性。&lt;/p>
&lt;p>查看压缩流个数：默认是1，可以直接向proc文件写入，也可以直接更改代码方式来改变默认压缩流个数&lt;/p>
&lt;pre>&lt;code>cat /sys/block/zram0/max_comp_streams
&lt;/code>&lt;/pre>&lt;p>设定压缩流个数：&lt;/p>
&lt;pre>&lt;code>echo 3 &amp;gt; /sys/block/zram0/max_comp_streams
&lt;/code>&lt;/pre>&lt;h3 id="其他参数">其他参数
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e5%85%b6%e4%bb%96%e5%8f%82%e6%95%b0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;strong>Name&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Access&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Description&lt;/strong>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>disksize&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>显示和设置该块设备的内存大小&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>initstate&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>显示设备的初始化状态&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>reset&lt;/td>
&lt;td>WO&lt;/td>
&lt;td>重置设备&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>num_reads&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>读数据的个数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>failed_reads&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>读数据失败的个数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>num_write&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>写数据的个数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>failed_writes&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>写数据失败的个数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>invalid_io&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>非页面大小对齐的I/O请求的个数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>max_comp_streams&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>最大可能同时执行压缩操作的个数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>comp_algorithm&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>显示和设置压缩算法&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>notify_free&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>空闲内存的通知个数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>zero_pages&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>写入该块设备的全为的页面的个数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>orig_data_size&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>保存在该块设备中没有被压缩的数据的大小&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>compr_data_size&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>保存在该块设备中已被压缩的数据的大小&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>mem_used_total&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>分配给该块设备的总内存大小&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>mem_used_max&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>该块设备已用的内存大小，可以写 1 重置这个计数参数到当前真实的统计值&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>mem_limit&lt;/td>
&lt;td>RW&lt;/td>
&lt;td>zram 可以用来保存压缩数据的最大内存&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>pages_compacted&lt;/td>
&lt;td>RO&lt;/td>
&lt;td>在压缩过程中可用的空闲页面的个数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>compact&lt;/td>
&lt;td>WO&lt;/td>
&lt;td>触发内存压缩&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="swappiness含义简述">swappiness含义简述
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#swappiness%e5%90%ab%e4%b9%89%e7%ae%80%e8%bf%b0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>swappiness参数是内核倾向于回收匿名页到swap（使用的ZRAM就是swap设备）的积极程度， 原生内核范围是0~100， 参数值越大， 表示回收匿名页到swap的比例就越大。如果配置为0， 表示仅回收文件页，不回收匿名页。默认值为60。可以通过节点“/proc/sys/vm/swappiness”配置。&lt;/p>
&lt;h2 id="zram相关的技术指标">zRam相关的技术指标
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#zram%e7%9b%b8%e5%85%b3%e7%9a%84%e6%8a%80%e6%9c%af%e6%8c%87%e6%a0%87">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="zram大小及剩余空间">zRAM大小及剩余空间
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#zram%e5%a4%a7%e5%b0%8f%e5%8f%8a%e5%89%a9%e4%bd%99%e7%a9%ba%e9%97%b4">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>Proc/meminfo 中可以查看相关信息
SwapTotal：swap 总大小, 如果配置为ZRAM, 这里就是ZRAM总大小
SwapFree：swap 剩余大小, 如果配置为ZRAM, 这里就是ZRAM剩余大小&lt;/p>
&lt;p>当然， 节点 /sys/block/zram0/disksize 是最直接的。&lt;/p>
&lt;h3 id="zram压缩率">zRAM压缩率
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#zram%e5%8e%8b%e7%bc%a9%e7%8e%87">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>/sys/block/zram/mm_stat 中有压缩前后的大小数据， 由此可以计算出实际的压缩率
orig_data_size：压缩前数据大小， 单位为bytes
compr_data_size ：压缩后数据大小， 单位为bytes&lt;/p>
&lt;h3 id="换出换入swap区的总量">换出/换入swap区的总量
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e6%8d%a2%e5%87%ba%e6%8d%a2%e5%85%a5swap%e5%8c%ba%e7%9a%84%e6%80%bb%e9%87%8f">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>proc/vmstat 中中有相关信息
pswpin:换入总量， 单位为page
pswout:换出总量， 单位为page&lt;/p>
&lt;h2 id="zram相关优化">zRam相关优化
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#zram%e7%9b%b8%e5%85%b3%e4%bc%98%e5%8c%96">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>上面提到zRam的一些缺陷, 怎么去改善呢?&lt;/p>
&lt;ol>
&lt;li>
&lt;p>zRam大小是可灵活配置的, 那是不是配置越大越好呢? 如果不是配置多大是最合适的呢?
zRam大小的配置比较灵活, 如果zRam配置过大, 后台缓存了应用过多, 这也是有可能会影响前台应用使用的流畅度。另外, zRam配置越大, 也需要关注系统的内存碎片化情。因此zRam并不是配置越大越好,具体的大小需要根据内存总大小及系统负载情况考虑及实测而定。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>使用zRam,可能会存在低内存场景由于频繁的内存压缩导致kswapd进程占CPU高, 怎样改善?
zRam本质就是以时间换空间, 在低内存的情况下, 肯定会比较频繁地回收内存, 这时kswapd进程是比较活跃的, 再加上通过压缩内存, 会更加消耗CPU资源。改善这种情况方法也比较多, 比如, 可以使用更优的压缩算法, 区别使用场景, 后台不影响用户使用的场景异步进行深度内存压缩, 与用户体验相关的场景同步适当减少内存压缩, 通过增加文件页的回收比例加快内存回收等等。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>增大了zRam配置,对系统内存碎片是否有影响?
使用zRam是有可能导致系统内存碎片变得更严重的, 特别是zsmalloc分配不支持可移动内存类型的时候。新版的内核zsmalloc已经支持可移动类型分配的， 但由于增大了zRam,结合android手机的使用特点, 仍然会有可能导致系统内存碎片较严重的情况,因些内存碎片问题也是需要重点关注的。解决系统内存碎片的方法也比较多, 可以结合具体的原因及场景进行优化。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h1 id="参考资料">参考资料
&lt;span>
&lt;a href="https://blog.deepin.org/posts/zram/#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;p>&lt;a href="https://cclinuxer.github.io/2020/09/Linux%E5%86%85%E5%AD%98%E5%8E%8B%E7%BC%A9%E6%B5%85%E6%9E%90%E4%B9%8B%E5%8E%9F%E7%90%86/">Linux内存压缩浅析之原理&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://www.wowotech.net/memory_management/zram.html">zRAM内存压缩技术原理与应用&lt;/a>&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>DDE v23 beta3 发布说明</title><link>https://deepin-community.github.io/sig-dde/posts/dde-beta3-release-note/?utm_source=atom_feed</link><pubDate>Sun, 28 Jan 2024 06:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde/posts/dde-beta3-release-note/</guid><description>&lt;p>deepin v23 beta3 将于 2024 年 1 月 31 日发布（注：因存在部分临时设计变动，延期发布，仍计划在当周发布），这里为大家简要描述本次更新中，DDE 所涉及的变更，以及我们的进一步计划。另需注意，本文章倾向于对 DDE 项目整体的技术内容进行描述，面向 DDE 开发者和对 DDE 开发感兴趣的读者，并非面向最终用户的特性概览文章。&lt;/p>
&lt;h2 id="变化较大的默认组件">变化较大的默认组件&lt;/h2>
&lt;p>相比 deepin v23 beta2 而言，在于 deepin v23 beta3 发布的 DDE 中，有两个项目得到了较大规模的重构，并随 beta3 默认提供给各位用户。这两个项目分别是：&lt;/p>
&lt;ul>
&lt;li>dde-application-manager (&amp;gt;= 1.2)&lt;/li>
&lt;li>dde-launchpad (取代 dde-launcher)&lt;/li>
&lt;/ul>
&lt;p>dde-application-manager 承载了 DDE 下应用程序的启动与管理等职责，在早期版本中存在诸多架构不合理以及实现问题，影响到了 dde-launcher、dde-dock、dde-grand-search 等项目启动应用与管理应用功能的正常使用。于是我们对 dde-application-manager 进行了大规模的重构，也重新设计了此项目的对外 D-Bus 接口。在 beta3 中，你将不再会遇到类如应用频繁集体闪退的问题，对应用 desktop 文件的支持变得更加完整。在未来，也会对实时调整应用缩放等功能预留支持的空间。&lt;/p>
&lt;p>dde-launchpad 则是对 dde-launcher 的完整重写。一方面，dde-launcher 对旧的 dde-application-manager 具有很高的偶合度，另一方面，旧的 dde-launcher 架构使得维护此组件变得非常困难。dde-launchpad 是目前 DDE 的首个基于 QML 的组件，且基于最新的 Qt 6。得益于 QML 技术，launchpad 将会有效利用 GPU 绘制界面，使交互体验更加流畅。不过由于 dde-launchpad 是完全重写版本，故尚有部分功能未在当前的 dde-launchpad 中提供。&lt;/p>
&lt;h2 id="技术预览组件">技术预览组件&lt;/h2>
&lt;p>&lt;em>非技术用户请慎重启用技术预览功能&lt;/em>&lt;/p>
&lt;p>deepin v23 beta2 时，我们提供了一个需要用户手动安装的 dcc-insider-plugin 插件，称为技术预览插件。这个组件旨在帮助用户方便的测试 deepin 未来版本中计划提供但仍不稳定的系统组件。在 beta2 时，技术预览组件仅包含了 dde-launchpad 一项。而在 beta3 中，dde-launchpad 走出了技术预览阶段，而有更多的组件进入了技术预览阶段：&lt;/p>
&lt;ul>
&lt;li>treeland&lt;/li>
&lt;li>dde-shell&lt;/li>
&lt;li>deepin-im&lt;/li>
&lt;/ul>
&lt;p>treeland 是 deepin 的下一代 wayland 窗口合成器，且可以同时提供会话管理功能。由于 treeland 会替换 deepin-kwin 并同样会接管 lightdm，故切换 treeland 时需要格外留意。&lt;/p>
&lt;p>切换到 treeland 后，将会进入的 DDE 会话也会与原本有差异。treeland 会话中会使用 dde-shell 而非原本的 dde-dock、dde-widgets 等组件。尽管 dde-shell 可以独立使用，但目前 dde-shell 是作为与 treeland 共同配合进入技术预览阶段的桌面组件。dde-shell 提供了与 wayland 会话下的桌面环境的相关功能集成，今后的 dock 等组件也均会作为 dde-shell 的插件存在，以便提供更深度的集成。&lt;/p>
&lt;p>deepin-im 是新的输入法组件，作为目前各种底层输入法框架的抽象层存在，以便用户更方便的管理输入法而无需关注底层的配置细节。deepin-im 并不与 treeland 绑定，可以单独启用。&lt;/p>
&lt;h2 id="后续计划">后续计划&lt;/h2>
&lt;p>在未来，首要计划即使 dde-shell 可以走出技术预览阶段并配合 treeland 达成真正稳定可用的 wayland 会话，这意味着 treeland 与 dde-shell 相关的项目还有比较多的相关工作需要持续进行。也是我们的主要努力方向。另外，尽管 dde-shell 相关的 API 可能仍会存在一些变动，但我们计划在随后提供一些必要的文档来帮助开发者更好的了解 dde-shell 的目的与作用，以便帮助开发者参与到项目之中来。如果你对这相关的话题感兴趣，欢迎加入 DDE SIG 的 Matrix 群聊 (&lt;code>#dde:matrix.org&lt;/code>) 之中来。&lt;/p>
&lt;p>另外，由于 DDE 在 beta2 与 beta3 的变化较大，我们也计划提供一篇移植注意事项博客。笔者也是 DDE 移植 SIG 的成员，于是对应的文章计划会发布到 DDE 移植小组。如果您感兴趣，请考虑订阅 &amp;lt;planet.deepin.org&amp;gt; 的 RSS 更新。如果你本身在参与 DDE 的移植工作，那么也欢迎你加入 DDE 移植小组（&lt;code>#dde-port:deepin.org&lt;/code> 或 &lt;code>https://t.me/ddeport&lt;/code>）。&lt;/p>
&lt;p>最后，感谢你读到这里。如有任何问题，欢迎在我们的开发者群（&lt;code>#deepin-community:deepin.org&lt;/code>）进行讨论。&lt;/p></description><author>DDE 桌面环境小组</author></item><item><title>TLP 电源管理简述</title><link>https://blog.deepin.org/posts/tlp-power-management/</link><pubDate>Thu, 07 Dec 2023 00:00:00 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/tlp-power-management/</guid><description>&lt;h2 id="简述">简述
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e7%ae%80%e8%bf%b0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>在上一篇&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/">浅析Linux电源配置&lt;/a>之后，我们一直在深入探索如何进一步优化我们系统的续航和性能表现，今天它来了：&lt;/p>
&lt;p>TLP 是适用于 Linux 的功能丰富的命令行实用程序，无需深入研究技术细节即可节省笔记本电脑电池电量。之前我们的系统使用的laptopmode,但是相较于TLP还有有部分劣势：比如tlp脚本是被动唤醒，可以以较小的开销完成电源管理相关内容。而且TLP文档支持非常完善，所以可以方便用户自行调整相关配置。以下是TLP官方文档内容的和我自己的理解的结合，各位系统用户可以结合自己的实际情况diy自己的电源策略文件，也可以将好的电源配置在&lt;a href="https://bbs.deepin.org/">deepin 论坛&lt;/a>中分享。&lt;/p>
&lt;h2 id="工作原理">工作原理
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e5%b7%a5%e4%bd%9c%e5%8e%9f%e7%90%86">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>TLP 基本上所做的是调整影响功耗的内核设置，内核态的配置文件存储在RAM中，所以并不具备持久性。TLP将配置存储在用户态中，在内核启动时对其进行配置&lt;/li>
&lt;li>TLP 处理的大多数内核设置都作为 sysfs 节点导出到用户空间，即 /sys/ 下的文件。&lt;code>tlp-stat&lt;/code> 的输出将显示路径。&lt;/li>
&lt;li>TLP 提供两组独立的设置，称为配置文件，一组用于电池 （BAT），另一组用于交流操作。这意味着 TLP 不仅在启动时，而且在每次电源更改时都必须应用适当的配置文件（可以据此实现AC BT切换电源调度状态）&lt;/li>
&lt;/ul>
&lt;h3 id="tlp触发事件信号">TLP触发事件（信号）
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#tlp%e8%a7%a6%e5%8f%91%e4%ba%8b%e4%bb%b6%e4%bf%a1%e5%8f%b7">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;ul>
&lt;li>充电器插入（交流供电）：应用AC配置文件&lt;/li>
&lt;li>充电器已拔下（电池供电）： 应用BAT配置文件&lt;/li>
&lt;li>已插入 USB 设备：激活设备的 USB 自动挂起模式（可以在配置文件设置例外或拒绝连接）&lt;/li>
&lt;li>系统启动（boot）：应用与当前电源 AC/BAT 相对应的设置配置文件。应用充电阈值并根据您的个人设置切换蓝牙、Wi-Fi 和 WWAN 设备（在默认配置中禁用）&lt;/li>
&lt;li>系统关机 (power off)：保存或切换蓝牙、Wi-Fi 和 WWAN 设备状态，并根据您的个人设置禁用 USB 自动挂起（在默认配置中禁用）&lt;/li>
&lt;li>系统重启： 相当于关机再启动&lt;/li>
&lt;li>系统挂起到 ACPI 睡眠状态 S0ix（空闲待机）、S3（挂起到 RAM）或 S4（挂起到磁盘）：保存蓝牙、Wi-Fi 和 WWAN 设备状态，并根据您的个人设置关闭可移动光盘驱动器的电源（在默认配置中禁用）。&lt;/li>
&lt;li>系统从 ACPI 睡眠状态 S0ix（空闲待机）、S3（挂起到 RAM）或 S4（挂起到磁盘）恢复： 应用与当前电源 AC/BAT 相对应的设置配置文件。恢复充电阈值以及蓝牙、Wi-Fi 和 WWAN 设备状态，具体取决于您的个人设置（在默认配置中禁用）。&lt;/li>
&lt;li>LAN、Wi-Fi、WWAN 连接/断开连接或笔记本电脑插接/未插接：根据您的个人设置启用或禁用内置蓝牙、Wi-Fi 和 WWAN 设备（在默认配置中禁用）&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>除了上述事件之外，TLP 不会对设置进行动态或自适应更改
特别是，TLP 绝不会因 CPU 负载、电池电量或其他原因而调整设置（如果我们需要去实现这一部分，则可以，则可以通过添加一个信号的方式来实现）&lt;/p>
&lt;/blockquote>
&lt;h2 id="安装">安装
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e5%ae%89%e8%a3%85">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>&lt;code>sudo apt install tlp&lt;/code>&lt;/p>
&lt;h2 id="使用">使用
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e4%bd%bf%e7%94%a8">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="启动">启动
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e5%90%af%e5%8a%a8">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>安装后TLP将在系统启动的时候自动启动，如果你不想重启系统，可以使用&lt;code>sudo tlp start&lt;/code>来启动tlp，也可以使用此命令来应用更改。&lt;/p>
&lt;h3 id="状态">状态
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e7%8a%b6%e6%80%81">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>&lt;code>tlp-stat -s&lt;/code> TLP是bash脚本，所以不存于daemon进程&lt;/p>
&lt;h3 id="命令行">命令行
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e5%91%bd%e4%bb%a4%e8%a1%8c">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;h4 id="tlp">TLP：
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#tlp">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>&lt;code>sudo tlp bat&lt;/code> 应用电池配置文件并进入手动模式 手动模式意味着对电源的更改将被忽略，直到下一次重新启动或发出 tlp start 以恢复自动模式&lt;/p>
&lt;p>&lt;code>sudo tlp ac&lt;/code>应用交流配置文件并进入手动模式&lt;/p>
&lt;p>&lt;code>sudo tlp usb&lt;/code> 对所有的ubs设备应用自动挂起&lt;/p>
&lt;p>&lt;code>sudo tlp bayoff&lt;/code> 关闭 MediaBay/Ultrabay 中的光驱电源&lt;/p>
&lt;p>&lt;code>sudo tlp setcharge [&amp;lt;START_CHARGE_THRESH&amp;gt; &amp;lt;STOP_CHARGE_THRESH&amp;gt;] [BAT0|BAT1|BAT&amp;lt;x&amp;gt;|CMB0|CMB1]&lt;/code> 可以设定对指定电池开始充电百分比和结束充电的百分比，以达到养护电池的目的（如果不带参数 会重置电池管理方案）（命令只能暂时更改，如果需要持久化更改 需要修改配置文件）&lt;/p>
&lt;p>&lt;code>sudo tlp fullcharge [BAT0|BAT1|BAT&amp;lt;x&amp;gt;|CMB0|CMB1]&lt;/code> 设定电池充满&lt;/p>
&lt;p>&lt;code>tlp diskid&lt;/code> 显示已经配置驱动器的磁盘ID&lt;/p>
&lt;p>以下部分为ThinkPad专属&lt;/p>
&lt;p>&lt;code>sudo tlp chargeonce [BAT0|BAT1]&lt;/code> 将电池充电至停止充电阈值一次，这个阈值是使用setcharge设置的&lt;/p>
&lt;p>&lt;code>sudo tlp discharge [BAT0|BAT1]&lt;/code> 让电池在交流电源下完全放电&lt;/p>
&lt;p>&lt;code>sudo tlp recalibrate [BAT0|BAT1]&lt;/code>校准电池&lt;/p>
&lt;h4 id="tlp-rdw">TLP-RDW
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#tlp-rdw">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>&lt;code>sudo tlp-rdw [ enable | disable ]&lt;/code> 启用或关闭无线电管理功能&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">bluetooth &lt;span style="color:#f92672">[&lt;/span> on | off | toggle &lt;span style="color:#f92672">]&lt;/span>
nfc &lt;span style="color:#f92672">[&lt;/span> on | off | toggle &lt;span style="color:#f92672">]&lt;/span>
wifi &lt;span style="color:#f92672">[&lt;/span> on | off | toggle &lt;span style="color:#f92672">]&lt;/span>
wwan &lt;span style="color:#f92672">[&lt;/span> on | off | toggle &lt;span style="color:#f92672">]&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>启用、禁用、切换或检查内置蓝牙、NFC、Wi-Fi 和 WWAN（3G/UMTS、4G/LTE 或 5G）无线电的状态，如果不带参数则为当前硬件状态（硬件需要支持rfkill）&lt;/p>
&lt;h4 id="tlp-stat">TLP-STAT
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#tlp-stat">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>&lt;code>sudo tlp-stat&lt;/code> 查看TLP配置信息，系统信息和内核省电设置以及电池数据&lt;/p>
&lt;p>&lt;code>sudo tlp-stat [-b /--battery]&lt;/code> 查看电池信息，部分电池加&lt;code>-v&lt;/code>参数可以查看电压&lt;/p>
&lt;p>&lt;code>sudo tlp-stat [-c /--config]&lt;/code>查看配置信息&lt;/p>
&lt;p>&lt;code>sudo tlp-stat --cdiff&lt;/code> 查看默认配置和用户配置之间的差异&lt;/p>
&lt;p>&lt;code>sudo tlp-stat [-d /--disk]&lt;/code> 查看硬盘配置信息&lt;/p>
&lt;p>&lt;code>sudo tlp-stat [-e/ --pcie]&lt;/code> 查看Pcie配置信息&lt;/p>
&lt;h2 id="配置">配置
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e9%85%8d%e7%bd%ae">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>TLP最重要的就是其配置文件，可以说，TLP是否节电的关键。 TLP 使用两个根据电源自动应用的设置配置文件：&lt;/p>
&lt;ul>
&lt;li>以&lt;code>_AC&lt;/code>结尾的参数在连接交流电源的时候生效&lt;/li>
&lt;li>以&lt;code>_BAT&lt;/code>结尾的参数在使用电池的时候有效&lt;/li>
&lt;li>既不以&lt;code> _AC &lt;/code>结尾也不以 &lt;code>_BAT&lt;/code> 结尾的参数适用于这两个配置文件&lt;/li>
&lt;/ul>
&lt;h3 id="配置文件">配置文件
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e9%85%8d%e7%bd%ae%e6%96%87%e4%bb%b6">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>按指定顺序从以下文件中读取设置：&lt;/p>
&lt;ul>
&lt;li>Intrinsic defaults 固有默认值（这个配置为TLP自带，不可被更改）&lt;/li>
&lt;li>&lt;code>/etc/tlp.d/*.conf&lt;/code>：插入式自定义片段，按词法（字母顺序）顺序读取，不过建议可以使用一般配置命名方法（00_xxxx.conf)&lt;/li>
&lt;li>&lt;code>/etc/tlp.conf&lt;/code>：用户配置&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>如果多个参数相同，但在同一文件中也存在相同的参数，则最后一个匹配项优先，这也意味着，&lt;code>/etc/tlp.conf&lt;/code> 中的参数将覆盖其他任何内容，因为它是最后读取的
默认的&lt;code>/etc/tlp.conf &lt;/code>中的所有参数都被禁用，删除前导 # 以激活您的更改
/etc/tlp.d/ 目录中的配置文件由用户创建：
* 文件名必须以 .conf 结尾，否则文件将被忽略
* 00-template.conf 作为示例提供&lt;/p>
&lt;/blockquote>
&lt;h3 id="参数默认值">参数默认值
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e5%8f%82%e6%95%b0%e9%bb%98%e8%ae%a4%e5%80%bc">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>配置中有两种参数，一种是具有默认值的，会在本文档中说明，并且在&lt;code>/etc/tlp.conf&lt;/code>中有&lt;code>Default&lt;/code>前缀。还有一种没有默认值的。&lt;/p>
&lt;h3 id="参数语法">参数语法
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e5%8f%82%e6%95%b0%e8%af%ad%e6%b3%95">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>配置文件由参数和注释行组成。&lt;/p>
&lt;h4 id="参数行">参数行
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e5%8f%82%e6%95%b0%e8%a1%8c">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">PARAMETER&lt;span style="color:#f92672">=&lt;/span>value
&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果value包含空格，则需要使用双引号&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">key&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">"111 1111 1111"&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="注释行">注释行
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e6%b3%a8%e9%87%8a%e8%a1%8c">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>以&lt;code>#&lt;/code>开头，在1.6版本后可以在参数行后接&lt;code>#&lt;/code>作为注释&lt;/p>
&lt;h4 id="禁用功能">禁用功能
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e7%a6%81%e7%94%a8%e5%8a%9f%e8%83%bd">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;ul>
&lt;li>没有默认值：使用注释或者删除即可&lt;/li>
&lt;li>有默认值： 赋空值即可 eg：&lt;code>key=&amp;quot;&amp;quot;&lt;/code>&lt;/li>
&lt;/ul>
&lt;h4 id="使用追加配置">使用&lt;code>+=&lt;/code>追加配置
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e4%bd%bf%e7%94%a8%e8%bf%bd%e5%8a%a0%e9%85%8d%e7%bd%ae">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>和bash的环境变量一样，支持使用&lt;code>+=&lt;/code>作为追加配置&lt;/p>
&lt;blockquote>
&lt;p>使用root权限编辑配置文件，在保存更改后可以使用重启，拔插ac电源或者使用&lt;code>sudo tlp start&lt;/code>命令激活配置&lt;/p>
&lt;/blockquote>
&lt;h3 id="配置详解">配置详解
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e9%85%8d%e7%bd%ae%e8%af%a6%e8%a7%a3">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;h4 id="基础操作">基础操作
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e5%9f%ba%e7%a1%80%e6%93%8d%e4%bd%9c">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>默认参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>TLP_ENABLE&lt;/td>
&lt;td>1&lt;/td>
&lt;td>设置为0可禁用TLP（需要重新启动）。未配置时的默认值：1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TLP_WARN_LEVEL&lt;/td>
&lt;td>/&lt;/td>
&lt;td>控制如何发出有关无效设置的警告：
0 - 禁用
1 - 向系统日志/日志报告后台任务（启动、恢复、电源更改）
2 - 外壳命令向终端报告（标准）
3 - 1和2的组合。未配置时的默认值：3&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TLP_DEFAULT_MODE&lt;/td>
&lt;td>/&lt;/td>
&lt;td>定义TLP的默认操作模式（AC或BAT），以防无法检测到电源。仅涉及某些台式机和嵌入式硬件。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TLP_PERSISTENT_DEFAULT&lt;/td>
&lt;td>0&lt;/td>
&lt;td>选择如何确定操作模式：
0 – 根据实际电源应用设置配置文件（默认）
1 – 始终使用TLP_DEFAULT_MODE设置。未配置时的默认值：0&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TLP_PS_IGNORE&lt;/td>
&lt;td>/&lt;/td>
&lt;td>确定工作模式时要忽略的电源等级：（用作错误检测到操作模式 AC 或 BAT 的笔记本电脑的解决方法）
AC
BAT
USB - 仅限版本 1.4 及更高版本。仅限版本 1.4 及更高版本：输入多个类，以空格分隔。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="音频">音频
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e9%9f%b3%e9%a2%91">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>默认参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>SOUND_POWER_SAVE_ON_AC/BAT&lt;/td>
&lt;td>1&lt;/td>
&lt;td>设置为0可禁用音频省电模式（需要重新启动）。未配置时的默认值：1（AC），1（BAT）- 版本 1.4 及更高版本，0（AC），1（BAT）- 版本 1.3。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SOUND_POWER_SAVE_CONTROLLER&lt;/td>
&lt;td>Y&lt;/td>
&lt;td>Y – 关闭控制器和声音芯片的电源
N – 控制器保持活动状态。未配置时的默认值：Y。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>注释： &lt;code>SOUND_POWER_SAVE_ON_AC/BAT&lt;/code> 指的是&lt;code>SOUND_POWER_SAVE_ON_AC&lt;/code> 和 &lt;code>SOUND_POWER_SAVE_ON_BAT&lt;/code>&lt;/p>
&lt;h4 id="电池保养">电池保养
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e7%94%b5%e6%b1%a0%e4%bf%9d%e5%85%bb">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>START_CHARGE_THRESH_BAT&amp;lt;x&amp;gt;&lt;/td>
&lt;td>75&lt;/td>
&lt;td>电池充电水平低于该水平，连接充电器时将开始充电。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>STOP_CHARGE_THRESH_BAT&amp;lt;x&amp;gt;&lt;/td>
&lt;td>80&lt;/td>
&lt;td>电池充电水平，超过该水平，充电器连接时充电将停止。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这些参数用于设置笔记本电脑主/内部电池（BAT0）和辅助电池（BAT1）的充电阈值。启动充电阈值表示在连接充电器时，电池充电水平低于该值时将开始充电。停止充电阈值表示在充电器连接时，电池充电水平超过该值时将停止充电。这些阈值始终具有较低的可用电池容量，因此默认情况下禁用这些设置，并且必须通过删除前导 # 来显式启用这些设置。&lt;/p>
&lt;h4 id="光驱">光驱
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e5%85%89%e9%a9%b1">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>默认参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>BAY_POWEROFF_ON_AC/BAT&lt;/td>
&lt;td>0&lt;/td>
&lt;td>控制光驱在交流电源和电池供电时是否关闭电源。
1：保持光驱开启状态
0：关闭光驱电源&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>BAY_DEVICE&lt;/td>
&lt;td>sr0&lt;/td>
&lt;td>指定光驱设备。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="硬盘">硬盘
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e7%a1%ac%e7%9b%98">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>默认参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>DISK_DEVICES&lt;/td>
&lt;td>&amp;ldquo;nvme0n1 sda&amp;rdquo;&lt;/td>
&lt;td>定义参数作用的磁盘设备。多个设备用空白分隔。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DISK_APM_LEVEL_ON_AC/BAT&lt;/td>
&lt;td>&amp;ldquo;254 254&amp;rdquo;（AC）
&amp;ldquo;128 128&amp;rdquo; (BAT)&lt;/td>
&lt;td>设置“高级电源管理级别”。可能的值介于1和255之间。
1 – 最大省电/最低性能 – 重要提示：此设置可能会导致磁盘驱动器磨损增加，因为读写磁头卸载过多
128 – 省电和磨损之间的折衷（电池的 TLP 标准设置）
192 – 防止某些 HDD 的磁头过度卸载
254 – 最小省电/最大性能（交流电的 TLP 标准设置）
255 – 禁用 APM（某些磁盘型号不支持）
keep – 用于跳过特定磁盘的此设置的特殊值（同义词：&lt;code>_&lt;/code>）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DISK_APM_CLASS_DENYLIST&lt;/td>
&lt;td>&amp;ldquo;usb ieee1394&amp;rdquo;&lt;/td>
&lt;td>从高级电源管理（APM）中排除磁盘类。可能的值：sata、ata、usb、ieee1394。默认为“usb ieee1394”。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DISK_SPINDOWN_TIMEOUT_ON_AC/BAT&lt;/td>
&lt;td>&amp;ldquo;0 0&amp;rdquo;&lt;/td>
&lt;td>磁盘空闲时主轴电机停止的超时值。有效设置：0（已禁用）、1..240（5秒到20分钟）、241..251（30分钟到5.5小时）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DISK_IOSCHED&lt;/td>
&lt;td>&amp;ldquo;keep keep&amp;rdquo;&lt;/td>
&lt;td>两个参数为
多队列 （blk-mq） 调度器：&lt;code>mq-deadline&lt;/code> 、&lt;code>none&lt;/code>、&lt;code>kyber&lt;/code>、&lt;code>bfq&lt;/code>、&lt;code>keep&lt;/code>
单队列调度程序：&lt;code>deadline&lt;/code>、&lt;code>cfq&lt;/code>、&lt;code>bfq&lt;/code>、&lt;code>noop&lt;/code>、&lt;code>keep&lt;/code>
如果未配置，默认情况下所有磁盘将使用内核的默认调度程序。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SATA_LINKPWR_ON_AC/BAT&lt;/td>
&lt;td>&amp;ldquo;med_power_with_dipm&amp;rdquo;&lt;/td>
&lt;td>设置SATA链路的电源管理模式。可能的值包括：max_performance、medium_power、med_power_with_dipm、min_power。默认为med_power_with_dipm。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SATA_LINKPWR_DENYLIST&lt;/td>
&lt;td>&amp;ldquo;host1&amp;rdquo;&lt;/td>
&lt;td>从AHCI链路电源管理（ALPM）中排除SATA磁盘的主机列表。默认为空。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AHCI_RUNTIME_PM_ON_AC&lt;/td>
&lt;td>&amp;ldquo;on&amp;rdquo;&lt;/td>
&lt;td>控制NVMe、SATA、ATA和USB磁盘以及SATA端口的运行时电源管理。可能的值包括：auto（启用）、on（禁用）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AHCI_RUNTIME_PM_ON_BAT&lt;/td>
&lt;td>&amp;ldquo;auto&amp;rdquo;&lt;/td>
&lt;td>同上&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AHCI_RUNTIME_PM_TIMEOUT&lt;/td>
&lt;td>15&lt;/td>
&lt;td>磁盘或端口挂起前的不活动时间（秒）。仅在激活AHCI_RUNTIME_PM_ON_AC/BAT时有效。默认为15。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>注释：DISK_IOSCHED 如果使用是NVME设备时，最好使用无IO调度程序来减少CPU开销（none和noop）&lt;/p>
&lt;h4 id="文件系统">文件系统
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e6%96%87%e4%bb%b6%e7%b3%bb%e7%bb%9f">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>默认参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>DISK_IDLE_SECS_ON_AC/BAT&lt;/td>
&lt;td>0 (AC), 2 (battery)&lt;/td>
&lt;td>笔记本电脑模式等待磁盘空闲的秒数，然后再次将脏缓存块从 RAM 同步到磁盘。值大于0将激活内核笔记本电脑模式。请勿更改此设置。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>MAX_LOST_WORK_SECS_ON_AC/BAT&lt;/td>
&lt;td>15 (AC), 60 (battery)&lt;/td>
&lt;td>将文件系统缓冲区中未保存的数据写入磁盘的超时时间（秒）。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="图形显卡">图形显卡
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e5%9b%be%e5%bd%a2%e6%98%be%e5%8d%a1">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>默认参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>INTEL_GPU_MIN_FREQ_ON_AC/BAT&lt;/td>
&lt;td>0&lt;/td>
&lt;td>设置 Intel GPU 的最小频率。可能的值取决于硬件。通过运行 &lt;code>tlp-stat -g&lt;/code> 命令查看可用频率。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>INTEL_GPU_MAX_FREQ_ON_AC/BAT&lt;/td>
&lt;td>0&lt;/td>
&lt;td>设置 Intel GPU 的最大频率。可能的值取决于硬件。通过运行 &lt;code>tlp-stat -g&lt;/code>命令查看可用频率。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>INTEL_GPU_BOOST_FREQ_ON_AC/BAT&lt;/td>
&lt;td>0&lt;/td>
&lt;td>设置 Intel GPU 的睿频频率。可能的值取决于硬件。通过运行 &lt;code>tlp-stat -g&lt;/code> 命令查看可用频率。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RADEON_DPM_PERF_LEVEL_ON_AC/BAT&lt;/td>
&lt;td>auto&lt;/td>
&lt;td>控制 AMD GPU 的动态电源管理（DPM）性能级别。支持 amdgpu（仅限 TLP 版本 1.4 及更高版本）和 radeon 驱动程序。可能的值包括 auto、low、high。默认值：auto。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RADEON_DPM_STATE_ON_AC/BAT&lt;/td>
&lt;td>performance (AC), battery (BAT)&lt;/td>
&lt;td>控制 AMD GPU 的电源管理方法。可能的值包括 battery、balanced、performance。默认值：performance（AC）、battery（BAT）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RADEON_POWER_PROFILE_ON_AC/BAT&lt;/td>
&lt;td>default&lt;/td>
&lt;td>控制 AMD GPU 的时钟。仅在旧版 ATI 硬件上受 radeon 驱动程序支持（DPM 不可用）。可能的值包括 low、mid、high、auto、default。默认值：default。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这些参数允许用户调整 Intel GPU 和 AMD GPU 在交流电和电池模式下的性能和电源管理行为。在配置这些参数时，建议参考硬件规格和运行 &lt;code>tlp-stat -g&lt;/code> 查看可用频率。&lt;/p>
&lt;h4 id="kernel">kernel
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#kernel">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>默认参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>NMI_WATCHDOG&lt;/td>
&lt;td>0&lt;/td>
&lt;td>激活内核 NMI 看门狗定时器。设置为 0 表示禁用，有助于节省电源。设置为 1 表示启用，对于内核调试和看门狗守护程序是相关的。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>不建议关闭watchdog 否则可能导致内核崩溃后无法自动重启和内核调试&lt;/p>
&lt;h4 id="网络">网络
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e7%bd%91%e7%bb%9c">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>默认参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>WIFI_PWR_ON_AC/BAT&lt;/td>
&lt;td>off (AC),&lt;/td>
&lt;td>设置 Wi-Fi 的电源保存模式。可能的值包括 off（禁用）和 on（启用）。默认值：off（AC）、on（BAT）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td>on (BAT)&lt;/td>
&lt;td>提示：支持已弃用的配置值 1=off/5=on，以实现向后兼容性。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>WOL_DISABLE&lt;/td>
&lt;td>Y&lt;/td>
&lt;td>控制是否禁用 Wake-on-LAN（LAN 唤醒）。可能的值包括 Y（禁用）和 N（不禁用，保持 BIOS 默认）。默认值：Y。
注意：更改为 WOL_DISABLE=N 后，需要重新启动才能使新设置生效（或在 shell 中使用 &lt;code>sudo ethtool -s wol g&lt;/code>）。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这些参数允许用户配置Wi-Fi的电源保存模式和控制Wake-on-LAN（LAN唤醒）功能。&lt;/p>
&lt;h4 id="平台">平台
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e5%b9%b3%e5%8f%b0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>默认参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>PLATFORM_PROFILE_ON_AC/BAT&lt;/td>
&lt;td>performance&lt;/td>
&lt;td>选择平台配置文件以控制系统的功率/性能级别、散热和风扇速度的运行特性。可能的值包括 performance、balanced、low-power。默认值：performance（AC）、low-power（BAT）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>MEM_SLEEP_ON_AC/BAT&lt;/td>
&lt;td>s2idle&lt;/td>
&lt;td>选择系统挂起模式。可能的值包括 s2idle（空闲待机）和 deep（挂起到 RAM）。注意：更改挂起模式可能导致系统不稳定和数据丢失。请使用 tlp-stat -s 检查系统上不同模式的可用性。如果不确定，请坚持使用系统默认值。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>其实如果能使用S3休眠那就更好，不过现在很多厂商并不支持S3,所以如果能用S2那就用S2吧。&lt;/p>
&lt;h4 id="处理器">处理器
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e5%a4%84%e7%90%86%e5%99%a8">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>默认参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>CPU_DRIVER_OPMODE_ON_AC/BAT&lt;/td>
&lt;td>active (amd-pstate), active (intel_pstate)&lt;/td>
&lt;td>选择 CPU 缩放驱动程序操作模式。配置取决于活动驱动程序：对于 amd-pstate（Active 模式），可能的值为 active 和 passive；对于 intel_pstate（Active 模式），可能的值为 active、passive 和 guided。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CPU_SCALING_GOVERNOR_ON_AC/BAT&lt;/td>
&lt;td>powersave&lt;/td>
&lt;td>选择用于自动频率缩放的 CPU 缩放调节器。配置取决于活动驱动程序。可能的值包括 performance、powersave、conservative、ondemand、userspace 和 schedutil。默认值：powersave（AC）、powersave（BAT）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CPU_SCALING_MIN/MAX_FREQ_ON_AC/BAT&lt;/td>
&lt;td>0, 9999999&lt;/td>
&lt;td>设置可用于缩放调控器的最小/最大频率。可能的值取决于您的 CPU。请查阅&lt;code>tlp-stat -p&lt;/code>的输出以获取可用频率。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CPU_ENERGY_PERF_POLICY_ON_AC/BAT&lt;/td>
&lt;td>balance_performance&lt;/td>
&lt;td>设置 CPU 能耗/性能策略。可能的值包括 performance、balance_performance、default、balance_power 和 power。默认值：balance_performance（AC）、balance_power（BAT）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CPU_MIN/MAX_PERF_ON_AC/BAT&lt;/td>
&lt;td>0, 100&lt;/td>
&lt;td>定义 Intel CPU 的最小/最大 P 状态，表示为总可用处理器性能的百分比。建议仅用于限制 CPU 的功耗。可能的值在 0 到 100 之间。默认值：0 到 100（AC）、0 到 30（BAT）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CPU_BOOST_ON_AC/BAT&lt;/td>
&lt;td>1&lt;/td>
&lt;td>配置 CPU “turbo boost”（Intel）或“turbo core”（AMD）功能。可能的值为 0（禁用）和 1（允许）。请注意，值为 1 不会激活提升，只是允许它。默认值：1（AC）、0（BAT）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CPU_HWP_DYN_BOOST_ON_AC/BAT&lt;/td>
&lt;td>1&lt;/td>
&lt;td>配置 Intel CPU HWP 动态提升功能。可能的值为 0（禁用）和 1（启用）。要求 Intel Core i 第 6 代（“Skylake”）或更新的 CPU，在活动模式下具有 intel_pstate 扩展驱动程序。默认值：1（AC）、0（BAT）。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这些参数允许用户配置 CPU 的性能和功耗特性，包括缩放驱动程序操作模式、调节器、频率范围、能耗/性能策略、P 状态范围、提升功能以及 HWP 动态提升功能。&lt;/p>
&lt;blockquote>
&lt;p>部分电脑的BIOS会干预PState 所以需要检查自己的CPU是否支持&lt;/p>
&lt;/blockquote>
&lt;h4 id="无线设备">无线设备
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e6%97%a0%e7%ba%bf%e8%ae%be%e5%a4%87">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>默认参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>RESTORE_DEVICE_STATE_ON_STARTUP&lt;/td>
&lt;td>0&lt;/td>
&lt;td>在启动时从上次关机中恢复无线电设备状态。可能的值为 0（禁用）和 1（启用）。默认值：0。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_DISABLE_ON_STARTUP&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>在启动时禁用内置无线电设备。可能的值包括 bluetooth、wifi 和 wwan，多个设备用空白分隔。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_ENABLE_ON_STARTUP&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>在启动时启用内置无线电设备。可能的值与上述相同，用于启用在默认情况下禁用的设备。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_ENABLE_ON_AC&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>插入交流电源时启用内置无线电设备。可能的值与上述相同。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_DISABLE_ON_BAT&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>在更改为电池电源时禁用内置无线电设备，无论其连接状态如何。可能的值与上述相同。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_DISABLE_ON_BAT_NOT_IN_USE&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>在更改为电池电源时禁用未连接的内置无线电设备。可能的值与上述相同。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这些参数允许用户配置在系统启动、关闭或更改电源状态时如何处理内置的蓝牙、Wi-Fi 和 WWAN 设备。可通过设置禁用或启用这些设备，以及在何种条件下执行这些操作。&lt;/p>
&lt;h4 id="无线配置向导自动化配置">无线配置向导（自动化配置）
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e6%97%a0%e7%ba%bf%e9%85%8d%e7%bd%ae%e5%90%91%e5%af%bc%e8%87%aa%e5%8a%a8%e5%8c%96%e9%85%8d%e7%bd%ae">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>参考参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>DEVICES_TO_DISABLE_ON_LAN_CONNECT&lt;/td>
&lt;td>&amp;ldquo;wifi wwan&amp;rdquo;&lt;/td>
&lt;td>当建立 LAN 连接时，禁用蓝牙、Wi-Fi 和 WWAN 设备。多个设备用空白分隔。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_DISABLE_ON_WIFI_CONNECT&lt;/td>
&lt;td>&amp;ldquo;wwan&amp;rdquo;&lt;/td>
&lt;td>当建立 Wi-Fi 连接时，禁用 WWAN 设备。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_DISABLE_ON_WWAN_CONNECT&lt;/td>
&lt;td>&amp;ldquo;wifi&amp;rdquo;&lt;/td>
&lt;td>当建立 WWAN 连接时，禁用 Wi-Fi 设备。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_ENABLE_ON_LAN_DISCONNECT&lt;/td>
&lt;td>&amp;ldquo;wifi wwan&amp;rdquo;&lt;/td>
&lt;td>当断开 LAN 连接时，启用蓝牙、Wi-Fi 和 WWAN 设备。多个设备用空白分隔。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_ENABLE_ON_WIFI_DISCONNECT&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>当断开 Wi-Fi 连接时，启用所有设备。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_ENABLE_ON_WWAN_DISCONNECT&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>当断开 WWAN 连接时，启用所有设备。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_ENABLE_ON_DOCK&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>在对接后，启用所有设备。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_DISABLE_ON_DOCK&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>在对接后，禁用所有设备。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_ENABLE_ON_UNDOCK&lt;/td>
&lt;td>&amp;ldquo;wifi&amp;rdquo;&lt;/td>
&lt;td>在取消对接后，启用 Wi-Fi 设备。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DEVICES_TO_DISABLE_ON_UNDOCK&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>在取消对接后，禁用所有设备。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这些参数允许用户配置在特定事件触发时如何处理内置的蓝牙、Wi-Fi 和 WWAN 设备。用户可以根据 LAN、Wi-Fi 或 WWAN 的连接状态、对接或取消对接等事件来启用或禁用这些设备。&lt;/p>
&lt;h4 id="pcie电源配置">PCIE电源配置
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#pcie%e7%94%b5%e6%ba%90%e9%85%8d%e7%bd%ae">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>默认参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>RUNTIME_PM_ON_AC&lt;/td>
&lt;td>on&lt;/td>
&lt;td>控制 PCIe 设备的运行时电源管理。可能的值：auto（启用）或 on（禁用）。未配置时的默认值：on（AC）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RUNTIME_PM_ON_BAT&lt;/td>
&lt;td>auto&lt;/td>
&lt;td>控制 PCIe 设备的运行时电源管理。可能的值：auto（启用）或 on（禁用）。未配置时的默认值： auto（BAT）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RUNTIME_PM_DENYLIST&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>从运行时电源管理中排除列出的 PCIe 设备地址。使用 lspci 查找地址。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RUNTIME_PM_DRIVER_DENYLIST&lt;/td>
&lt;td>&amp;ldquo;mei_me nouveau radeon&amp;rdquo;&lt;/td>
&lt;td>从运行时电源管理中排除分配给所列驱动程序的 PCIe 设备。使用 tlp-stat -e 查找驱动程序。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RUNTIME_PM_ENABLE&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>为列表中的 PCI（e） 设备地址永久启用（自动）运行时 PM。这优先于所有先前的运行时 PM 设置。使用 lspci 获取地址。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RUNTIME_PM_DISABLE&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>为列表中的 PCI（e） 设备地址永久禁用（on）运行时 PM。与 RUNTIME_PM_ENABLE 类似，不过是禁用。使用 lspci 获取地址。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PCIE_ASPM_ON_AC&lt;/td>
&lt;td>default&lt;/td>
&lt;td>设置 PCIe ASPM 省电模式。可能的值：default（推荐）、performance（性能）、powersave（省电）和 powersupersave（PowerSuperSave，超级省电）。未配置时的默认值：default。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PCIE_ASPM_ON_BAT&lt;/td>
&lt;td>default&lt;/td>
&lt;td>设置 PCIe ASPM 省电模式。可能的值：default（推荐）、performance（性能）、powersave（省电）和 powersupersave（PowerSuperSave，超级省电）。未配置时的默认值：default。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>这些参数允许用户配置与 PCIe 设备相关的运行时电源管理和 ASPM 等功能。用户可以根据电源来源、设备地址、驱动程序等来调整这些设置，以实现更好的功耗管理。（建议不要对nvidia驱动进行调整，可能会引发意外）&lt;/p>
&lt;h4 id="usb">USB
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#usb">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数名称&lt;/th>
&lt;th>默认参数值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>USB_AUTOSUSPEND&lt;/td>
&lt;td>1&lt;/td>
&lt;td>在启动时和插入时为 USB 设备设置自动挂起模式。可能的值：1（启用）或 0（禁用）。未配置时的默认值：1。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>USB_DENYLIST&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>从自动挂起模式中排除 USB 设备 ID。使用 tlp-stat -u 查找 ID。多个 ID 用空格分隔。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>USB_EXCLUDE_AUDIO&lt;/td>
&lt;td>1&lt;/td>
&lt;td>从自动挂起模式中排除音频设备：1（排除）或 0（不排除）。未配置时的默认值：1。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>USB_EXCLUDE_BTUSB&lt;/td>
&lt;td>0&lt;/td>
&lt;td>从自动挂起模式中排除蓝牙设备：1（排除）或 0（不排除）。未配置时的默认值：0。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>USB_EXCLUDE_PHONE&lt;/td>
&lt;td>0&lt;/td>
&lt;td>将智能手机从自动挂起模式中排除以启用充电：1（排除）或 0（不排除）。未配置时的默认值：0。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>USB_EXCLUDE_PRINTER&lt;/td>
&lt;td>1&lt;/td>
&lt;td>从自动挂起模式中排除打印机：1（排除）或 0（不排除）。未配置时的默认值：1。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>USB_EXCLUDE_WWAN&lt;/td>
&lt;td>0&lt;/td>
&lt;td>从自动挂起模式中排除内置 WWAN 设备：1（排除）或 0（不排除）。未配置时的默认值：0。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>USB_ALLOWLIST&lt;/td>
&lt;td>&amp;quot;&amp;quot;&lt;/td>
&lt;td>为已被上述任何设置排除的 USB 设备 ID 重新启用自动挂起模式。使用 &lt;code>tlp-stat -u&lt;/code> 查找 ID。多个 ID 用空格分隔。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>USB_AUTOSUSPEND_DISABLE_ON_SHUTDOWN&lt;/td>
&lt;td>0&lt;/td>
&lt;td>在系统关闭时禁用 USB 自动挂起模式：1（启用）或 0（禁用）。未配置时的默认值：0。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="trace-mode">Trace Mode
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#trace-mode">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>&lt;code>TLP_DEBUG=&amp;quot;arg bat disk lock nm path pm ps rf run sysfs udev usb&amp;quot;&lt;/code>&lt;/p>
&lt;h2 id="结语">结语
&lt;span>
&lt;a href="https://blog.deepin.org/posts/tlp-power-management/#%e7%bb%93%e8%af%ad">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>我们对于系统的优化不仅于此，现阶段tlp的配置策略仅对于部分有能力的用户公开，后续经过充分的测试和调优之后，会提供几份默认的配置给普通用户使用。并将来将这些配置文件GUI化，集成于深度定制项目中，为用户提供更为方便直观的操作体验。&lt;/p>
&lt;p>从这一阶段对于电源优化的探索可以看出，deepin系统的电源管理方案优化不仅是为了解决用户反馈的问题，更是一种对用户需求的回应和尊重。在未来，deepin系统将继续秉持用户至上的原则，不断提升系统的性能和用户体验，为广大用户提供更加优秀的操作系统产品。&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>关于 DDUC2023 和 QtWS23 分享内容的一些想法</title><link>https://blog.deepin.org/posts/deepin%E4%B8%8Eqt/</link><pubDate>Thu, 30 Nov 2023 00:00:00 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/deepin%E4%B8%8Eqt/</guid><description>&lt;p>2023 年 11 月 21 日，Qt Group 在上海成功举办了“Qt 全球峰会 2023 中国站”，本次大会上发布了关于 Qt 开放框架与开发工具的战略，集中展示了最新的产品、解决方案，阐述了它们在优化软件跨平台开发全流程、提升研发团队跨职能协作效率以及加速产品迭代速度等方面的强大优势，同时大会上也分享了对于人工智能如何影响软件开发和测试的思考。&lt;/p>
&lt;p>&lt;img alt="pic1" src="https://blog.deepin.org/posts/deepin%E4%B8%8Eqt/1.png" />&lt;/p>
&lt;p>Qt 作为 Linux 上最重要的开发工具之一，为中国信创产业的发展和创新提供了更多的可能性和选择。Qt Group 中国区负责人许晟在开幕致辞中表示，“Qt 中国作为 Qt Group 在中国的本地化团队，不仅通过源代码交付帮助本地企业实现软件的自主创新，还基于强大的跨平台能力支持针对本地操作系统和主流芯片的开发，为中国信创产业的发展和创新提供了更多的可能性和选择。&lt;/p>
&lt;p>&lt;img alt="pic2" src="https://blog.deepin.org/posts/deepin%E4%B8%8Eqt/2.png" />&lt;/p>
&lt;p>值得一提的是，deepin 已为 Qt社区贡献 100000+ 行代码，在贡献者中名列前茅。此外，deepin 社区也基于 Qt 开发了一整套简单且实用的通用开发框架 Development ToolKit（简称DTK），DTK 处于deepin系统中的核心位置。DDE 中超过30+组件，如浏览器、音乐、邮件等40余款原生应用全部使用 DTK 开发。为此，deepin（深度）社区在&lt;a href="https://blog.deepin.org/posts/deepin%E4%B8%8Eqt/docs.deepin.org"> deepin 开发者平台&lt;/a>上专门提供了在 deepin 上进行 Qt 开发的文档指导。&lt;/p>
&lt;p>2023 年 11 月 18 日，deepin（深度）社区在北京798艺术中心举办了第十三届深度开发者与用户大会（Deepin Developer&amp;amp;User Conference，简称DDUC），在本次大会上，deepin社区也是公开了近一年在 DTK 和 Qt 这块的建设成果，目前 DTK 已正式适配 Qt6(6.4.2)，实现全面升级，相关介绍可以查看&lt;a href="https://www.deepin.org/zh/2023-9-7/">《deepin（深度）宣布 deepin DTK 已完成基于 Qt6 的全面升级！》&lt;/a>。&lt;/p>
&lt;p>&lt;img alt="pic3" src="https://blog.deepin.org/posts/deepin%E4%B8%8Eqt/3.png" />&lt;/p>
&lt;p>在本次 QtWS23 峰会上，Qt Group 资深开发工程师齐亮介绍了Qt Wayland 的最新进展。近些年 Linux 桌面发行版迁移到 Wayland 的趋势愈发明显，大有替代 X11 之势。Ubuntu 目前已经默认 Wayland 会话，GNOME 在 Wayland 支持这块一直走在 Linux 发行版的前列，且动作非常激进，目前 GNOME 桌面已经对外宣布将移除对 &lt;a href="https://x.org/">X.Org&lt;/a> 会话支持，默认使用 Wayland。X11 诞生于 1984 年，其设计符合当时的硬件环境、用户需求。随着技术的发展，出现了很多问题，特别是在桌面环境里面，需要 Xorg、窗口管理器、桌面环境组件三者之间进行复杂的交互、协作，导致演进困难。同时 X11 对于新特性的支持较差，Hi-DPI 体验糟糕，并且完全不支持 HDR。&lt;/p>
&lt;p>事实上，在 2023 年5 月 17 日，deepin（深度）社区在推出 deepin V23 Beta 版的同时也已经支持了 Wayland 桌面环境。在 V23 beta 版本中，DDE 试验性的开启了 Wayland 的支持，允许用户在 Wayland 协议下的桌面工作环境启动 。在 deepin V23 中支持 Wayland，是 DDE 的一个非常重要的特性，也是今后 deepin 团队的工作重点，以实现 X11 版本的完全替代，提升 DDE 的优质体验，在 Wayland 桌面环境领域达到领先水平。&lt;/p>
&lt;blockquote>
&lt;p>DDE（deepin desktop environment）是deepin（深度）社区自主开发的美观易用、极简操作的桌面环境，主要由桌面、启动器、任务栏、控制中心、窗口管理器等组成，系统中预装了深度特色应用，它既能让您体验到丰富多彩的娱乐生活，也可以满足您的日常工作需要。&lt;/p>
&lt;/blockquote>
&lt;p>在此背景下，deepin 团队在今年 DDUC 大会了宣布将推出 Treeland 作为今后 DDE 所有功能开发的核心。Treeland 的底层基于 wlroots，并与 Qt Quick 进行了绑定，可同时兼顾两者的优点，wlroots 是 Wayland 生态中发展最迅速的开发库之一，具有功能丰富、演进速度快等优势，将其与 Qt Quick 结合则可以弥补 wlroots 在 GUI 能力方面的欠缺，极大的降低 Wayland 合成器的开发难度，实现 Vulkan、OpenGL ES2、软件渲染等多种渲染方式的无缝切换。&lt;/p>
&lt;p>&lt;img alt="pic4" src="https://blog.deepin.org/posts/deepin%E4%B8%8Eqt/4.png" />&lt;/p>
&lt;p>DDE 的新架构，将桌面环境各技术领域的组件进行了统一设计，允许桌面环境开发人员对其进行完全掌控，可轻松实现设备共享、多端无缝协同等高级功能。&lt;/p>
&lt;p>&lt;img alt="pic5" src="https://blog.deepin.org/posts/deepin%E4%B8%8Eqt/5.png" />&lt;/p>
&lt;p>未来，deepin（深度）社区将继续与最新技术保持同步，持续推进 DTK 的改进优化，也期待更多感兴趣的朋友加入到 deepin 开源社区中来，讨论更多内容，为推动生态发展贡献力量。&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>deepin 5g wwan 支持</title><link>https://blog.deepin.org/posts/5g-support/</link><pubDate>Wed, 08 Nov 2023 00:00:00 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/5g-support/</guid><description>&lt;p>本文简述了deepin v23系统使用5G WWAN网卡连接互联网的方式，仅仅作为一个临时解决方案和技术验证使用，后期会在系统中内置此功能。&lt;/p>
&lt;h2 id="第一步安装modemmanager">第一步：安装modemmanager
&lt;span>
&lt;a href="https://blog.deepin.org/posts/5g-support/#%e7%ac%ac%e4%b8%80%e6%ad%a5%e5%ae%89%e8%a3%85modemmanager">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>modemmanager是一个由freedesktop托管的项目，旨在在linux设备上运行调制解调器以让linux设备获得蜂窝无线网络连接的能力，所以我们第一步骤就是安装此软件。目前deepin已经支持此软件最新版本：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">sudo apt install modemmanager
&lt;/code>&lt;/pre>&lt;/div>&lt;p>同时，你需要确保你的内核模块已经正确加载了你的WWAN驱动，你可以通过使用&lt;code>lspci -vvv&lt;/code>查看详细信息&lt;/p>
&lt;p>在安装了modemmanager后，你可以通过&lt;code>mmcli&lt;/code> 命令使用命令行与其交互，使用&lt;code>mmcli —help-all&lt;/code>来获取全部的帮助选项。&lt;/p>
&lt;h2 id="第二步使用mmcli连接5g网络">第二步：使用mmcli连接5G网络
&lt;span>
&lt;a href="https://blog.deepin.org/posts/5g-support/#%e7%ac%ac%e4%ba%8c%e6%ad%a5%e4%bd%bf%e7%94%a8mmcli%e8%bf%9e%e6%8e%a55g%e7%bd%91%e7%bb%9c">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>使用&lt;code>mmcli -L&lt;/code> 获取你的wwan卡信息，返回的信息有三个内容，分别为：DBus地址、设备类型 、设备ID，其中DBus地址的最后一位为设备编号，你可以使用&lt;code>mmcli --modem=&amp;lt;设备编号&amp;gt;&lt;/code>的方式查看设备详细支持信息。&lt;/p>
&lt;p>如果你的SIM卡设置了PIN锁，则需要在连接网络之前使用&lt;code>mmcli --modem=0 --sim=0 --pin=****&lt;/code> 的方式连接，而后我们就可以启动相关设备了：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">mmcli --modem&lt;span style="color:#f92672">=&lt;/span>&amp;lt;设备编号&amp;gt; --enable
&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后你需要使用simple connect连接网络&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">mmcli -m &amp;lt;设备编号&amp;gt; --simple-connect&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">'apn=&amp;lt;apn名&amp;gt;,ip-type=ipv4v6'&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>比如我的连接方式为&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">mmcli -m &lt;span style="color:#ae81ff">4&lt;/span> --simple-connect&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">'apn=ctnet,ip-type=ipv4v6'&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>此时你再使用&lt;code>mmcli -m &amp;lt;设备编号&amp;gt;&lt;/code> 查看信息的时候 可以查看Bearer的信息，Bearer的DBus的最后一位为其编号。使用命令查看Bearer的信息：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">mmcli -m &amp;lt;设备编号&amp;gt; -b &amp;lt;Bearer编号&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果你看到Bearer相关信息，就几乎接近成功了：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-text" data-lang="text"> ------------------------------------
General | path: /org/freedesktop/ModemManager1/Bearer/0
| type: default
------------------------------------
Status | connected: yes
| suspended: no
| multiplexed: no
| interface: wwan0
| ip timeout: 20
------------------------------------
Properties | apn: ctnet
| roaming: allowed
| ip type: ipv4
| allowed-auth: none, pap, chap, mschap, mschapv2, eap
| user: ctnet@mycdma.cn
| password: vnet.mobi
------------------------------------
IPv4 configuration | method: static
| address: 10.122.58.19
| prefix: 8
| gateway: 10.122.58.17
| dns: 202.103.24.68, 202.103.44.150
| mtu: 1420
------------------------------------
Statistics | start date: 2023-11-07T05:40:08Z
| duration: 1260
| uplink-speed: 1250000000
| downlink-speed: 4670000000
| attempts: 1
| total-duration: 1260
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="第三步使用nmcli打开连接">第三步：使用nmcli打开连接
&lt;span>
&lt;a href="https://blog.deepin.org/posts/5g-support/#%e7%ac%ac%e4%b8%89%e6%ad%a5%e4%bd%bf%e7%94%a8nmcli%e6%89%93%e5%bc%80%e8%bf%9e%e6%8e%a5">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>networkmanager对mm是有做支持，在你完成上述步骤之后，可以通过&lt;code>ip a&lt;/code>命令查看，可以看到一个被down掉的接口，我们使用nmcli查看其详细信息：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">nmcli device show
&lt;/code>&lt;/pre>&lt;/div>&lt;p>你就可以 看到一个以wwan开头的设备：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-text" data-lang="text">GENERAL.DEVICE: wwan0mbim0
GENERAL.TYPE: gsm
GENERAL.HWADDR: （未知）
GENERAL.MTU: 1420
GENERAL.STATE: 100（已连接）
GENERAL.CONNECTION: wwan0mbim0
GENERAL.CON-PATH: /org/freedesktop/NetworkManager/ActiveConnection/3
IP4.ADDRESS[1]: 10.122.58.19/8
IP4.GATEWAY: 10.122.58.17
IP4.ROUTE[1]: dst = 10.0.0.0/8, nh = 0.0.0.0, mt = 700
IP4.ROUTE[2]: dst = 0.0.0.0/0, nh = 10.122.58.17, mt = 700
IP4.DNS[1]: 202.103.24.68
IP4.DNS[2]: 202.103.44.150
IP6.GATEWAY: --
&lt;/code>&lt;/pre>&lt;/div>&lt;p>使用下述命令打开此设备&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">nmcli d connect &amp;lt;设备名&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>上述设备名就是以wwan开头的设备&lt;/p>
&lt;p>然后你就可以使用5G网络连接了&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>xtrace</title><link>https://blog.deepin.org/posts/xtrace/</link><pubDate>Sat, 04 Nov 2023 02:19:31 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/xtrace/</guid><description>&lt;h2 id="介绍">介绍
&lt;span>
&lt;a href="https://blog.deepin.org/posts/xtrace/#%e4%bb%8b%e7%bb%8d">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>xtrace是一个用于跟踪分析X11图形协议通信的工具,它可以监控和记录X11服务器上的各种场景,以帮助开发人员诊断和调试与图形界面相关的问题。作为一款强大的工具，xtrace可用于逆向工程、调试分析、性能分析等领域。在Linux X11系统中，xtrace能够记录一个程序在运行时所发起的X11协议请求和XServer发送给程序的事件，以及这些调用的参数。这对于在不阅读源码情况排查程序中的问题、理解程序行为、分析性能瓶颈以及进行协议审计都非常有用。笔者在工作过程中使用该工具深度剖析过腾讯会议、simplescreenrecorder等应用程序的实现，在没有阅读代码的前提下可以获得软件录屏的工作流程，配合阅读常规的X11录屏代码，可分析出其部分工功能异常原因，这些经验在xwayland适配X11应用程序截图录屏项目中通过实战解决了一系列问题。&lt;/p>
&lt;p>本文主要介绍的是X11协议的监视工具，希望您在读完本篇文章后可以对如何监控X11协议有比较深刻的认知，在工作中经常会碰到一些X11应用程序运行时功能异常，在没有应用程序源码的情况下通过xtrace进行调试是一个不错的选择，希望在阅读完这篇文章后，能丰富您的调试技巧。祝您阅读愉快！&lt;/p>
&lt;p>xtrace的安装和使用非常简单，打开终端像如下输入命令即可启动工具：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">// UOS上安装xtrace，deepin上没有可以在http://snapshot.debian.org/上下载
sudo apt install xtrace
// 运行之后协议电报内容会存储在/tmp/dde-calendar.xtrace中
xtrace -n -o /tmp/dde-calendar.xtrace dde-calendar
&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>命令行选项&lt;/th>
&lt;th>含义或用途&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&amp;ndash;display, -d&lt;/td>
&lt;td>用于ssh远程调试，例如:xtrace -d :0 dde-calendar&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;ndash;outfile, -o&lt;/td>
&lt;td>用于将通信内容转存到磁盘，例如:xtrace -o /tmp/x.log dde-calendar&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;ndash;stopwhendone, -s&lt;/td>
&lt;td>进程退出后停止xtrace，例如:xtrace -s dde-calendar&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="工作原理">工作原理
&lt;span>
&lt;a href="https://blog.deepin.org/posts/xtrace/#%e5%b7%a5%e4%bd%9c%e5%8e%9f%e7%90%86">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>如图1所示，X11其主要有两种通信方式，在同一个计算机内主要使用unix domain socket进行通信；在不同计算机之间使用TCP/IP通信。只需要“截获”通信消息，将其转为易于读取的格式输出即可达到监控目的。这是xtrace工作的基本原理。
&lt;figure>&lt;img src="https://blog.deepin.org/posts/xtrace/x11-frame.jpg" />&lt;figcaption>
&lt;h4>图1. 不同客户端使用同一个XServer显示器框架图&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;/p>
&lt;p>如下图2是X11窗口创建的基本流程，因为篇幅限制，图中只绘制了基础的请求和事件，但X11协议远比图中绘制的复杂，笔者在此只介绍一下基础的协议交互模型，方便在读者在查看xtrace追踪日志时有基础的认知。
&lt;figure>&lt;img src="https://blog.deepin.org/posts/xtrace/client-server.jpg" />&lt;figcaption>
&lt;h4>图2. X11协议交互流程&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;/p>
&lt;p>总之xtrace是Xorg X Server自带的一个工具，通过分析xtrace的输出，你可以了解X Server是如何处理客户端应用程序的请求，以及可能的问题所在。xtrace 工具的作用：&lt;/p>
&lt;ul>
&lt;li>调试X问题：如果你遇到了与图形界面相关的问题，例如窗口无法正常显示、图形卡驱动问题等，你可以使用xtrace来捕获X Server的活动，帮助你找出问题所在。&lt;/li>
&lt;li>性能分析：xtrace可以记录X Server内部的操作，帮助你分析系统的图形性能，找出潜在的瓶颈。&lt;/li>
&lt;li>理解X协议交互：X Server与客户端应用程序之间的通信是通过X协议进行的。xtrace可以捕获这些通信，帮助你理解应用程序与X Server之间的交互方式。&lt;/li>
&lt;li>功能逆向：对于依赖X11协议的软件无法查看源码，可以通过观察通信协议，逆向其部分核心功能的实现
请注意，xtrace的输出可能会非常详细，因此在使用时需要注意过滤和分析输出，以便关注于你感兴趣的信息。&lt;/li>
&lt;/ul>
&lt;p>X11在文件系统中暴露了通信用的socket套接字文件，这使得第三方应用程序可以监控套接字文件从而“窥视&amp;quot;X11客户端和服务端之间的通信，这一技术点是xtrace工作的主要基础。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">// X11 socket套接字文件在文件系统中的位置
ls /tmp/.X11-unix
X0 X9
&lt;/code>&lt;/pre>&lt;/div>&lt;p>如下日志所示xtrace工作的本质就是不断地获取wrote和received的数据然后将其解析为易于阅读的描述语言打印出来。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">// 全量日志
001:&amp;gt;:received &lt;span style="color:#ae81ff">32&lt;/span> bytes
001:&amp;gt;:09dc:32: Reply to InternAtom: atom&lt;span style="color:#f92672">=&lt;/span>0x250&lt;span style="color:#f92672">(&lt;/span>&lt;span style="color:#e6db74">"_NET_KDE_COMPOSITE_TOGGLING"&lt;/span>&lt;span style="color:#f92672">)&lt;/span>
001:&amp;gt;:wrote &lt;span style="color:#ae81ff">32&lt;/span> bytes
001:&amp;gt;:received &lt;span style="color:#ae81ff">32&lt;/span> bytes
001:&amp;gt;:09dc: Event XKEYBOARD-XkbEvent&lt;span style="color:#f92672">(&lt;/span>85&lt;span style="color:#f92672">)&lt;/span> type&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span> time&lt;span style="color:#f92672">=&lt;/span>0x018d8071 device&lt;span style="color:#f92672">=&lt;/span>0x03 not-yet-supported&lt;span style="color:#f92672">=&lt;/span>0x10,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x01,0x90,0x10,0x10,0x10,0x90,0x00,0x01,0x90,0x11,0x00,0x00,0x87,0x05;
001:&amp;gt;:wrote &lt;span style="color:#ae81ff">32&lt;/span> bytes
001:&amp;gt;:received &lt;span style="color:#ae81ff">32&lt;/span> bytes
001:&amp;gt;:09dc: Event XKEYBOARD-XkbEvent&lt;span style="color:#f92672">(&lt;/span>85&lt;span style="color:#f92672">)&lt;/span> type&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span> time&lt;span style="color:#f92672">=&lt;/span>0x018d8072 device&lt;span style="color:#f92672">=&lt;/span>0x03 not-yet-supported&lt;span style="color:#f92672">=&lt;/span>0x10,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x10,0x10,0x10,0x00,0x01,0x90,0x11,0x00,0x00,0x87,0x05;
001:&amp;gt;:wrote &lt;span style="color:#ae81ff">32&lt;/span> bytes
001:&amp;gt;:received &lt;span style="color:#ae81ff">32&lt;/span> bytes
001:&amp;gt;:09dc: Event XKEYBOARD-XkbEvent&lt;span style="color:#f92672">(&lt;/span>85&lt;span style="color:#f92672">)&lt;/span> type&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span> time&lt;span style="color:#f92672">=&lt;/span>0x018d8073 device&lt;span style="color:#f92672">=&lt;/span>0x03 not-yet-supported&lt;span style="color:#f92672">=&lt;/span>0x10,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x01,0x90,0x10,0x10,0x10,0x90,0x00,0x01,0x90,0x11,0x00,0x00,0x87,0x05;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>其工作流程大致如下：&lt;/p>
&lt;ul>
&lt;li>通过套接字地址族AF_INET判断是tcp通信还是本地domain socket通信，然后通过generateSocketName或者calculateTCPport拿到addr相关的信息&lt;/li>
&lt;li>进入mainqueue死循环读取socket通信数据&lt;/li>
&lt;li>通过parse_server(c)翻译Event事件通信，然后打印输出&lt;/li>
&lt;li>parse_client(c)翻译Requst请求通信，然后打印输出&lt;/li>
&lt;/ul>
&lt;p>相关的代码调用堆栈如下：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-c" data-lang="c">Breakpoint &lt;span style="color:#ae81ff">1&lt;/span>, startline (c&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0x4f8270&lt;/span>, d&lt;span style="color:#f92672">=&lt;/span>TO_SERVER, format&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0x417a71&lt;/span> &lt;span style="color:#e6db74">"%04x:%3u: Request(%hhu): %s "&lt;/span>) at parse.c:&lt;span style="color:#ae81ff">52&lt;/span>
&lt;span style="color:#ae81ff">52&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span>( (print_timestamps &lt;span style="color:#f92672">||&lt;/span> print_reltimestamps)
(gdb) bt
&lt;span style="color:#75715e">#0 startline (c=0x4f8270, d=TO_SERVER, format=0x417a71 "%04x:%3u: Request(%hhu): %s ") at parse.c:52
&lt;/span>&lt;span style="color:#75715e">#1 0x000000000040b5f5 in print_client_request (c=0x4f8270, bigrequest=false) at parse.c:1692
&lt;/span>&lt;span style="color:#75715e">#2 0x000000000040cc2d in parse_client (c=0x4f8270) at parse.c:1996
&lt;/span>&lt;span style="color:#75715e">#3 0x0000000000403b4a in mainqueue (listener=4) at main.c:406
&lt;/span>&lt;span style="color:#75715e">#4 0x00000000004045d4 in main (argc=4, argv=0x7fffffffdef8) at main.c:706
&lt;/span>&lt;span style="color:#75715e">&lt;/span>(gdb) c
Continuing.
Breakpoint &lt;span style="color:#ae81ff">1&lt;/span>, startline (c&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0x4f8270&lt;/span>, d&lt;span style="color:#f92672">=&lt;/span>TO_CLIENT, format&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0x417b32&lt;/span> &lt;span style="color:#e6db74">"%04x:%u: Reply to %s: "&lt;/span>) at parse.c:&lt;span style="color:#ae81ff">52&lt;/span>
&lt;span style="color:#ae81ff">52&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span>( (print_timestamps &lt;span style="color:#f92672">||&lt;/span> print_reltimestamps)
(gdb) bt
&lt;span style="color:#75715e">#0 startline (c=0x4f8270, d=TO_CLIENT, format=0x417b32 "%04x:%u: Reply to %s: ") at parse.c:52
&lt;/span>&lt;span style="color:#75715e">#1 0x000000000040c1f3 in print_server_reply (c=0x4f8270) at parse.c:1859
&lt;/span>&lt;span style="color:#75715e">#2 0x000000000040d0e5 in parse_server (c=0x4f8270) at parse.c:2064
&lt;/span>&lt;span style="color:#75715e">#3 0x00000000004035ed in mainqueue (listener=4) at main.c:336
&lt;/span>&lt;span style="color:#75715e">#4 0x00000000004045d4 in main (argc=4, argv=0x7fffffffdef8) at main.c:706
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#75715e">// connect socket
&lt;/span>&lt;span style="color:#75715e">&lt;/span>Breakpoint &lt;span style="color:#ae81ff">2&lt;/span>, generateSocketName (addr&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0x7fffffffdd30&lt;/span>, display&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">9&lt;/span>) at x11common.c:&lt;span style="color:#ae81ff">114&lt;/span>
&lt;span style="color:#ae81ff">114&lt;/span> snprintf(addr&lt;span style="color:#f92672">-&amp;gt;&lt;/span>sun_path,&lt;span style="color:#66d9ef">sizeof&lt;/span>(addr&lt;span style="color:#f92672">-&amp;gt;&lt;/span>sun_path),&lt;span style="color:#e6db74">"/tmp/.X11-unix/X%d"&lt;/span>,display);
(gdb) p display
&lt;span style="color:#960050;background-color:#1e0010">$&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">9&lt;/span>
(gdb) c
Continuing.
[Detaching after fork from child process &lt;span style="color:#ae81ff">7682&lt;/span>]
Got connection from unknown(local)
Breakpoint &lt;span style="color:#ae81ff">2&lt;/span>, generateSocketName (addr&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0x7fffffffd9d0&lt;/span>, display&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>) at x11common.c:&lt;span style="color:#ae81ff">114&lt;/span>
&lt;span style="color:#ae81ff">114&lt;/span> snprintf(addr&lt;span style="color:#f92672">-&amp;gt;&lt;/span>sun_path,&lt;span style="color:#66d9ef">sizeof&lt;/span>(addr&lt;span style="color:#f92672">-&amp;gt;&lt;/span>sun_path),&lt;span style="color:#e6db74">"/tmp/.X11-unix/X%d"&lt;/span>,display);
(gdb) p display
&lt;span style="color:#960050;background-color:#1e0010">$&lt;/span>&lt;span style="color:#ae81ff">6&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
(gdb) bt
&lt;span style="color:#75715e">#0 generateSocketName (addr=0x7fffffffd9d0, display=0) at x11common.c:114
&lt;/span>&lt;span style="color:#75715e">#1 0x0000000000404c92 in connectToServer (displayname=0x7fffffffe363 ":0", family=1, hostname=0x0, display=0) at x11client.c:77
&lt;/span>&lt;span style="color:#75715e">#2 0x0000000000402766 in acceptConnection (listener=3) at main.c:95
&lt;/span>&lt;span style="color:#75715e">#3 0x0000000000403edd in mainqueue (listener=3) at main.c:452
&lt;/span>&lt;span style="color:#75715e">#4 0x00000000004045d4 in main (argc=2, argv=0x7fffffffdf18) at main.c:706
&lt;/span>&lt;span style="color:#75715e">&lt;/span>(gdb)
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="协议分析">协议分析
&lt;span>
&lt;a href="https://blog.deepin.org/posts/xtrace/#%e5%8d%8f%e8%ae%ae%e5%88%86%e6%9e%90">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>xtrace通过拦截X11协议通信来进行分析。它捕获传输到X服务器的请求以及服务器对这些请求的响应，将他们解析化以日志输出的形式打印出来。所以本质来说协议分析指的是X11协议交互分析，需要对
X11相关的协议做到非常了解，即每个协议有什么功能，在xcb中是如何处理，在xserver中又是如何处理。受限于篇幅，笔者在此不会阐述所有的协议分析，而是拿我们平时常用的一些软件做一些分析和介绍。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>日志流关键字&lt;/th>
&lt;th>含义或用途&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Present-Request(148,1)&lt;/td>
&lt;td>客户端用于GLX等送显&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Request(1): CreateWindow&lt;/td>
&lt;td>请求创建X窗口，shm、glx都有&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GLX-Request(152,3): glXCreateContext&lt;/td>
&lt;td>请求创建GLX上下文，可以用来判断客户端是否GLX应用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>MIT-SHM-Request(130,3): PutImage&lt;/td>
&lt;td>X11 shm客户端请求更新图像&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Event XKEYBOARD-XkbEvent(85)&lt;/td>
&lt;td>xserver键盘事件传递给X客户端&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Event Generic(35) XInputExtension&lt;/td>
&lt;td>鼠标事件，后面带着ButtonPress、ButtonRelease、Motion等&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Request(36): GrabServer&lt;/td>
&lt;td>grab请求&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Request(37): UngrabServer&lt;/td>
&lt;td>解除grab请求&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DeleteProperty&lt;/td>
&lt;td>请求删除X11窗口的一些属性&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ChangeProperty&lt;/td>
&lt;td>改变X11窗口的一些属性&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PropertyNotify&lt;/td>
&lt;td>窗口属性改变发送事件通知客户端&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>MIT-SHM-Request(130,4): GetImage&lt;/td>
&lt;td>shm方式获取屏幕图像&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Request(62): CopyArea&lt;/td>
&lt;td>复制屏幕一部分区域图像（离屏）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Request(53): CreatePixmap&lt;/td>
&lt;td>创建图像（离屏）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>上述表格中只是介绍了常见部分的协议，X协议非常的丰富，完整的模块如下所示：&lt;/p>
&lt;ul>
&lt;li>XCB BigRequests API 发送和接受超过请求长度(65535字节)限制的数据&lt;/li>
&lt;li>XCB Composite API 支持窗口合成，将多个窗口的内容合成最终的显示图像&lt;/li>
&lt;li>XCB Damage API 用于跟踪窗口或者绘图上下文的可视区域的改变&lt;/li>
&lt;li>XCB DPMS API 用于管理显示器的电源管理功能，控制显示器电源模式&lt;/li>
&lt;li>XCB DRI2 API 支持直接渲染和硬件加速&lt;/li>
&lt;li>XCB DRI3 API 支持直接渲染和硬件加速，对DRI2的扩展和改进&lt;/li>
&lt;li>XCB Glx API 提供GLX接口创建OpenGL上下文，进行图形渲染和交互&lt;/li>
&lt;li>XCB Present API 实现高性能的图像呈现在屏幕上&lt;/li>
&lt;li>XCB RandR API 管理显示器的分辨率、屏幕方向和显示器布局等&lt;/li>
&lt;li>XCB Record API 记录X服务器的事件流，以便进行调试、分析等&lt;/li>
&lt;li>XCB Render API xrender绘图&lt;/li>
&lt;li>XCB ScreenSaver API 管理屏幕保护程序的行为和状态&lt;/li>
&lt;li>XCB Shape API 用于创建和操作不规则窗口的形状&lt;/li>
&lt;li>XCB Shm API 应用程序能够通过共享内存的方式高效的传输图像&lt;/li>
&lt;li>XCB Sync API 实现同步操作和时间戳的管理，确保预期的时序&lt;/li>
&lt;li>XCB XCMisc API 提供额外的杂项函数和功能，获取一些服务器信息&lt;/li>
&lt;li>XCB Core API 核心部分，提供了基本通信功能和操作&lt;/li>
&lt;li>XCB Xevie API 拦截和处理X服务器上的事件流&lt;/li>
&lt;li>XCB XF86Dri API 直接渲染，直接访问图形硬件，提高图形性能和效率&lt;/li>
&lt;li>XCB XFixes API 增强功能：光标、窗口形状、窗口属性、窗口位置&lt;/li>
&lt;li>XCB Xinerama API 用于管理多个显示器的配置和操作&lt;/li>
&lt;li>XCB Input API 处理输入事件（如键盘、鼠标、触摸屏等）交互&lt;/li>
&lt;li>XCB xkb API 配置与操作键盘相关的设置，键盘布局和状态&lt;/li>
&lt;li>XCB XPrint API 直接从应用程序打印文档、图像和其他内容&lt;/li>
&lt;li>XCB API xcb基础功能&lt;/li>
&lt;li>XCB SELinux API 在应用程序中管理selinux安全策略和执行安全操作&lt;/li>
&lt;li>XCB Test API 测试协议，常用于远程控制&lt;/li>
&lt;li>XCB Xv API xvideo相关，用于视频渲染、视频加速、获取视频信息&lt;/li>
&lt;li>XCB XvMC API 在GPU上执行视频解码和运动补偿功能
笔者在工作中也有时对xtrace日志无法分析到有用信息,此时会查看&lt;a href="https://xcb.freedesktop.org/manual/modules.html">XCB&lt;/a>帮助文档，通过分析对比查找到相关的xcb函数，从而逐渐熟悉X11协议。&lt;/li>
&lt;/ul>
&lt;h2 id="总结">总结
&lt;span>
&lt;a href="https://blog.deepin.org/posts/xtrace/#%e6%80%bb%e7%bb%93">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>总体来说xtrace是一个有用的工具，对于笔者来说经常会接触生态软件的图形显示问题，对于少部分软件开发商不愿意提供代码和问题复现最小demo，此时其软件对于笔者来说是一个黑盒，当问题边界靠近X相关的技术时，笔者会使用xtrace去详细分析该软件的详细功能，往往这可以在底层剖析软件的显示工作方式，配合系统上相关的组建库代码，可以方便地处理客户的紧急问题。
笔者编写这篇文档是希望可以鼓励更多的同事使用xtrace，这个工具可以帮助你熟悉X11的工具原理，同时也可以理解像qt、gtk等UI库的底层实现，在定位系统复制粘贴、图形显示、拖拽、窗口相关的问题时可以提供更加底层的日志，方便更加精准地定位问题根因！&lt;/p>
&lt;h2 id="参考资料">参考资料
&lt;span>
&lt;a href="https://blog.deepin.org/posts/xtrace/#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/X_Window_System">X11&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/X_server">XServer&lt;/a>&lt;/li>
&lt;/ul></description><author>deepin 社区官方博客</author></item><item><title>drm介绍</title><link>https://blog.deepin.org/posts/drm/</link><pubDate>Wed, 01 Nov 2023 05:19:31 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/drm/</guid><description>&lt;h2 id="介绍">介绍
&lt;span>
&lt;a href="https://blog.deepin.org/posts/drm/#%e4%bb%8b%e7%bb%8d">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>随着Linux图形发展的需求增长，需要更多的图形功能和性能，这促成了Direct Rending Infastructure（DRI）和Direct Rendering Manager（DRM）的出现，DRM是一个内核级别的子系统，提供了对图形设备的管理和访问控制，它允许用户空间的图形库和应用程序直接访问GPU进行渲染，而无需操作系统介入，它的主要作用如下：&lt;/p>
&lt;ul>
&lt;li>图形硬件管理：DRM负责管理图形硬件资源，如显存、显卡以及显示设备。它允许内核与这些硬件设备进行通信和控制。&lt;/li>
&lt;li>图形加速：提供对硬件加速功能的支持，例如3D渲染和视频加速。这使得图形渲染更加高效和流畅。&lt;/li>
&lt;li>多显示器支持：允许多个显示器的管理和配置，包括扩展桌面、镜像模式等。&lt;/li>
&lt;li>用户空间接口：提供了用户空间图形库（如Mesa 3D等）和应用程序与内核中DRM子系统进行通信的接口。&lt;/li>
&lt;li>支持不同的图形协议：DRM在支持X Window System（X11）的同时，也能与新一代图形协议，如Wayland，进行整合。&lt;/li>
&lt;/ul>
&lt;p>这种更好的性能、3D加速和对硬件的更直接访问使得它可以支持X Window System和Wayland的图形输出和管理。其现状如下：&lt;/p>
&lt;ul>
&lt;li>持续发展：随着硬件技术和图形需求的不断演进，DRM在Linux内核中也在不断发展。新的功能和改进不断加入，以满足新一代图形硬件和应用程序的需求。&lt;/li>
&lt;li>多厂商支持：DRM的发展得到了多家硬件厂商的支持，这包括AMD、Intel、NVIDIA等，它们为Linux内核开发并贡献了各自硬件的DRM驱动程序。&lt;/li>
&lt;li>支持新技术：DRM也在逐步支持新的图形技术，例如，对于低功耗图形处理的优化、支持更高分辨率、HDR显示以及机器学习和AI加速等方面的改进。&lt;/li>
&lt;li>Wayland和DRM集成：Wayland作为新一代图形显示协议，与DRM更为紧密地整合，提供更为直接和高效的图形渲染和显示方式。&lt;/li>
&lt;/ul>
&lt;p>总体而言，现如今很多Linux发行版上图形服务器后端都是对接的drm实现图形输出和相关控制，DRM在Linux图形子系统中扮演着关键角色，随着技术和需求的不断发展，它持续进化以适应新的硬件和应用场景，为Linux系统提供了强大的图形渲染支持。
&lt;figure>&lt;img src="https://blog.deepin.org/posts/drm/drm-timeline.svg" />&lt;figcaption>
&lt;h4>图1. DRM时间线&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;/p>
&lt;h2 id="drm剖析">DRM剖析
&lt;span>
&lt;a href="https://blog.deepin.org/posts/drm/#drm%e5%89%96%e6%9e%90">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="framebuffer--drm">Framebuffer &amp;amp;&amp;amp; DRM
&lt;span>
&lt;a href="https://blog.deepin.org/posts/drm/#framebuffer--drm">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>在谈drm之前必须先了解一下它的前辈Framebuffer，Framebuffer是指一块内存区域，用户存储显示设备上每个像素的颜色信息。在早期的计算机系统中，操作系统直接将图形数据写入这块显存区域，这被称为直接显存访问（Direct Framebuffer Access），这种方式简单、直接，但随着图形复杂程度增加和硬件发展，它变得难以满足高分辨率和复杂图形的需求，随着事件的推移，由DRM提供了更多先进的功能，它追加能替代了Framebuffer，并称为linux图形系统的主要组成部分（图形框架演变如下图2）。然而Frambuffer仍然在某些特定场景下有其用户之地，尤其是在一些嵌入式系统和特定的硬件上。
总的来说，Framebuffer到DRM的发展代表了图形处理在Linux系统中的进步和演进，从最初的直接访问到高级的硬件加速和更加强大的图形功能。drm为显示硬件的适配和图形的发展贡献了很大的力量。
&lt;figure>&lt;img src="https://blog.deepin.org/posts/drm/kms-vs-fbdev-display.svg" />&lt;figcaption>
&lt;h4>图2. Framebuffer到DRM&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;/p>
&lt;h3 id="drm-plane">drm-plane
&lt;span>
&lt;a href="https://blog.deepin.org/posts/drm/#drm-plane">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>drm_plane本质是对显示控制器中scanout硬件的抽象。简单来说，给定一个plane，可以让其与一个framebuffer关联表示进行scanout的数据，同时控制scanout时进行的额外操作，比如colorspace的改变，旋转、拉伸、偏移等操作。drm_plane是与硬件强相关的，显示控制器支持的plane是固定的，其支持的功能也是由硬件决定的。所有的drm_plane必为三种类型之一：&lt;/p>
&lt;ul>
&lt;li>Primary - 主plane，一般控制整个显示器的输出。CRTC必须要有一个这样的plane。&lt;/li>
&lt;li>Curosr - 表示鼠标光标图层（可选），一般启用的话称其为开启硬件光标，代码如下，流程如下图3。&lt;/li>
&lt;li>Overlay - 叠加plane，可以在主plane上叠加一层输出，可选。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-c" data-lang="c">....
&lt;span style="color:#66d9ef">if&lt;/span> (cursor &lt;span style="color:#f92672">!=&lt;/span> NULL &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> drm_connector_is_cursor_visible(conn)) &amp;#123;
&lt;span style="color:#66d9ef">struct&lt;/span> wlr_drm_fb &lt;span style="color:#f92672">*&lt;/span>cursor_fb &lt;span style="color:#f92672">=&lt;/span> get_next_cursor_fb(conn);
&lt;span style="color:#66d9ef">if&lt;/span> (cursor_fb &lt;span style="color:#f92672">==&lt;/span> NULL) &amp;#123;
wlr_drm_conn_log(conn, WLR_DEBUG, &lt;span style="color:#e6db74">"Failed to acquire cursor FB"&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> false;
}
drmModeFB &lt;span style="color:#f92672">*&lt;/span>drm_fb &lt;span style="color:#f92672">=&lt;/span> drmModeGetFB(drm&lt;span style="color:#f92672">-&amp;gt;&lt;/span>fd, cursor_fb&lt;span style="color:#f92672">-&amp;gt;&lt;/span>id);
&lt;span style="color:#66d9ef">if&lt;/span> (drm_fb &lt;span style="color:#f92672">==&lt;/span> NULL) &amp;#123;
wlr_drm_conn_log_errno(conn, WLR_DEBUG, &lt;span style="color:#e6db74">"Failed to get cursor "&lt;/span>
&lt;span style="color:#e6db74">"BO handle: drmModeGetFB failed"&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> false;
}
&lt;span style="color:#66d9ef">uint32_t&lt;/span> cursor_handle &lt;span style="color:#f92672">=&lt;/span> drm_fb&lt;span style="color:#f92672">-&amp;gt;&lt;/span>handle;
&lt;span style="color:#66d9ef">uint32_t&lt;/span> cursor_width &lt;span style="color:#f92672">=&lt;/span> drm_fb&lt;span style="color:#f92672">-&amp;gt;&lt;/span>width;
&lt;span style="color:#66d9ef">uint32_t&lt;/span> cursor_height &lt;span style="color:#f92672">=&lt;/span> drm_fb&lt;span style="color:#f92672">-&amp;gt;&lt;/span>height;
drmModeFreeFB(drm_fb);
&lt;span style="color:#75715e">// 设置硬件光标，更新光标图像buffer
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> ret &lt;span style="color:#f92672">=&lt;/span> drmModeSetCursor(drm&lt;span style="color:#f92672">-&amp;gt;&lt;/span>fd, crtc&lt;span style="color:#f92672">-&amp;gt;&lt;/span>id, cursor_handle,
cursor_width, cursor_height);
&lt;span style="color:#66d9ef">int&lt;/span> set_cursor_errno &lt;span style="color:#f92672">=&lt;/span> errno;
&lt;span style="color:#66d9ef">if&lt;/span> (drmCloseBufferHandle(drm&lt;span style="color:#f92672">-&amp;gt;&lt;/span>fd, cursor_handle) &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>) &amp;#123;
wlr_log_errno(WLR_ERROR, &lt;span style="color:#e6db74">"drmCloseBufferHandle failed"&lt;/span>);
}
&lt;span style="color:#66d9ef">if&lt;/span> (ret &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>) &amp;#123;
wlr_drm_conn_log(conn, WLR_DEBUG, &lt;span style="color:#e6db74">"drmModeSetCursor failed: %s"&lt;/span>,
strerror(set_cursor_errno));
&lt;span style="color:#66d9ef">return&lt;/span> false;
}
&lt;span style="color:#75715e">// 移动硬件光标位置
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span> (drmModeMoveCursor(drm&lt;span style="color:#f92672">-&amp;gt;&lt;/span>fd,
crtc&lt;span style="color:#f92672">-&amp;gt;&lt;/span>id, conn&lt;span style="color:#f92672">-&amp;gt;&lt;/span>cursor_x, conn&lt;span style="color:#f92672">-&amp;gt;&lt;/span>cursor_y) &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>) &amp;#123;
wlr_drm_conn_log_errno(conn, WLR_ERROR, &lt;span style="color:#e6db74">"drmModeMoveCursor failed"&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> false;
}
} &lt;span style="color:#66d9ef">else&lt;/span> &amp;#123;
&lt;span style="color:#66d9ef">if&lt;/span> (drmModeSetCursor(drm&lt;span style="color:#f92672">-&amp;gt;&lt;/span>fd, crtc&lt;span style="color:#f92672">-&amp;gt;&lt;/span>id, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>)) &amp;#123;
wlr_drm_conn_log_errno(conn, WLR_DEBUG, &lt;span style="color:#e6db74">"drmModeSetCursor failed"&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> false;
}
}
....
&lt;/code>&lt;/pre>&lt;/div>&lt;figure>&lt;img src="https://blog.deepin.org/posts/drm/kms-display-plane.svg" />&lt;figcaption>
&lt;h4>图3. drm-plane框架图&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/drm/kms-plane.svg" />&lt;figcaption>
&lt;h4>图4. 多drm-plane合成流程图&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>在笔者看来，drm-plane与layer的概念类似，只是更加偏向硬件底层概念，开发者可以通过控制显示相关的参数将plane投显到显示器上任何区域（参考图5），这种映射机制提供了非常方便地偏移、缩放方法，如果在这一层上实现移动和缩放等动画，效率将会比使用OpenGL等API制作高，将频繁变化的图像抽象工作在一个单独的drm-plane也可以达到提高性能和省电的目的。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>名字&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>SRC_X&lt;/td>
&lt;td>当前framebuffer crop区域的起始偏移x坐标&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SRC_Y&lt;/td>
&lt;td>当前framebuffer crop区域的起始偏移y坐标&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SRC_W&lt;/td>
&lt;td>当前framebuffer crop区域的宽度&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SRC_H&lt;/td>
&lt;td>当前framebuffer crop区域的高度&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CRTC_X&lt;/td>
&lt;td>屏幕显示区域的起始偏移x坐标&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CRTC_Y&lt;/td>
&lt;td>屏幕显示区域的起始偏移y坐标&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CRTC_W&lt;/td>
&lt;td>屏幕显示区域的宽度&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CRTC_H&lt;/td>
&lt;td>屏幕显示区域的高度&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p align="left">表1. drm-plane部分属性表&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/drm/plane-update-coordinates.png" />&lt;figcaption>
&lt;h4>图5. drm-plane送显参数控制&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;h3 id="crtccathode-ray-tube-controller">crtc（Cathode Ray Tube Controller）
&lt;span>
&lt;a href="https://blog.deepin.org/posts/drm/#crtccathode-ray-tube-controller">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>CRTC 是显示控制器中的一个重要部分，主要用于管理显示设备的扫描和刷新。CRTC 负责生成视频信号的定时和同步，控制屏幕上像素的扫描和刷新，以确保正确的图像显示。它管理像素的输出到屏幕上的确切位置，以及刷新率、分辨率等显示参数。在现代的图形处理中，CRTC通常由图形处理单元（GPU）或显示控制器中的专用部分来控制，以确保正确的图像输出。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/drm/kms-display-crtc.svg" />&lt;figcaption>
&lt;h4>图6. crtc框架图&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>drm legacy链路通过调用drmModePageFlip请求crtc更新显示器图像，其代码如下，流程如图6所示：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-c" data-lang="c">&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> &lt;span style="color:#a6e22e">legacy_crtc_commit&lt;/span>(&lt;span style="color:#66d9ef">struct&lt;/span> wlr_drm_connector &lt;span style="color:#f92672">*&lt;/span>conn,
&lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">struct&lt;/span> wlr_drm_connector_state &lt;span style="color:#f92672">*&lt;/span>state,
&lt;span style="color:#66d9ef">uint32_t&lt;/span> flags, &lt;span style="color:#66d9ef">bool&lt;/span> test_only) &amp;#123;
....
&lt;span style="color:#66d9ef">if&lt;/span> (flags &lt;span style="color:#f92672">&amp;amp;&lt;/span> DRM_MODE_PAGE_FLIP_EVENT) &amp;#123;
&lt;span style="color:#66d9ef">uint32_t&lt;/span> page_flags &lt;span style="color:#f92672">=&lt;/span> DRM_MODE_PAGE_FLIP_EVENT;
&lt;span style="color:#66d9ef">if&lt;/span> (flags &lt;span style="color:#f92672">&amp;amp;&lt;/span> DRM_MODE_PAGE_FLIP_ASYNC) &amp;#123;
page_flags &lt;span style="color:#f92672">|=&lt;/span> DRM_MODE_PAGE_FLIP_ASYNC;
}
&lt;span style="color:#66d9ef">if&lt;/span> (drmModePageFlip(drm&lt;span style="color:#f92672">-&amp;gt;&lt;/span>fd, crtc&lt;span style="color:#f92672">-&amp;gt;&lt;/span>id, fb_id,
page_flags, drm)) &amp;#123;
wlr_drm_conn_log_errno(conn, WLR_ERROR, &lt;span style="color:#e6db74">"drmModePageFlip failed"&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> false;
}
}
&lt;span style="color:#66d9ef">return&lt;/span> true;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;figure>&lt;img src="https://blog.deepin.org/posts/drm/page-flipping.svg" />&lt;figcaption>
&lt;h4>图7. buffer更新流程图&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;h3 id="encoder">encoder
&lt;span>
&lt;a href="https://blog.deepin.org/posts/drm/#encoder">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>编码器从CRTC获取像素数据，并将其转换为适合任何连接器的格式。在某些设备上，CRTC可以将数据发送到多个编码器。在这种情况下，两个编码器将从同一扫描输出缓冲区接收数据，从而在连接到每个编码器的连接器上产生“克隆”显示配置。
在Linux系统中，DRM（Direct Rendering Manager）是用于处理图形显示的子系统，负责在用户空间和图形硬件之间提供接口。DRM编码器在Linux中扮演了关键角色，主要用于对图形和视频内容进行编码、解码、处理和显示。在DRM框架中，编码器的作用包括但不限于：&lt;/p>
&lt;ul>
&lt;li>视频压缩和解压缩： 编码器负责对视频内容进行压缩，以减小数据量并更有效地传输和存储视频流。解码器用于解压缩视频内容，以便图形硬件能够正确地渲染和显示内容。&lt;/li>
&lt;li>图形处理和渲染： DRM编码器能够处理图形和视频流，进行渲染和合成，然后将图像数据发送到显示设备以在屏幕上显示。&lt;/li>
&lt;li>硬件加速： DRM编码器还可以利用硬件加速功能，以便更快地处理图形和视频内容。这有助于提高性能和效率，特别是对于高分辨率视频或图形内容的处理。&lt;/li>
&lt;li>支持多种编解码标准： DRM编码器能够支持多种视频编解码标准，如H.264、H.265等，确保对不同格式的视频流进行正确的处理。
在Linux系统中，DRM编码器与图形驱动程序、图形处理单元（GPU）和显示设备等硬件密切相关。它允许Linux系统管理和控制图形硬件，处理视频流并在屏幕上显示图像，同时也与DRM系统中的访问控制和安全性机制集成，确保受保护内容的安全传输和显示。&lt;/li>
&lt;/ul>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/drm/kms-display-encoder.svg" />&lt;figcaption>
&lt;h4>图8. encoder框架图&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;h3 id="connector">connector
&lt;span>
&lt;a href="https://blog.deepin.org/posts/drm/#connector">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>DRM connectors是用于管理和描述图形设备的连接器或端口。它们提供了对显示设备的连接和属性描述。在图形系统中，这些连接器可以代表诸如HDMI、DisplayPort、DVI等物理连接接口，而它们也可以描述内部连接，例如LCD panels。DRM connectors 在 Linux 中的作用包括：&lt;/p>
&lt;ul>
&lt;li>显示设备连接描述： DRM connectors 提供了关于显示设备的物理连接信息，比如类型（HDMI、VGA、DisplayPort等）、连接状态以及支持的分辨率和刷新率等信息。&lt;/li>
&lt;li>动态连接管理： 它们可以监测连接和断开事件。当显示设备插入或拔出时，DRM connectors 可以检测到这些变化，并通知系统相应的变化。这使系统能够动态调整图形配置以适应新的连接或断开状态。&lt;/li>
&lt;li>多显示器支持： DRM connectors 允许系统管理多个显示器的连接和配置。这使得可以同时使用多个显示器或监视器，或者在不同的显示设备上显示不同的内容。&lt;/li>
&lt;li>显示属性查询和配置： 通过 DRM connectors，可以查询连接器支持的分辨率、刷新率以及其他显示属性。这使系统可以调整和配置图形设备，以最佳方式显示图形内容。&lt;/li>
&lt;/ul>
&lt;p>总体来说，DRM connectors 在 Linux 中的作用是管理显示设备的物理连接、状态和属性，使系统能够动态适应不同的显示设备、支持多个显示器，并提供相应的配置信息，以便正确地显示图形内容。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/drm/kms-display-connector.svg" />&lt;figcaption>
&lt;h4>图9. connector框架图&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;h3 id="framebuffer">framebuffer
&lt;span>
&lt;a href="https://blog.deepin.org/posts/drm/#framebuffer">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>framebuffer是一个重要概念。Framebuffers是用于存储屏幕上每个像素的颜色和其他相关信息的内存区域。在DRM中，framebuffer提供了一个抽象接口，允许用户空间程序访问和操作显存，以便渲染图形数据。其作用包括：&lt;/p>
&lt;ul>
&lt;li>图形数据存储： Framebuffers提供一个区域，用于存储图形数据，包括每个像素的颜色、透明度等信息。这些数据构成了屏幕上显示的图像。&lt;/li>
&lt;li>直接访问屏幕数据： 通过framebuffers，用户空间程序或操作系统内核能够直接访问和操作图形数据，而无需经过额外的复杂处理。&lt;/li>
&lt;li>硬件抽象层： Framebuffers提供了一个硬件无关的抽象接口，这意味着不同的图形设备和硬件都可以通过相同的接口进行访问和操作。&lt;/li>
&lt;li>显示图像： Framebuffers存储了最终用于在屏幕上显示的图像数据。图形数据在 framebuffer中进行组织和处理，然后通过显示控制器输出到屏幕。&lt;/li>
&lt;/ul>
&lt;p>在DRM 中，framebuffers通常与CRTC和显示控制器等组件一起工作。framebuffers提供的抽象层允许操作系统或应用程序以统一的方式对图形数据进行处理和管理，无论具体的硬件设备是什么样的。总之，framebuffers 在 Linux 的 DRM 中提供了一个接口和数据结构，用于存储、操作和管理图形数据，使其能够在屏幕上正确地显示图像。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/drm/framebuffer-fields.svg" />&lt;figcaption>
&lt;h4>图10. framebuffer内存结构图&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;h2 id="drm调试工具">drm调试工具
&lt;span>
&lt;a href="https://blog.deepin.org/posts/drm/#drm%e8%b0%83%e8%af%95%e5%b7%a5%e5%85%b7">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="drm_info">drm_info
&lt;span>
&lt;a href="https://blog.deepin.org/posts/drm/#drm_info">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>用于转储有关 DRM 设备信息的小实用程序&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">drm_info
Node: /dev/dri/card1
├───Driver: i915 &lt;span style="color:#f92672">(&lt;/span>Intel Graphics&lt;span style="color:#f92672">)&lt;/span> version 1.6.0 &lt;span style="color:#f92672">(&lt;/span>20201103&lt;span style="color:#f92672">)&lt;/span>
│ ├───DRM_CLIENT_CAP_STEREO_3D supported
│ ├───DRM_CLIENT_CAP_UNIVERSAL_PLANES supported
│ ├───DRM_CLIENT_CAP_ATOMIC supported
│ ├───DRM_CLIENT_CAP_ASPECT_RATIO supported
│ ├───DRM_CLIENT_CAP_WRITEBACK_CONNECTORS supported
│ ├───DRM_CAP_DUMB_BUFFER &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
│ ├───DRM_CAP_VBLANK_HIGH_CRTC &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
│ ├───DRM_CAP_DUMB_PREFERRED_DEPTH &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">24&lt;/span>
│ ├───DRM_CAP_DUMB_PREFER_SHADOW &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
│ ├───DRM_CAP_PRIME &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">3&lt;/span>
│ ├───DRM_CAP_TIMESTAMP_MONOTONIC &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
│ ├───DRM_CAP_ASYNC_PAGE_FLIP &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
│ ├───DRM_CAP_CURSOR_WIDTH &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">256&lt;/span>
│ ├───DRM_CAP_CURSOR_HEIGHT &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">256&lt;/span>
│ ├───DRM_CAP_ADDFB2_MODIFIERS &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
│ ├───DRM_CAP_PAGE_FLIP_TARGET &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ ├───DRM_CAP_CRTC_IN_VBLANK_EVENT &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
│ ├───DRM_CAP_SYNCOBJ &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
│ └───DRM_CAP_SYNCOBJ_TIMELINE &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
├───Device: PCI 8086:46aa Intel Corporation Alder Lake-UP4 GT2 &lt;span style="color:#f92672">[&lt;/span>Iris Xe Graphics&lt;span style="color:#f92672">]&lt;/span>
│ └───Available nodes: primary, render
├───Framebuffer size
│ ├───Width: &lt;span style="color:#f92672">[&lt;/span>0, 16384&lt;span style="color:#f92672">]&lt;/span>
│ └───Height: &lt;span style="color:#f92672">[&lt;/span>0, 16384&lt;span style="color:#f92672">]&lt;/span>
├───Connectors
│ ├───Connector &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───Object ID: &lt;span style="color:#ae81ff">236&lt;/span>
│ │ ├───Type: eDP
│ │ ├───Status: connected
│ │ ├───Physical size: 290x180 mm
│ │ ├───Subpixel: unknown
│ │ ├───Encoders: &lt;span style="color:#f92672">&amp;#123;&lt;/span>0&lt;span style="color:#f92672">}&lt;/span>
│ │ ├───Modes
│ │ │ └───2880x1800@60.00 preferred driver phsync nvsync
│ │ └───Properties
│ │ ├───&lt;span style="color:#e6db74">"EDID"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: blob &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">266&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"DPMS"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>On, Standby, Suspend, Off&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> On
│ │ ├───&lt;span style="color:#e6db74">"link-status"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Good, Bad&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Good
│ │ ├───&lt;span style="color:#e6db74">"non-desktop"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>0, 1&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"TILE"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: blob &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"CRTC_ID"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>atomic&lt;span style="color:#f92672">)&lt;/span>: object CRTC &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">80&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"scaling mode"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Full, Center, Full aspect&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Full aspect
│ │ ├───&lt;span style="color:#e6db74">"panel orientation"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Normal, Upside Down, Left Side Up, Right Side Up&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Normal
│ │ ├───&lt;span style="color:#e6db74">"Broadcast RGB"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Automatic, Full, Limited 16:235&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Automatic
│ │ ├───&lt;span style="color:#e6db74">"max bpc"&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>6, 12&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"Colorspace"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Default, BT709_YCC, XVYCC_601, XVYCC_709, SYCC_601, opYCC_601, opRGB, BT2020_CYCC, BT2020_RGB, BT2020_YCC, DCI-P3_RGB_D65, RGB_WIDE_FIXED, RGB_WIDE_FLOAT, BT601_YCC&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Default
│ │ ├───&lt;span style="color:#e6db74">"HDR_OUTPUT_METADATA"&lt;/span>: blob &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ │ └───&lt;span style="color:#e6db74">"vrr_capable"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>0, 1&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ ├───Connector &lt;span style="color:#ae81ff">1&lt;/span>
│ │ ├───Object ID: &lt;span style="color:#ae81ff">245&lt;/span>
│ │ ├───Type: DisplayPort
│ │ ├───Status: disconnected
│ │ ├───Encoders: &lt;span style="color:#f92672">&amp;#123;&lt;/span>1&lt;span style="color:#f92672">}&lt;/span>
│ │ └───Properties
│ │ ├───&lt;span style="color:#e6db74">"EDID"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: blob &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"DPMS"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>On, Standby, Suspend, Off&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Off
│ │ ├───&lt;span style="color:#e6db74">"link-status"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Good, Bad&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Good
│ │ ├───&lt;span style="color:#e6db74">"non-desktop"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>0, 1&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"TILE"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: blob &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"CRTC_ID"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>atomic&lt;span style="color:#f92672">)&lt;/span>: object CRTC &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"subconnector"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Unknown, VGA, DVI-D, HDMI, DP, Wireless, Native&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Unknown
│ │ ├───&lt;span style="color:#e6db74">"audio"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>force-dvi, off, auto, on&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> auto
│ │ ├───&lt;span style="color:#e6db74">"Broadcast RGB"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Automatic, Full, Limited 16:235&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Automatic
│ │ ├───&lt;span style="color:#e6db74">"max bpc"&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>6, 12&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">12&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"Colorspace"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Default, BT709_YCC, XVYCC_601, XVYCC_709, SYCC_601, opYCC_601, opRGB, BT2020_CYCC, BT2020_RGB, BT2020_YCC, DCI-P3_RGB_D65, RGB_WIDE_FIXED, RGB_WIDE_FLOAT, BT601_YCC&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Default
│ │ ├───&lt;span style="color:#e6db74">"HDR_OUTPUT_METADATA"&lt;/span>: blob &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"vrr_capable"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>0, 1&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"Content Protection"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Undesired, Desired, Enabled&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Undesired
│ │ └───&lt;span style="color:#e6db74">"HDCP Content Type"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>HDCP Type0, HDCP Type1&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> HDCP Type0
│ └───Connector &lt;span style="color:#ae81ff">2&lt;/span>
│ ├───Object ID: &lt;span style="color:#ae81ff">258&lt;/span>
│ ├───Type: DisplayPort
│ ├───Status: disconnected
│ ├───Encoders: &lt;span style="color:#f92672">&amp;#123;&lt;/span>6&lt;span style="color:#f92672">}&lt;/span>
│ └───Properties
│ ├───&lt;span style="color:#e6db74">"EDID"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: blob &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ ├───&lt;span style="color:#e6db74">"DPMS"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>On, Standby, Suspend, Off&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Off
│ ├───&lt;span style="color:#e6db74">"link-status"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Good, Bad&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Good
│ ├───&lt;span style="color:#e6db74">"non-desktop"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>0, 1&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ ├───&lt;span style="color:#e6db74">"TILE"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: blob &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ ├───&lt;span style="color:#e6db74">"CRTC_ID"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>atomic&lt;span style="color:#f92672">)&lt;/span>: object CRTC &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ ├───&lt;span style="color:#e6db74">"subconnector"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Unknown, VGA, DVI-D, HDMI, DP, Wireless, Native&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Unknown
│ ├───&lt;span style="color:#e6db74">"audio"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>force-dvi, off, auto, on&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> auto
│ ├───&lt;span style="color:#e6db74">"Broadcast RGB"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Automatic, Full, Limited 16:235&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Automatic
│ ├───&lt;span style="color:#e6db74">"max bpc"&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>6, 12&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">12&lt;/span>
│ ├───&lt;span style="color:#e6db74">"Colorspace"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Default, BT709_YCC, XVYCC_601, XVYCC_709, SYCC_601, opYCC_601, opRGB, BT2020_CYCC, BT2020_RGB, BT2020_YCC, DCI-P3_RGB_D65, RGB_WIDE_FIXED, RGB_WIDE_FLOAT, BT601_YCC&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Default
│ ├───&lt;span style="color:#e6db74">"HDR_OUTPUT_METADATA"&lt;/span>: blob &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ ├───&lt;span style="color:#e6db74">"vrr_capable"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>0, 1&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ ├───&lt;span style="color:#e6db74">"Content Protection"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Undesired, Desired, Enabled&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Undesired
│ └───&lt;span style="color:#e6db74">"HDCP Content Type"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>HDCP Type0, HDCP Type1&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> HDCP Type0
├───Encoders
│ ├───Encoder &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───Object ID: &lt;span style="color:#ae81ff">235&lt;/span>
│ │ ├───Type: TMDS
│ │ ├───CRTCS: &lt;span style="color:#f92672">&amp;#123;&lt;/span>0, 1, 2, 3&lt;span style="color:#f92672">}&lt;/span>
│ │ └───Clones: &lt;span style="color:#f92672">&amp;#123;&lt;/span>0&lt;span style="color:#f92672">}&lt;/span>
│ ├───Encoder &lt;span style="color:#ae81ff">1&lt;/span>
│ │ ├───Object ID: &lt;span style="color:#ae81ff">244&lt;/span>
│ │ ├───Type: TMDS
│ │ ├───CRTCS: &lt;span style="color:#f92672">&amp;#123;&lt;/span>0, 1, 2, 3&lt;span style="color:#f92672">}&lt;/span>
│ │ └───Clones: &lt;span style="color:#f92672">&amp;#123;&lt;/span>1&lt;span style="color:#f92672">}&lt;/span>
.........
├───CRTCs
│ ├───CRTC &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───Object ID: &lt;span style="color:#ae81ff">80&lt;/span>
│ │ ├───Legacy info
│ │ │ ├───Mode: 2880x1800@60.00 preferred driver phsync nvsync
│ │ │ └───Gamma size: &lt;span style="color:#ae81ff">256&lt;/span>
│ │ └───Properties
│ │ ├───&lt;span style="color:#e6db74">"ACTIVE"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>atomic&lt;span style="color:#f92672">)&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>0, 1&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"MODE_ID"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>atomic&lt;span style="color:#f92672">)&lt;/span>: blob &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">271&lt;/span>
│ │ │ └───2880x1800@60.00 preferred driver phsync nvsync
│ │ ├───&lt;span style="color:#e6db74">"OUT_FENCE_PTR"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>atomic&lt;span style="color:#f92672">)&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>0, UINT64_MAX&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"VRR_ENABLED"&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>0, 1&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"SCALING_FILTER"&lt;/span>: enum &lt;span style="color:#f92672">&amp;#123;&lt;/span>Default, Nearest Neighbor&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">=&lt;/span> Default
│ │ ├───&lt;span style="color:#e6db74">"DEGAMMA_LUT"&lt;/span>: blob &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"DEGAMMA_LUT_SIZE"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>0, UINT32_MAX&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">129&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"CTM"&lt;/span>: blob &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
│ │ ├───&lt;span style="color:#e6db74">"GAMMA_LUT"&lt;/span>: blob &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">270&lt;/span>
│ │ └───&lt;span style="color:#e6db74">"GAMMA_LUT_SIZE"&lt;/span> &lt;span style="color:#f92672">(&lt;/span>immutable&lt;span style="color:#f92672">)&lt;/span>: range &lt;span style="color:#f92672">[&lt;/span>0, UINT32_MAX&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1024&lt;/span>
........
└───Planes
├───Plane &lt;span style="color:#ae81ff">0&lt;/span>
│ ├───Object ID: &lt;span style="color:#ae81ff">31&lt;/span>
│ ├───CRTCs: &lt;span style="color:#f92672">&amp;#123;&lt;/span>0&lt;span style="color:#f92672">}&lt;/span>
│ ├───Legacy info
│ │ ├───FB ID: &lt;span style="color:#ae81ff">237&lt;/span>
│ │ │ ├───Object ID: &lt;span style="color:#ae81ff">237&lt;/span>
│ │ │ ├───Size: 2880x1800
│ │ │ ├───Format: ARGB2101010 &lt;span style="color:#f92672">(&lt;/span>0x30335241&lt;span style="color:#f92672">)&lt;/span>
│ │ │ ├───Modifier: I915_FORMAT_MOD_Y_TILED &lt;span style="color:#f92672">(&lt;/span>0x100000000000002&lt;span style="color:#f92672">)&lt;/span>
│ │ │ └───Planes:
│ │ │ └───Plane 0: offset &lt;span style="color:#f92672">=&lt;/span> 0, pitch &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">11520&lt;/span> bytes
│ │ └───Formats:
│ │ ├───C8 &lt;span style="color:#f92672">(&lt;/span>0x20203843&lt;span style="color:#f92672">)&lt;/span>
│ │ ├───RGB565 &lt;span style="color:#f92672">(&lt;/span>0x36314752&lt;span style="color:#f92672">)&lt;/span>
│ │ ├───XRGB8888 &lt;span style="color:#f92672">(&lt;/span>0x34325258&lt;span style="color:#f92672">)&lt;/span>
│ │ ├───XBGR8888 &lt;span style="color:#f92672">(&lt;/span>0x34324258&lt;span style="color:#f92672">)&lt;/span>
│ │ ├───ARGB8888 &lt;span style="color:#f92672">(&lt;/span>0x34325241&lt;span style="color:#f92672">)&lt;/span>
│ │ ├───ABGR8888 &lt;span style="color:#f92672">(&lt;/span>0x34324241&lt;span style="color:#f92672">)&lt;/span>
.....
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="drm_monitor">drm_monitor
&lt;span>
&lt;a href="https://blog.deepin.org/posts/drm/#drm_monitor">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>用于监控 KMS 状态的 CLI 工具&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">drm_monitor -d /dev/dri/card1
CRTC 80: seq&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">30095325875&lt;/span> ns&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">3076787603594&lt;/span> delta_ns&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">16668012&lt;/span> Hz&lt;span style="color:#f92672">=&lt;/span>59.995157
CRTC 131: seq&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span> ns&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span> delta_ns&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span> Hz&lt;span style="color:#f92672">=&lt;/span>0.000000
CRTC 182: seq&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span> ns&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span> delta_ns&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span> Hz&lt;span style="color:#f92672">=&lt;/span>0.000000
CRTC 233: seq&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span> ns&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span> delta_ns&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span> Hz&lt;span style="color:#f92672">=&lt;/span>0.000000
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="参考资料">参考资料
&lt;span>
&lt;a href="https://blog.deepin.org/posts/drm/#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://docs.kernel.org/gpu/drm-kms.html">Kernel Mode Setting&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://bootlin.com/pub/conferences/2014/elce/brezillon-drm-kms/">brezillon-drm-kms&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://gitlab.freedesktop.org/emersion/drm_info">drm_info&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/emersion/drm_monitor">drm_monitor&lt;/a>&lt;/li>
&lt;/ul></description><author>deepin 社区官方博客</author></item><item><title>浅析 linux 电源配置</title><link>https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/</link><pubDate>Tue, 31 Oct 2023 13:00:00 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/</guid><description>&lt;h2 id="一背景">一、背景
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#%e4%b8%80%e8%83%8c%e6%99%af">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>近期，我们收到用户反馈，在使用 deepin 系统过程中遇到了 CPU 功耗过高导致的设备发热、续航较差情况，而用户在这些负载场景下，CPU 的占用往往不高。为了解决这个痛点，统信软件开源社区中心特别成立专项计划，对于 deepin 的电源进行专项优化，本文旨在针对此问题根因进行分析于说明。&lt;/p>
&lt;p>在对电源进行专项优化之前，我们首先对 deepin 系统进行了深入的调查和分析，以了解其在负载场景下的实际运行情况。经过对 CPU 使用率和功耗的监测，我们发现了一个令人惊讶的事实：尽管在高负载场景下 CPU 的占用率不高，但其功耗却持续升高，最终导致设备发热并影响续航。也就是说，我们前期做的省电优化工作，不仅无效，还起了反作用（具体情况作者将在下文作出仔细说明）。&lt;/p>
&lt;h2 id="二问题">二、问题
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#%e4%ba%8c%e9%97%ae%e9%a2%98">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="1内核">1. 内核
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#1%e5%86%85%e6%a0%b8">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>最开始发现问题的地方在内核。有用户将我们的内核和 ubuntu 的内核进行对比后发现，虽然我们的系统和 ubuntu 系统的性能差不多，但是在发热和续航上，我们较 ubuntu 落后较多。有用户在 deepin 系统上使用 ubuntu 和其他开源 linux 发行版的配置文件分别编译内核，发现 deepin 的主要问题存在于发热控制之上。我们的测试同事高度重视这一社区反馈，遂对社区用户反馈的问题进行复现，佐证了这一现象。&lt;/p>
&lt;p>对于这种问题，我们立即联系了内核研发部的同事，并邀请部分对内核配置有一定研究的社区用户共同参与。在大家的合力排查下，我们发现，deepin v23 中提供的 HWE 内核存在部分 debug 和无用的内核选项被开启的情况，并且部分节电功能实际未能获得启用，这些都在一定程度上导致了 deepin v23 的续航表现不佳。&lt;/p>
&lt;h3 id="2系统">2. 系统
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#2%e7%b3%bb%e7%bb%9f">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>在系统层面，我重新审视了 dde-daemon 提供的电源调度模块，并且对比内核文档提供的文件接口，分析我们用户使用的电源模式，发现其中存在可以优化的空间。这将是本文着重讲解的内容之一。&lt;/p>
&lt;h2 id="三前置知识">三、前置知识
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#%e4%b8%89%e5%89%8d%e7%bd%ae%e7%9f%a5%e8%af%86">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="1acpi">1.ACPI
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#1acpi">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>ACPI 是 Advanced Configuration and Power Interface 的缩写，是一种计算机硬件和操作系统之间交换能源相关信息的接口规范。它定义了计算机硬件的能源相关信息，如电源供应器状态、设备功耗、设备功率因数等。ACPI 是操作系统控制计算机硬件能源管理的标准，同时也是硬件厂商和操作系统之间通信的标准。&lt;/p>
&lt;p>在 deepin 系统中，ACPI 负责处理计算机硬件的能源管理，它与 deepin 系统的电源管理模块进行交互，以实现对计算机硬件的能源管理。&lt;/p>
&lt;p>在分析系统层面的问题时，我们需要了解 ACPI 和电源管理模块的作用和功能，以及它们是如何协同工作的。在本文中，我们将会详细讲解 ACPI 的工作原理以及 deepin 系统中的电源调度模块工作模式，并提出可行优化建议。&lt;/p>
&lt;p>首先，让我们了解一下 ACPI 的工作原理。当计算机硬件发生电源变化时，ACPI 会收集硬件信息，并向操作系统发送电源请求。操作系统收到电源请求后，会根据用户配置自动调整各个硬件的电源策略。而 deepin 系统的电源模块则是帮助用户生成配置来调整 ACPI 的行为。所以在这一方面，我们能做的就是向 ACPI 提供合理的电源策略，在保证性能的同时，降低设备温度并提升续航表现。&lt;/p>
&lt;h3 id="2平台电源配置">2.平台电源配置
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#2%e5%b9%b3%e5%8f%b0%e7%94%b5%e6%ba%90%e9%85%8d%e7%bd%ae">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>相关内核文档&amp;lt;&lt;/strong>&lt;a href="https://docs.kernel.org/userspace-api/sysfs-platform_profile.html">https://docs.kernel.org/userspace-api/sysfs-platform_profile.html&lt;/a>&lt;strong>&amp;gt;&lt;/strong>&lt;/li>
&lt;li>&lt;strong>UEFI ACPI 文档&amp;lt;&lt;/strong>&lt;a href="https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/index.html">https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/index.html&lt;/a>&lt;strong>&amp;gt;&lt;/strong>
平台电源配置是电源管理模块的一个重要组成部分，它用于管理计算机平台硬件（指所有支持 ACPI 的设备）的电源状态，而 CPU 也是属于 ACPI 设备的一部分。不过 CPU 是计算机的核心发热大户，对于其使用平台电源管理可能粒度较粗，所以 APCI 引入了一些 CPU 独有的性能管理方式，在下一小节将详细说明。&lt;/li>
&lt;/ul>
&lt;p>平台电源配置提供了三种可选模式：performance（性能模式），balance（平衡模式），low-power（节能模式）。一般情况下，用户使用平衡模式就可以。在台式机和 mini 主机类（对于功耗和发热没有任何要求）设备上默认提供性能模式，在笔记本等移动设备上默认提供平衡模式。默认不提供节能模式，因为某些 ACPI 设备在节能模式工作过程中可能出现“睡死现象”，所以为了避免此问题，默认不提供 low-power 节电模式。&lt;/p>
&lt;h3 id="3cpu-电源配置">3.CPU 电源配置
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#3cpu-%e7%94%b5%e6%ba%90%e9%85%8d%e7%bd%ae">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>传统电源模式（SCalling）内核文档&amp;lt;&lt;/strong>&lt;a href="https://www.kernel.org/doc/html/v6.1/admin-guide/pm/cpufreq.html">https://www.kernel.org/doc/html/v6.1/admin-guide/pm/cpufreq.html&lt;/a>&lt;strong>&amp;gt;&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Intel PState 内核文档&amp;lt;&lt;/strong>&lt;a href="https://www.kernel.org/doc/html/v6.1/admin-guide/pm/intel_pstate.html">https://www.kernel.org/doc/html/v6.1/admin-guide/pm/intel_pstate.html&lt;/a>&lt;strong>&amp;gt;&lt;/strong>&lt;/li>
&lt;li>&lt;strong>AMD PState 内核文档&amp;lt;&lt;/strong>&lt;a href="https://docs.kernel.org/admin-guide/pm/amd-pstate.html">https://docs.kernel.org/admin-guide/pm/amd-pstate.html&lt;/a>&lt;strong>&amp;gt;&lt;/strong>
如果有时间，可以自行研读上述电源文档，就很容易理解 CPU 电源配置相关内容。作者在这里的讲述将忽略大量技术细节和实现方式，仅表述如何调整 CPU 的电源模式&lt;/li>
&lt;/ul>
&lt;p>在&lt;code>/sys/devices/system/cpu/cpufreq&lt;/code>目录下有许多文件名为&lt;code>policy&amp;lt;x&amp;gt;&lt;/code>（x 代表核心编号），这些文件对应着你电脑上的 CPU 核心，而 CPU 的电源调度细节就在这些文件夹里面。在&lt;code>policy&amp;lt;x&amp;gt;&lt;/code>目录下有一个文件`scaling_driver``,使用 cat 或其他方式访问它，得到的结果就是我们当前使用的调度器：&lt;/p>
&lt;ul>
&lt;li>intel_cpufreq / acpi_cpufreq : 使用 scaling freq 调度&lt;/li>
&lt;li>intel_pastate : 使用 Intel Pstate 调度&lt;/li>
&lt;li>amd-pstate : 使用 AMD Pstate 调度&lt;/li>
&lt;/ul>
&lt;h4 id="scalling-freq-调度"> scalling freq 调度
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#scalling-freq-%e8%b0%83%e5%ba%a6">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>这是最传统的 CPU 调度方式，你可以在 policy&lt;x>文件夹下的 scaling_available_governors 获取可选电源模式：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">英文&lt;/th>
&lt;th style="text-align:left">中文&lt;/th>
&lt;th style="text-align:left">含义&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">performance&lt;/td>
&lt;td style="text-align:left">性能模式&lt;/td>
&lt;td style="text-align:left">最极致的性能表现，最火热的 CPU 温度，最短的续航。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">powersave&lt;/td>
&lt;td style="text-align:left">节能模式&lt;/td>
&lt;td style="text-align:left">为绿色地球出一份力。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">balance&lt;/td>
&lt;td style="text-align:left">平衡模式&lt;/td>
&lt;td style="text-align:left">性能和续航兼顾。小孩子才做选择，我全都要。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">schedutil&lt;/td>
&lt;td style="text-align:left">平衡模式&lt;/td>
&lt;td style="text-align:left">平衡模式的一种，使用不同算法进行调度。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">ondemand&lt;/td>
&lt;td style="text-align:left">平衡模式&lt;/td>
&lt;td style="text-align:left">平衡模式的一种，根据当前 CPU 负载动态调整频率。当负载大于阈值时调整到最高频率，其他情况按负载比例计算频率。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">conservative&lt;/td>
&lt;td style="text-align:left">平衡模式&lt;/td>
&lt;td style="text-align:left">平衡模式的一种，根据当前 CPU 负载动态调整频率。当负载大于最大阈值时步进递增频率，当负载小于最低阈值时步进递减。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">userspace&lt;/td>
&lt;td style="text-align:left">用户模式&lt;/td>
&lt;td style="text-align:left">以用户指定的频率运行 CPU，可通过/sys/devices/system/cpu/cpuX/cpufreq/scaling_setspeed 进行配置&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>你可能好奇，为啥这里有这么多平衡模式，其实这些平衡模式的作用都是是一样的：平衡性能和续航，不过使用的算法可能不同，这里我不做详细说明，我在网络上找到一些详细资料可以参考，有兴趣的朋友可自行查阅：&lt;/p>
&lt;ul>
&lt;li>oppo 内核团队：schedutil governor 情景分析：&lt;a href="https://www.wowotech.net/process_management/schedutil_governor.html">http://www.wowotech.net/process_management/schedutil_governor.html&lt;/a>&lt;/li>
&lt;li>linux 内核文档：schedutil governor：&lt;a href="https://www.kernel.org/doc/html/next/translations/zh_CN/scheduler/schedutil.html">https://www.kernel.org/doc/html/next/translations/zh_CN/scheduler/schedutil.html&lt;/a>&lt;/li>
&lt;li>Arch wiki：&lt;a href="https://wiki.archlinux.org/title/CPU_frequency_scaling">https://wiki.archlinux.org/title/CPU_frequency_scaling&lt;/a>
我们可以更改 scaling_governors 来更改此核心的电源调度（注意：只能更改为 scaling_available_governors 文件中列出的电源调度，不同电脑可能存在差异）。&lt;/li>
&lt;/ul>
&lt;h4 id="intel-pstate"> Intel Pstate
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#intel-pstate">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>这是 Intel 近几代 CPU 独享的 moment，内核开启 intel pstate 后（V23 内核默认开启）你会发现在 policy&lt;x>文件夹下多了几个文件：&lt;/p>
&lt;p>我们只需要关注：&lt;/p>
&lt;ul>
&lt;li>energy_performance_available_perference : 可用的 pstate 电源调度&lt;/li>
&lt;li>energy_performance_perference：当前选定的 pstate 电源调度，可以更改此文件内容来更改电源调度&lt;/li>
&lt;li>在 Intel Pstate 中出现了两个新的调度方案：&lt;/li>
&lt;li>balance_performance : 平衡偏性能，平时工作频率不高，在负载增大时能快速响应&lt;/li>
&lt;li>balance_power : 平衡偏节能，电源策略较为保守
在部分电脑上还有 default 方案，此方案就是经过 pstate 优化过的 balance 策略。具体 PState 使用的黑魔法以及主动模式和被动模式的调度策略，可以参照内核文档进行分析。&lt;/li>
&lt;/ul>
&lt;h4 id="amd-pstate">AMD PState
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#amd-pstate">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>这是 AMD ZEN2 以上用户，以及支持 kernel 6.4.x 用户独享的 moment。其实 AMD 在 6.1 内核已经做了 PState 的支持，不过是被动模式。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>（Actvie Mode）主动模式&lt;/p>
&lt;p>Active Mode 仅在内核版本大于 6.4 以上，且内核选项打开 AMD PState 时可用。可能需要在 grub 内加入启动参数以打开此功能：amd_pstate=active，也可以修改文件实现 Active Mode 的电源策略和 Intel PStatewi 类似。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>（Passive Mode）被动模式&lt;/p>
&lt;p>Passtive Mode 仅在内核大于 6.1 以上，且内核选项打开 AMD PState 时可用。可能需要在 grub 加入启动参数开启此功能：amd_pstate=passive，也可修改文件实现。&lt;/p>
&lt;p>Passive Mode 提供两种电源模式，在&lt;code>/sys/device/system/cpu/cpufreq/scaling_governor&lt;/code>文件进行调整：&lt;/p>
&lt;ul>
&lt;li>performance 使用 platform_profile 进行配置，调度积极性较高&lt;/li>
&lt;li>scheutils 在&lt;code>/sys/device/system/cpu/cpufreq/schedutil/rate_limit_us&lt;/code>文件中调整调度粒度（两次调度的间隔时间）和 ACPI 的 scheutils 类&lt;code>/sys/device/system/cpu/cpufreq/scaling_governor&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>（Guided Mode）引导模式&lt;/p>
&lt;p>Guided Mode 仅在内核大于 6.1 以上，且内核选项打开 AMD PState 时可用。可能需要在 grub 加入启动参数开启此功能：amd_pstate=guided，也可修改文件实现。这就类似汽车的自动挡，驱动程序请求最低和最大性能级别，平台自动选择此范围内适合当前工作负荷的性能级别。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="4gpu-电源管理部分">4.GPU 电源管理部分
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#4gpu-%e7%94%b5%e6%ba%90%e7%ae%a1%e7%90%86%e9%83%a8%e5%88%86">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;h4 id="amd-gpu"> AMD GPU
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#amd-gpu">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>如果是 AMD GPU 则需要更改两个文件（使用 tee 命令进行写入）：&lt;/p>
&lt;ul>
&lt;li>&lt;code>/sys/class/drm/card0/device/power_dpm_state&lt;/code>（这是一个遗留接口，目的是向后兼容）&lt;/li>
&lt;li>performance 高性能模式&lt;/li>
&lt;li>balance 平衡模式&lt;/li>
&lt;li>battery 节能模式&lt;/li>
&lt;li>&lt;code>/sys/class/drm/card0/device/power_dpm_force_performance_level&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>以下设置来自 AMD 官方驱动文档：&lt;/p>
&lt;p>&lt;a href="https://dri.freedesktop.org/docs/drm/gpu/amdgpu.html#power-dpm-force-performance-level">https://dri.freedesktop.org/docs/drm/gpu/amdgpu.html#power-dpm-force-performance-level&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://dri.freedesktop.org/docs/drm/gpu/amdgpu.html#power-dpm-force-performance-level">drm/amdgpu AMDgpu driver — The Linux Kernel  documentation&lt;/a>&lt;/p>
&lt;p>power_dpm_force_performance_level:&lt;/p>
&lt;p>AMD GPU 驱动程序提供了一个 sysfs API，用于调整某些与功率相关的参数。文件 power-dpm-force-performance-level 将用于执行此操作。它接受以下参数：&lt;/p>
&lt;ul>
&lt;li>auto：当选择 auto 时，设备将尝试针对驱动中的当前条件动态选择最佳功率曲线&lt;/li>
&lt;li>low：当选择低时，GPU 被强制到最低功率状态&lt;/li>
&lt;li>high：当选择高时，GPU 被强制到最高功率状态&lt;/li>
&lt;li>manual：当选择手动时，用户可以通过 sysfs pp_dpm_mclk、pp_dpm_sclk 和 pp_dpm_pcie 文件手动调整每个时钟域启用的电源状态，并通过 pp_power_profile_mode sysfs 文件调整电源状态转换方式。&lt;/li>
&lt;li>profile_standard 固定时钟级别分析模式。此模式将时钟设置为固定级别，该级别因 ASIC 而异。这对于分析特定工作负载很有用（不常用）。&lt;/li>
&lt;li>profile_min_sclk 最小 sclk 分析模式。此模式将 sclk 强制设置为最低级别。这对于分析最小功耗的场景很有用（不常用）。&lt;/li>
&lt;li>profile_min_mclk 最小 mclk 分析模式。此模式将 mclk 强制设置为最低级别。这对于分析最小功耗的场景很有用（不常用）。&lt;/li>
&lt;li>profile_peak 峰值分析模式。此模式将所有时钟（mclk、sclk、pcie）设置为最高级别。这对于分析最大性能的场景很有用（不常用）。&lt;/li>
&lt;/ul>
&lt;p>测试：&lt;/p>
&lt;ul>
&lt;li>LOW 模式的跑分&lt;/li>
&lt;/ul>
&lt;p>&lt;img alt="LOW 模式的跑分" src="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/low.jpg" />&lt;/p>
&lt;ul>
&lt;li>auto 模式的跑分&lt;/li>
&lt;/ul>
&lt;p>&lt;img alt="auto 模式的跑分" src="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/auto.jpg" />&lt;/p>
&lt;ul>
&lt;li>high 模式的跑分&lt;/li>
&lt;/ul>
&lt;p>&lt;img alt="high 模式的跑分" src="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/high.jpg" />&lt;/p>
&lt;h4 id="intel-gpu"> Intel GPU
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#intel-gpu">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>intel GPU 使用的 i915 驱动，并不希望你对其做出调整，因为其驱动自带的电源策略已经足够聪明。不过你也可以通过 intel 提供的 intel-gpu-tools 进行调整和获取信息。&lt;/p>
&lt;p>sudo apt install intel-gpu-tools&lt;/p>
&lt;p>然后使用&lt;/p>
&lt;p>sudo intel_gpu_frequency&lt;/p>
&lt;p>来获取当前频率（当前使用的是 Intel A750）&lt;/p>
&lt;p>可以看到 intel 的显卡驱动是在 600 MHz 到 2400 MHz 之间动态调整（如上图）&lt;/p>
&lt;p>测试笔记本下 intel 核显跑分如下
&lt;img alt="图片" src="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/gxn.png" />&lt;/p>
&lt;h4 id="nvidia">Nvidia
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#nvidia">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>由于 nvidia 驱动不开源，所以在系统层面无法对其做控制。&lt;/p>
&lt;h2 id="四应用">四、应用
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#%e5%9b%9b%e5%ba%94%e7%94%a8">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>应用级别的省电，应该就是在保证用户使用流畅度的情前提下下节省性能。之前也有用户提出过，是否能参照 vivo 的 origin3 os 的不公平调度算法来实现优化。毕竟安卓系统的底层也是 linux，理论上实现难度不大。&lt;/p>
&lt;p>Cgroups，全称 Control Groups，是 Linux 内核提供的一种资源管理机制，用于对进程分组并对其资源进行限制和隔离。Cgroups 可以用于限制进程的 CPU、内存、磁盘、网络等资源，也可以用于限制进程的优先级和 IO 权限。利用其提供的能力，我们很容易实现类似不公平调度算法（我们新的 AM 天然支持 Cgroups 的操作），但是我还有一些顾虑：&lt;/p>
&lt;ul>
&lt;li>不同于手机操作系统，计算机操作系统是多任务并行的，在多数窗口管理器下，我们并没有一个明显的前台应用，此时使用不公平调度可能存在隐患；&lt;/li>
&lt;li>容易引发人机对抗。在我的观念里面，计算机是为人服务的，那么用户的意志必定是第一优先级，所以我们不应改变用户的行为，如果使用不平衡调度和用户预期不一致，会极大降低用户体验；&lt;/li>
&lt;li>使用前后台区分应用，可能导致开销和收益比下降，性价比不高。linux 桌面不像安卓设备有明显前后台，那么用户频繁切换应用的操作将导致我们的调度器频繁切换调度，使得开销过大。
我认为最佳的解决方案是：提供能力，但不提供方案。我们可以提供基于 Cgroups 方式修改应用组的优先级，然后让用户自己选择什么应用优先级更高，什么应用优先级低，以实现调度（比如在 dock 上右键选择优先级）或提供一套配置以供用户自由选择。&lt;/li>
&lt;/ul>
&lt;p>如果一个电脑需要使用不平衡调度来保证使用流畅性，可能这并不是一个操作系统能解决的问题，而更应该考虑硬件是否需要更换，以保证多任务使用的流畅性。&lt;/p>
&lt;h2 id="附录常用的调试测试工具">附录——常用的调试测试工具
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#%e9%99%84%e5%bd%95%e5%b8%b8%e7%94%a8%e7%9a%84%e8%b0%83%e8%af%95%e6%b5%8b%e8%af%95%e5%b7%a5%e5%85%b7">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="1s-tui">1. S-tui
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#1s-tui">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>可以看到 CPU 频率变化，配合 stress 可以对 cpu 进行压力测试。
&lt;img alt="图片" src="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/s-tui.jpg" />&lt;/p>
&lt;h3 id="2intel-gpu-tools">2. intel-gpu-tools
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#2intel-gpu-tools">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>可以使用 intel_gpu_frequency 来获取和调整 i965 的驱动频率。&lt;/p>
&lt;h3 id="3glmark2">3. glmark2
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#3glmark2">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>GPU 跑分软件。&lt;/p>
&lt;h3 id="4stress-ng">4. stress-ng
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#4stress-ng">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>CPU 压力测试软件。&lt;/p>
&lt;h3 id="5powertop">5. powertop
&lt;span>
&lt;a href="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/#5powertop">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>电源测试软件，可以看到电源的功耗和使用情况。&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>Wireshark构建编译随笔</title><link>https://blog.deepin.org/posts/impressions-after-compiling-wireshark/</link><pubDate>Sun, 24 Sep 2023 17:14:57 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/impressions-after-compiling-wireshark/</guid><description>&lt;h2 id="随笔">随笔
&lt;span>
&lt;a href="https://blog.deepin.org/posts/impressions-after-compiling-wireshark/#%e9%9a%8f%e7%ac%94">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>在给Wireshark做完编译、调整、上架之后，我打算到论坛里看看大家对开源应用有没有什么疑惑的地方，没想到大家现在对开源的关注度这么高了。在看到最近一些闹得比较沸沸扬扬的话题之后，我也对我现在开源应用适配工作的很多地方产生了疑惑，我这样跟开源还有关系吗？&lt;/p>
&lt;h2 id="重新适配开源应用的意义">重新适配开源应用的意义
&lt;span>
&lt;a href="https://blog.deepin.org/posts/impressions-after-compiling-wireshark/#%e9%87%8d%e6%96%b0%e9%80%82%e9%85%8d%e5%bc%80%e6%ba%90%e5%ba%94%e7%94%a8%e7%9a%84%e6%84%8f%e4%b9%89">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>在一些实际的场景中，其实有很多&amp;quot;重复造轮子&amp;quot;的情况，即明确存在其他开源项目可用来替代的情况下仍然自己重头造一个项目来实现相同/类似的功能。比如各大桌面环境都有自己的一套文管:DDE的dde-file-manager、KDE的Dolphin，但严格意义上他们不是重复造轮子，而是更好地融入桌面环境中。
另外一种情况就是我现在做的适配开源应用，我的作品很大一部分可以在系统的应用商店中找到。但细心的小伙伴可能会发现:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&amp;ldquo;诶，这个应用不是系统仓库里有吗？为什么还要消耗资源去重复适配?&amp;rdquo;&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&amp;ldquo;Debian 12的这个应用已经去到3.6.1了，为什么商店里的还是3.6.0?&amp;rdquo;&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&amp;ldquo;为什么软件官方已经提供了deepin可以直接安装的包，应用商店还要另起一个新包名互不兼容?&amp;rdquo;&lt;/p>
&lt;p>想到这几个问题，大家是不是血压都上来了?这不就是妥妥重复劳动吗?别急，听我慢慢道出里面的小细节。我愿总结为两个核要义:平易、近人。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="易用性">易用性
&lt;span>
&lt;a href="https://blog.deepin.org/posts/impressions-after-compiling-wireshark/#%e6%98%93%e7%94%a8%e6%80%a7">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>首先问大家一个问题，抛开命令真爱粉不谈，你认为用apt安装包方便还是在应用商店看着截图、应用描述、评论等等来一站式点击&amp;quot;安装&amp;quot;方便?
虽然命令也可以直接安装各种系统仓内的各种包，但对于新手用户个人认为还是有一定难度的。而且不使用应用商店来下载，很有可能就与应用商店各种特性擦肩而过了，比如定期的应用推荐、应用版本迭代的changelog。&lt;/p>
&lt;h3 id="用户侧体验优化">用户侧体验优化
&lt;span>
&lt;a href="https://blog.deepin.org/posts/impressions-after-compiling-wireshark/#%e7%94%a8%e6%88%b7%e4%be%a7%e4%bd%93%e9%aa%8c%e4%bc%98%e5%8c%96">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>不知道大家有没有体验最近Wireshark-4.0.8，有使用过的朋友应该会发现我们很贴心的准备了两个desktop启动方式，一个是普通模式一个是root模式，中间的取舍故事今天就不多赘述了，大家有兴趣可以后面单独拎出来。
至于为什么会设置这么两个模式?其实我在适配开源项目前一般都会预览大家在应用商店里的反馈，这次的举动主要是由于Wireshark用普通user权限可以打开程序，但只是能看个壳子，只有root权限打开才是完整的功能。而且大家也反馈了比较多这个情况，我在评估一段之间、测试可行性之后给大家带来了&amp;quot;两个desktop+提示窗+双语支持&amp;quot;的独家体验。
&amp;ldquo;两个desktop+提示窗&amp;quot;是为了让大家方便以不同的模式打开，&amp;ldquo;提示窗&amp;quot;则是根据不同模式给大家展示相关提示信息，&amp;ldquo;双语支持&amp;quot;则是根据系统语言来设置中文和其他语言下分别显示中文提示和英文提示，一定程度上兼顾海外友人的体验。&lt;/p>
&lt;p>&lt;img alt="image" src="https://storage.deepin.org/thread/202309071547445194_MTY4ODg1NTA3OTQ2MTg2MQ_195246_7TDtqIUatOie8hMn_1693545829.png" />&lt;/p>
&lt;p>我观察了下，Wireshark编译之后的原版desktop文件是不提供这种级别的优化的；而现在deepin应用商店里的版本实实在在可以做到了这个层次的优化，亲近人心。
大家可以把细节打在评论上。&lt;/p>
&lt;h3 id="生态多样性">生态多样性
&lt;span>
&lt;a href="https://blog.deepin.org/posts/impressions-after-compiling-wireshark/#%e7%94%9f%e6%80%81%e5%a4%9a%e6%a0%b7%e6%80%a7">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>严格意义上，每个包只有包名是相对唯一的，这也是因为大部分包管理器都会通过包名来检查该包的情况。但对于同一个应用特别是开源项目而言，它允许被多次修改分发，除了用版本号用以区分，也并没有强制要求每个人维护的包名都遵循同一个。
但为什么Debian等主流发行版一般包名都会随着版本迭代而保留使用，一般并不轻易修改包名?个人认为有以下几个可能:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>一般主流发行版仓库中某个重要项目/库为了保证长期稳定性和可维护性，一般都由同一个维护者负责维护，所以包名不会发生明显改动。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>包名变动过大，相关联的其他包/库均需要重新调整依赖关系，不太利于用户体验和仓库维护管理。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>接第一个情况，即便每个人都有编译开源项目的自由，但大家都比较自觉不会向系统仓中投递已经确切存在的包。&lt;/p>
&lt;p>除此之外，如果你同时在应用商店和外部安装一个包名一致的应用，则只会保留版本较新的一个。或者说，如果你想要保留本地安装的版本，但你在应用商店里对其进行升级，那此时你本地的包就会被替换掉了。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="稳定性测试">稳定性测试
&lt;span>
&lt;a href="https://blog.deepin.org/posts/impressions-after-compiling-wireshark/#%e7%a8%b3%e5%ae%9a%e6%80%a7%e6%b5%8b%e8%af%95">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>其实在开源应用上架到应用商店前都会有测试流程，大家也很难保障哪一个版本会有致命问题，或者哪一个版本会丧失预期特性。所以我们在选包之后，一般是先编译一遍，没有致命问题我们才进行其他操作，尽最大努力保驾护航。&lt;/p>
&lt;h2 id="适配开源应用的难度">适配开源应用的难度
&lt;span>
&lt;a href="https://blog.deepin.org/posts/impressions-after-compiling-wireshark/#%e9%80%82%e9%85%8d%e5%bc%80%e6%ba%90%e5%ba%94%e7%94%a8%e7%9a%84%e9%9a%be%e5%ba%a6">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>要是你觉得我们就是单纯把一些官方提供的二进制归档文件甚至是直接可用的deb包转化来上架应用商店，比如Visual Studio Code。那我很坦白告诉你，就是单纯几个字，&amp;ldquo;解包&amp;ndash;运行&amp;ndash;重新打包&amp;rdquo;。
但如果你是看了上文内容的话，就知道事情并不简单了。不只是从源码开始编译，核心在于如何与终端用户、各大开发者站在统一战线。当然，这里指的并不只是不断去满足用户的需要，这种可能会比较适合C端方向。我更希望的，是帮助用户在专业性比较强的问题面前能够提供满意的答卷，比如这次Wireshark，其实大部分用户真的不一定知道需要使用root来运行，所以我们做了优化。
应用/软件包只是一个普通产出物，不可替代的其实是整个适配过程中与其他人产生差异化的用户思维。&lt;/p>
&lt;h2 id="结语">结语
&lt;span>
&lt;a href="https://blog.deepin.org/posts/impressions-after-compiling-wireshark/#%e7%bb%93%e8%af%ad">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>曾经，我觉得我适配出来的开源应用只上架应用商店确实是玷污了开源，而且我并不是对程序Core本身做出了代码性贡献，这些优化充其量可能也就是普通建议而已。但后来，能看到大家对开源的支持，我也释怀了。
我希望以后有机会可以分享我在编译中发生的故事，同时也很支持使用其他发行版的小伙伴可以使用我在deepin上编译、优化调整之后的开源应用。
我也希望在开源这片蓝天里，我不是孤军奋战。&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>deepin-IDE中的调试技术</title><link>https://blog.deepin.org/posts/deepin-ide-debug/</link><pubDate>Fri, 22 Sep 2023 10:19:31 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/deepin-ide-debug/</guid><description>&lt;p>前不久深度科技旗下deepin社区发布了自己的 IDE：deepin-IDE，得到了全网用户尤其是开源社区用户的广泛关注，目前在 GitHub（&lt;a href="https://github.com/linuxdeepin/deepin-unioncode">https://github.com/linuxdeepin/deepin-unioncode&lt;/a>）仓库的 star 数量已经达到 600 多个，说明大家的热情还是很高涨的。&lt;/p>
&lt;div align="center">&lt;img src="https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922163311.png" />&lt;/div>
&lt;p>为了从技术层面给大家的热情做一个反馈，本文试着将 deepin-IDE 内部的一些实现方法进行分享，希望能够解答友友们的疑惑并得到积极的反馈。&lt;/p>
&lt;p>本篇挑了大家关心的“调试”部分进行分享。需要说明的是，deepin-IDE 的调试功能是选用 DAP（Debug Adapter Protocol ）调试适配协议实现的，所以整体架构是围绕该协议搭建的，至于 &lt;code>DAP&lt;/code> 具体是什么，让我们带着问号往下看。&lt;/p>
&lt;h2 id="什么是-dap-协议">什么是 &lt;code>DAP&lt;/code> 协议
&lt;span>
&lt;a href="https://blog.deepin.org/posts/deepin-ide-debug/#%e4%bb%80%e4%b9%88%e6%98%af-dap-%e5%8d%8f%e8%ae%ae">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>&lt;code>DAP&lt;/code> 即调试适配协议( Debug Adapter Protocol )，顾名思义，它是用来对多种调试器进行抽象统一的适配层，将原有 &lt;code>IDE&lt;/code> 和调试工具直接交互的模式更改为和 &lt;code>DAP&lt;/code> 进行交互。该模式可以让 &lt;code>IDE&lt;/code> 集成多种调试器变得更简单，且灵活性更好。&lt;/p>
&lt;p>在 &lt;code>IDE&lt;/code> 中的调试功能有许多小功能组成，包括单步执行、断点、查看变量值等，常规的实现方式是在每个 &lt;code>IDE&lt;/code> 中去实现这些逻辑，且因为调试工具的接口不同，还需要为每个调试工具做一些适配工作，这将导致大量且重复的工作，如下图所示：&lt;/p>
&lt;div align="center">&lt;img src="https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922164609.png" />&lt;/div>
&lt;p>调试适配器协议背后的想法是标准化一个抽象协议，用于开发工具如何与具体调试器通信。这个思想和 &lt;code>LSP&lt;/code>(Language Server Protocol)和 &lt;code>BSP&lt;/code>(Build Server Protocol)类似，都是通过协议去统一相同功能在不同工具之间的差异性。其所处位置如下图所示，其中左边为不同的开发工具，右边为不能同的调试器，不同于开发工具和调试器直接交互的方式，&lt;code>DAP&lt;/code> 将这些交互统一了起来，让开发工具和调试工具都面向 &lt;code>DAP&lt;/code> 编程。&lt;/p>
&lt;p>上图中的交互是通过协议进行，所以不会像通过 &lt;code>API&lt;/code> 的方式存在语言限制，可以更好的适应调试器的集成。&lt;/p>
&lt;h2 id="dap-如何工作">DAP 如何工作
&lt;span>
&lt;a href="https://blog.deepin.org/posts/deepin-ide-debug/#dap-%e5%a6%82%e4%bd%95%e5%b7%a5%e4%bd%9c">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>以下部分解释了开发工具（例如 &lt;code>IDE&lt;/code> 或编辑器）和调试适配器之间的交互，包括具体的协议格式说明、交互流程等。&lt;/p>
&lt;h3 id="调试会话">调试会话
&lt;span>
&lt;a href="https://blog.deepin.org/posts/deepin-ide-debug/#%e8%b0%83%e8%af%95%e4%bc%9a%e8%af%9d">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>开发工具有两种基础的方式和调试器进行交互，分别是:&lt;/p>
&lt;p>【单会话模式】&lt;/p>
&lt;p>在这种模式下，开发工具启动一个调试适配器作为一个单独的进程并且通过标准的std接口进行通信。在调试会话的结束时调试适配器就终止，对于当前的调试会话，开发工具往往需要实现多个调试适配。&lt;/p>
&lt;p>【多会话模式】&lt;/p>
&lt;p>在这种模式下，开发工具不会启动调试适配器，而是假定它已经在运行并且会在特定端口上侦听连接尝试，对于每个调试会话，开发工具在特定端口上启动一个新的通信会话并在会话结束时断开连接。&lt;/p>
&lt;p>在与调试适配器建立连接后，开发工具和调试适配器之间通过基础协议进行通信。&lt;/p>
&lt;h3 id="基础协议">基础协议
&lt;span>
&lt;a href="https://blog.deepin.org/posts/deepin-ide-debug/#%e5%9f%ba%e7%a1%80%e5%8d%8f%e8%ae%ae">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>基础协议由两部分组成，包括头和内容(类似于 HTTP)，头部和内容部分通过“\r\n”进行分割：&lt;/p>
&lt;p>【协议头】&lt;/p>
&lt;p>协议头部分由字段组成， 每个头字段由一个键和一个值组成，用‘:’（一个冒号和一个空格）分隔， 每个头字段都以“\r\n“结尾。由于最后一个协议头字段和整个协议头本身都以 \r\n 终止，并且由于协议头是强制性的，所以消息的内容部分总是在（并唯一标识）两个 \r\n 序列之前。当前只支持一个协议头字段：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">头字段名&lt;/th>
&lt;th style="text-align:left">值类型&lt;/th>
&lt;th style="text-align:left">描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">Content-Length&lt;/td>
&lt;td style="text-align:left">数字&lt;/td>
&lt;td style="text-align:left">这个字段是必须的，用来记录内容字段的长度，单位是字节。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>协议头部分使用的是“ASCII”编码。&lt;/p>
&lt;p>【内容部分】&lt;/p>
&lt;p>内容部分包含了实际要传输的数据，这些数据用 &lt;code>JSON&lt;/code> 格式来描述请求、响应和事件。内容部分用的是 &lt;code>utf-8&lt;/code> 编码&lt;/p>
&lt;p>为了有个具体的认识，这里举个简单的例子。在调试过程中，开发人员经常会使用到下一步操作，在 &lt;code>DAP&lt;/code> 中其协议为：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">Content-Length: 119\r\n
\r\n
&amp;#123;
   "seq": 153,
   "type": "request",
   "command": "next",
   "arguments": &amp;#123;
       "threadId": 3
  }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>类型是“请求”，命令是下一步，参数部分可以携带多个，这里是用的线程Id。
这个协议看着挺简单的，是吧？接下来就讲讲如何使用它。&lt;/p>
&lt;h3 id="使用方法">使用方法
&lt;span>
&lt;a href="https://blog.deepin.org/posts/deepin-ide-debug/#%e4%bd%bf%e7%94%a8%e6%96%b9%e6%b3%95">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>详细的使用方法这里就不涉及，因为用一个时序图就可以说明：&lt;/p>
&lt;div align="center">&lt;img src="https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922164954.png" />&lt;/div>
&lt;p>可以看到，初始化、请求、响应等必要的步骤都在图中。其中调试适配器可以理解为调试器的抽象，调试功能的最终执行者是由对应语言的调试工具实现的。&lt;/p>
&lt;h2 id="在-deepin-ide-中的实现">在 deepin-IDE 中的实现
&lt;span>
&lt;a href="https://blog.deepin.org/posts/deepin-ide-debug/#%e5%9c%a8-deepin-ide-%e4%b8%ad%e7%9a%84%e5%ae%9e%e7%8e%b0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>在 deepin-IDE 中，调试功能的实现是结合 &lt;code>cppdap&lt;/code> + &lt;code>debugmanager&lt;/code> 实现的。&lt;/p>
&lt;p>&lt;code>cppdap&lt;/code> 是一款基于 C++ 开发的 &lt;code>SDK&lt;/code>，基本实现了 &lt;code>DAP&lt;/code> 的全量协议。 deepin-IDE 的客户端和服务端都是应用的该 &lt;code>SDK&lt;/code> 进行开发，据此可以实现以下功能：&lt;/p>
&lt;p>1.通信功能，包括服务端的 TCP 监听，客户端的 TCP 连接等；&lt;/p>
&lt;p>2.&lt;code>DAP&lt;/code> 协议的封装，并实现协议的串行化和解串行化；&lt;/p>
&lt;p>3.提供注册回调功能，从而可以在回调内处理各种事件、请求等；&lt;/p>
&lt;p>它的层级结构如下：&lt;/p>
&lt;div align="center">&lt;img src="https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922165226.png" />&lt;/div>
&lt;p>用 &lt;code>cppdap&lt;/code> 可以减少客户端和服务端不少工作量，也统一了两边的协议数据。而 debugmanager 可以理解为调试器的抽象，包含所有必要的调试要素。整体结构如下：&lt;/p>
&lt;div align="center">&lt;img src="https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922165251.png" />&lt;/div>
&lt;p>左边是客户端，右边是服务端，内部实现如下：&lt;/p>
&lt;h3 id="客户端实现">客户端实现
&lt;span>
&lt;a href="https://blog.deepin.org/posts/deepin-ide-debug/#%e5%ae%a2%e6%88%b7%e7%ab%af%e5%ae%9e%e7%8e%b0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>客户端包含了两个个主要功能，一个是和 &lt;code>DAP&lt;/code> 服务端进行交互，发送调试命令或处理返回的数据；另一个是将&lt;code>DAP&lt;/code> 数据转换后显示到用户界面，并响应界面发送的事件。概括起来就包含业务模块、事件模块、&lt;code>DAP&lt;/code> 模块和界面4个部分。&lt;/p>
&lt;p>业务模块&lt;/p>
&lt;ul>
&lt;li>
&lt;p>业务模块包含了插件类、调试参数、调试管理类等，其中插件类负责插件加载、初始化、获取上下文等，调试管理类用来组合事件、&lt;code>DAP&lt;/code>、界面几个模块。
事件模块&lt;/p>
&lt;/li>
&lt;li>
&lt;p>事件模块包含两个子模块，分别是事件发送和事件接收，比如页面跳转事件、添加\移除断点事件等。
&lt;code>DAP&lt;/code> 模块&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>&lt;code>DAP&lt;/code> 模块基于 &lt;code>cppdap&lt;/code> 开发，采用层级结构，底层是原始 &lt;code>DAP&lt;/code> 协议封装，中间层是针对业务做的进一步封装，简化了向外提供的接口，最上层是对整个调试功能的整合，包括数据缓存、界面元素、命令收发。&lt;/p>
&lt;div align="center">&lt;img src="https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922165331.png" />&lt;/div>
&lt;ul>
&lt;li>界面部分
界面模块包含堆栈界面、变量界面、断点列表、异步对话框等，用于 &lt;code>DAP&lt;/code> 的数据展示。&lt;/li>
&lt;/ul>
&lt;div align="center">&lt;img src="https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922165342.png" />&lt;/div>
&lt;ul>
&lt;li>如上图所示，灰色部分为 &lt;code>DAP&lt;/code> 客户端的界面呈现。&lt;/li>
&lt;/ul>
&lt;h3 id="服务端实现">服务端实现
&lt;span>
&lt;a href="https://blog.deepin.org/posts/deepin-ide-debug/#%e6%9c%8d%e5%8a%a1%e7%ab%af%e5%ae%9e%e7%8e%b0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>服务端的功能分为两个部分，一个是基于 &lt;code>cppdap&lt;/code> 实现命令的收发，另一个是与 &lt;code>gdb&lt;/code> 交互，实现调试程序的启动、暂停、退出等一系列动作。&lt;/p>
&lt;p>DAP&lt;/p>
&lt;ul>
&lt;li>
&lt;p>和客户端一样，服务端也是基于&lt;code>cppdap&lt;/code>实现的通信和协议封装和解析。
调试工具&lt;/p>
&lt;/li>
&lt;li>
&lt;p>和调试工具的交互是通过进程调用的方式实现，接收进程输出得到返回信息。如果调试工具本身支持 &lt;code>DAP&lt;/code> 协议，则可以直接交互。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>至此，本次的分享就到这儿啦！不知道你对 &lt;code>deepin-IDE&lt;/code> 中的调试功能有所了解了吗？&lt;/p>
&lt;p>温馨提示，deepin-IDE 还包含很多有意思的功能，如果大家感兴趣可以积极反馈，后续有机会再进行分享。&lt;/p>
&lt;h2 id="参考文档">参考文档
&lt;span>
&lt;a href="https://blog.deepin.org/posts/deepin-ide-debug/#%e5%8f%82%e8%80%83%e6%96%87%e6%a1%a3">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>&lt;a href="https://microsoft.github.io/debug-adapter-protocol/overview">debug-adapter-protocol&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://wiki.deepin.org/zh/05_HOW-TO/02_%E5%BC%80%E5%8F%91%E7%9B%B8%E5%85%B3/deepin-unioncode">deepin-IDE 使用手册&lt;/a>&lt;/p>
&lt;p>内容来源：deepin社区&lt;/p>
&lt;p>内容作者：deepin-mozart、toberyan&lt;/p>
&lt;p>转载请注明出处&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>深度学习的历史与八卦</title><link>https://blog.deepin.org/posts/history-of-deep-learning/</link><pubDate>Fri, 01 Sep 2023 05:50:31 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/history-of-deep-learning/</guid><description>&lt;h2 id="楔子">楔子
&lt;span>
&lt;a href="https://blog.deepin.org/posts/history-of-deep-learning/#%e6%a5%94%e5%ad%90">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>  有这么一个说法，每多一个数学公式，读者就减少一半。深度学习想来也无法免俗，毕竟技术文章不免艰涩，而要完全绕过公式讲好深度学习与大模型，以臣妾微薄的实力实在是做不到啊。&lt;/p>
&lt;p>  因此，本文先歪歪楼，讲讲深度学习与大模型的历史与八卦，一方面是让大家稍微了解下技术发展的脉络，另一方面也是尝试挽救一下读者的欢心，毕竟历史八卦，人人都爱。&lt;/p>
&lt;h2 id="历史">历史
&lt;span>
&lt;a href="https://blog.deepin.org/posts/history-of-deep-learning/#%e5%8e%86%e5%8f%b2">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>  说到神经网络的起源，一般都会追溯到沃尔特·皮茨（Walter Pitts）与麦卡洛克（McCulloch），其中皮茨起到了更主要的作用，而且更具传奇性。皮茨（见图1）于1923年出生于美国底特律的铁匠家庭，家庭教育以老爸的拳头为主，而在这种家庭环境里，他主要靠自学学会了拉丁文、希腊文、逻辑和数学。在12岁那年，皮茨看完了罗素与怀特海的大厚本《数学原理》，并向罗素写邮件附上了自己发现的一些问题，罗素不免大吃一惊，还回信邀请皮茨到剑桥大学读他的研究生。由于家庭与年龄原因，皮茨当然不可能成行，不过当三年后，罗素到美国芝加哥讲学的消息传到皮茨耳朵里时，他就离家出走，而且终其一生再也没有回去过。其后皮茨遇到了麦卡洛克，两人在数学、逻辑和神经网络上有着共同的看法，并一起努力，于1943年合作完成了知名论文《A LOGICAL CALCULUS OF THE IDEAS IMMANENT IN NERVOUS ACTIVITY》，在这篇论文中，他们用二进制逻辑门来表示神经元，而且证明了此模型可以实现任何经典逻辑，从而表明了神经网络的通用性，奠定了深度学习的基础，同时也建立了神经科学和计算机科学之间的交叉研究。高中未毕业的皮茨受到了数学家与控制论之父维纳的欣赏，破格进入麻省理工学院攻读博士学位，被引荐给了冯·诺依曼，而上述论文也成为了冯·诺依曼关于计算机架构的著名论文《First Draft of a Report on the EDVAC》所引用的唯一一篇文章。但是在其后，首先是维纳与皮茨等断绝了关系，然后科学家们又发现神经网络并不能解释一切生物感知现象，皮茨深感失望，于是烧掉了自己历经数年写作的博士论文，从此退出了科研界，并死于酗酒相关的病症。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/history-of-deep-learning/img-20230831173324.png" />&lt;figcaption>
&lt;h4>图1 沃尔特·皮茨&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  我们要介绍的下一位是弗兰克·罗森布拉特（Frank Rosenblatt），他是感知机（Perceptron）的提出者（感知机在我们之前的文章“深度学习入门”里介绍过），并且于1958年在实验了50次之后，让IBM 704自行学会了识别打孔卡上的标记是在左侧还是在右侧。他认为，通过感知机可以不依赖人类的训练与控制，就能感知、识别和辨认周边的环境，其代表作是《Principles of Neurodynamics: Perceptrons and the Theory of Brain Mechanisms》。但是，当时罗森布拉特设计的感知机实际上是只有一层的神经网络，能力非常有限，相比之下，现代成熟的神经网络则有数十层，因此同样是人工智能专家的明斯基撰文指出了感知机的局限性，并使得相应的研究进入了低潮。直到在2004年，大家重新看到了神经网络的潜力，IEEE协会为此也专门设立了罗森布拉特奖。&lt;/p>
&lt;p>  在神经网络漫长的寒冬期，研究者相对较少，其中就包括了于1974年在其博士论文中提出了反向传播（backpropagation）的Werbos（并由于此贡献获得了IEEE罗森布拉特奖），还有于上个世纪八十年代提出了Hopfield 神经网络的Hopfield。&lt;/p>
&lt;p>  下面隆重登场的是深度学习之父，大名鼎鼎的杰弗里·辛顿（Geoffrey Hinton）。辛顿1986年发表的论文《Learning Representations by Back-Propagating Errors》，给出了通过反向传播学习表征的算法，于2006年其推出了深度学习（Deep Learning）的概念，为深度学习与大模型的大潮解开了序幕。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/history-of-deep-learning/img-20230831173612.png" />&lt;figcaption>
&lt;h4>图2 杰弗里·辛顿&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  另一位深度学习的大佬杨立昆（Yann LeCun）在辛顿麾下求学后就职于贝尔实验室，并在1989～1993年间发明了卷积神经网络（CNN），可以用来解决手写数字识别（如MNIST）的问题。当时可没有GPU，那时CPU的性能也相当低下。&lt;/p>
&lt;p>  2012年是深度学习重要的一年。在这一年，辛顿和他的两个学生Alex Krizhevsky，以及Ilya Sutskeverz共同发布了AlexNet这个多层神经网络。这个神经网络用到了诸多的新技术，包括使用了ReLU作为激活函数，使用了CUDA利用英伟达的GPU来进行神经网络的计算，使用了dropout作为神经网络的一种优化方法等。AlexNet在知名的图像识别分类比赛ImageNet中一鸣惊人，它不仅获得了比赛的冠军，而且其错误率达到了16.4%，比当年亚军的26.2%低了将近10%，比2011年冠军的25.8%低了超过9%，几乎可以认为是降维打击。自此，深度学习名声大噪，大量研究人员都转向深度学习尝试完成计算机视觉等相关的人工智能任务。&lt;/p>
&lt;p>  下面深度学习的发展就顺畅了很多，2014年Ian Goodfellow推出了GAN，通过生成式对抗网络能生成逼真的图像甚至视频；2016年 DeepMind 推出了AlphaGo，其水平很快就远远超过了人类围棋冠军；同样在 2016年，何恺明等研究者推出了ResNet，它成为了包括大语言模型在内的各神经网络的通用技术，对应论文引用截止2023年上半年已经超过了17万，成为了深度学习领域引用最高的文章。&lt;/p>
&lt;p>  2017年是大模型关键技术transformer的诞生年，它是在《Attention is All You Need》这篇论文中被提出的，其后就成为包括BERT、GPT、T5等大语言模型使用的框架，而且跨界到了计算机视觉领域，形成了ViT等新的研究方向，几乎成了一统江湖的标准模型。&lt;/p>
&lt;p>  2018年是深度学习三巨头辛顿、杨立昆与约书亚·本吉奥（Yoshua Bengio）的收获之年，他们因为在深度学习方面的诸多成就与影响力获得了ACM图灵奖，这项计算机科学领域的最高奖。在接下来的几年，深度学习的各项研究成果仍然难以大规模落地，诸多深度学习相关的公司持续烧钱，很多人认为这一波人工智能的热潮马上又要过去了。&lt;/p>
&lt;p>  让我们快进到2022年，上半年以stable diffusion为代表的图像生成模型风靡一时，而到了下半年的十一月底，以ChatGPT为代表的大语言模型横空出世，其知识广度、推理能力与多轮对话能力使得它成为了历史上最快达到一亿用户的产品，重新点燃了人工智能产业化的火炬，并使得业界的诸多大佬认定它是划时代的产品，其影响深远，延续至今。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/history-of-deep-learning/img-20230831173802.png" />&lt;figcaption>
&lt;h4>图3 DNNResearch团队&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  顺便说一句，GPT系列大模型的出品者是OpenAI，而OpenAI的首席科学家就是之前提到过的Ilya Sutskeverz。在2012年AlexNet一飞冲天后，多个大厂邀请辛顿等三人加入，于是辛顿团队成立了一个名为DNNResearch的公司，公司仅有他们叁，公司唯一的目的就是被大厂整体收购。竞拍的公司包括谷歌、百度、微软与DeepMind，随着竞拍价格的逐步走高，微软与DeepMind首先被淘汰出局，谷歌与百度均出价到了4400万美元。辛顿当晚暂停了竞拍，第二天早上便决定公司被谷歌收购。Ilya Sutskeverz在其后做出了诸多贡献，包括首创seq2seq（大语言模型的前身），参与深度学习框架Tensorflow与AlphaGo的开发等，最终于2015年加入OpenAI，成为了GPT等系列产品的核心技术人物。&lt;/p>
&lt;h2 id="八卦">八卦
&lt;span>
&lt;a href="https://blog.deepin.org/posts/history-of-deep-learning/#%e5%85%ab%e5%8d%a6">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>  关于深度学习之父辛顿，还有很多不少八卦，其家族还与中国有着不少联系。&lt;/p>
&lt;p>  从头说起，George Everest是十九世纪英国的大地测量工作者，其主要的工作地域都在印度，他和其前任测量了从喜马拉雅山到印度次大陆最南端科莫林角11.5度的经向弧，由于这些贡献，珠穆朗玛峰（Mount Everest）以他的名字命名。George Everest有一个侄女婿名为乔治·布尔（George Boole），也就是著名的布尔代数的发明者，现代计算机所使用的逻辑与、或、非等运算均来自于他。&lt;/p>
&lt;p>  乔治·布尔的小女儿艾捷尔·丽莲·伏尼契（Ethel Lilian Voynich）则是知名的《牛虻》的作者，这本书描写的是19世纪意大利爱国者反对奥地利统治者的斗争，是风靡一时的革命书籍，在苏联和中国都拥有广大读者。&lt;/p>
&lt;p>  乔治·布尔的大女儿玛丽（Mary）则嫁给了一个姓辛顿的数学家，他们有一个孙子威廉·辛顿（William Hinton），以及孙女琼·辛顿（Joan Hinton），不过在中国，他们一般被称为韩丁与寒春。寒春是芝加哥大学核子物理研究所的研究生，也是曼哈顿计划中少数的女科学家之一，在洛斯阿拉莫斯（Los Alamos）武器试验室做费米的助手，其后反对核战争，并于1948年来到中国，进入延安，投身中国革命，并与阳早（Erwin Engst）结婚，成为了奶牛专家。现在在北京还留有他们的工作成果，那就是北京市昌平区沙河大学城农机院的学农基地，寒春与阳早均为农机院学农基地的创办人，时至今日，每年仍然有大量的北京中学生来到学农基地参加学农实习（笔者的小孩有幸成为其中的一员）。寒春和阳早把大部分生命都奉献给了中国的事业，并最终在中国去世。2004年8月，中国开始实施“绿卡”制度，寒春成为了第一个获得中国“绿卡”的外国人。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/history-of-deep-learning/img-20230831173819.png" />&lt;figcaption>
&lt;h4>图4 阳早与寒春&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  玛丽的另一支当然就是深度学习之父杰弗里·辛顿了，他是玛丽的曾孙，因此比寒春、韩丁晚一辈。当然，他们之间并没有直接联系。&lt;/p>
&lt;p>  深度学习的历史与八卦到此告一段落。下面，我们又将步入正轨，重新来讲讲产品与技术了，咱们下次再见。&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>走进大模型</title><link>https://blog.deepin.org/posts/approaching-llms/</link><pubDate>Thu, 24 Aug 2023 02:02:31 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/approaching-llms/</guid><description>&lt;h2 id="应用大模型">应用大模型
&lt;span>
&lt;a href="https://blog.deepin.org/posts/approaching-llms/#%e5%ba%94%e7%94%a8%e5%a4%a7%e6%a8%a1%e5%9e%8b">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>  AIGC 是基于大模型的，而大模型的基础是深度学习。上一篇文章对深度学习进行了初步介绍，首先是深度学习的神经元起源，引发了基于线性函数的模拟，又因为线性函数无法习得逻辑异或，因此引入了非线性的激活函数，再通过三层神经网络给出了MNIST手写数字识别的模型，接着又介绍了神经网络是如何通过数据与反向传播来学习与调整参数的，最后给出了神经网络的分层结构。&lt;/p>
&lt;p>  大模型的直观应用当然首先体现在包括ChatGPT、文心一言、讯飞星火等问答型产品的使用上，另一方面也体现在编程上，在此先给出大模型的编程应用。以下使用的模型、库与样例均来自于Hugging Face。&lt;/p>
&lt;p>  图1给出了基于大模型的英中翻译代码与运行结果。从图1中可以看到，真实的翻译代码只有14、15两行，其逻辑是使用了Helsinki-NLP的opus-mt-en-zh模型，其中mt代表机器翻译（machine translation）、en和zh分别表示英文和中文。从图1中同样可以看到，翻译结果相对还是比较准确的。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/approaching-llms/%e5%9b%be%e7%89%871.png" />&lt;figcaption>
&lt;h4>图1 基于大模型的英翻中&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  图2给出了基于大模型的文本情感分析的代码与运行结果。从图2中可以看到，实际有用的代码也仅需14、16两行，而且这次没有指定具体模型，只给出了需要text-classification这种模型。代码运行结果是认为文本情绪是负面的（NEGATIVE），准确度大概是90.1546%，这显然是符合实际的，因为文本是对商家发错货的抱怨。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/approaching-llms/%e5%9b%be%e7%89%872.png" />&lt;figcaption>
&lt;h4>图2 基于大模型的文本情感分析&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  图3给出了基于大模型的问答。这次的代码稍多一点，但实际的代码也只有三行。第14行给出了需要一个问答（question-answering）的大模型，但是没有指定大模型的名字，第15行是提问的字符串“What does the customer want”，即用户到底想要什么。第16行则使用上述文本作为上下文，提问字符串作为问题，传给问答大模型获取答案。从运行结果看来，答案还是蛮靠谱的。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/approaching-llms/%e5%9b%be%e7%89%873.png" />&lt;figcaption>
&lt;h4>图3 基于大模型的问答&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  当然，基于大模型的程序还有很多，但是从上面三个例子已经可以看出，基于大模型可以写出简短而强大的自然语言处理的程序，下面让我们走进大模型，看看它究竟是如何做到这一点的。&lt;/p>
&lt;h2 id="走进大模型">走进大模型
&lt;span>
&lt;a href="https://blog.deepin.org/posts/approaching-llms/#%e8%b5%b0%e8%bf%9b%e5%a4%a7%e6%a8%a1%e5%9e%8b">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>  大模型在自然语言处理领域里大放异彩，因此首先需要了解自然语言的特点。&lt;/p>
&lt;p>  自然语言的显著特点（也是难点），那就是词与词之间有着广泛的关联。比如下面两句英文：&lt;/p>
&lt;ul>
&lt;li>Go to the bank to get some money.&lt;/li>
&lt;li>Go to the bank to get some water.&lt;/li>
&lt;/ul>
&lt;p>  只有看到每一句的最后一个词，才能分辨出 bank 到底是银行还是堤坝。再比如下面这两句中文：&lt;/p>
&lt;ul>
&lt;li>今天太冷了，能穿多少穿多少。&lt;/li>
&lt;li>今天太热了，能穿多少穿多少。&lt;/li>
&lt;/ul>
&lt;p>  整句话唯一不同的就是冷与热这两个字，但也就是这句话的一字之差，就导致了整句话的意义完全不同了。&lt;/p>
&lt;p>  因此，自然语言处理的关键点就在于如何能准确地判断词与词之间的关系，如果能准确地知道所有词之间的关系，那即使缺了一个词，也能根据关系推出缺的词应该是什么词。当下处理这一问题的主流技术是transformer，这个词不好翻译，主要因为它和变形金刚的英文一模一样。transformer的核心概念是注意力（attention），即每个词到底在注意其它的哪个词，或者说哪些词之间有什么关系。&lt;/p>
&lt;p>  注意力具体由以下关键概念组成：&lt;/p>
&lt;ul>
&lt;li>每个词（实际上是词元，token）均有对应的 q/query（查询）、k/key（键值）与 v/value（值）这三个矩阵变量&lt;/li>
&lt;li>q[i]用来查询本词（i）与其它词（j）之间的注意力&lt;/li>
&lt;li>k[j]是词j回应查询的键值，具体是q[i]与k[j]相乘后缩放，接着用softmax激活函数处理，再乘以v[j]，这就得到了词i针对词j的注意力att[i,j]。&lt;/li>
&lt;li>词i的对应输出为计算得到的注意力之和，即 y[i] = att[i,1] + att[i,2] + &amp;hellip; + att[i,n]&lt;/li>
&lt;li>注意力可以有多个（multi-head），每个注意力可以关注不同的方向，例如有的注意力关注的是词与词之间的意义，有的关注的是押韵，等等&lt;/li>
&lt;/ul>
&lt;p>  图4给出了一个句子中各个词注意力的计算过程。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/approaching-llms/%e5%9b%be%e7%89%874.png" />&lt;figcaption>
&lt;h4>图4 注意力计算过程&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  句子是“小明、小刚、小红是小强的朋友，小明是&amp;hellip;”，当前的词是“小红”，序号是3，可以看到小红的q值与每个词的k值相乘之后再用softmax处理（缩放操作在这里省略了），接着再与每个词的q值相乘，最后相加即可得到序号为3的输出。&lt;/p>
&lt;p>  那么这些k、q、v 等的值如何确定呢？当然是通过上一篇文章里提到的反向传播进行学习的，那反向传播学习自然语言的正确输出是什么呢？在大语言模型中，其训练手段是使用大量的高质量语料，将词语按序逐批输入大模型，以原句子中的下一个词或者特意被空缺出来的词为正确输出来学习的。例如在上面的句子中，大模型在输入了“小明、小刚、小红是小强的”之后，应该能计算输出“朋友”这个词，如果输出错误，则通过反向传播调整各个参数。也就是说，大模型就是根据一个词之前的词或者周围的词是什么来计算出这个词的，这就是大模型的理论基础和学习方法。&lt;/p>
&lt;p>  注意力虽然是大模型的核心概念，但除此之外，大模型还用到了其他技术，它们包括：&lt;/p>
&lt;ul>
&lt;li>第一步需要将词转为数值向量（vector）的嵌入层（embedding），这也是现在很火的向量数据库的那个向量，向量数据库就是用来查询哪些文本向量比较相似，从而提取出对应的文字材料的&lt;/li>
&lt;li>给词向量加上含位置信息的位置编码，以分辨出词的先后顺序，显然“我爱你”与“你爱我”的意义截然不同，因此词序是很重要的，而注意力本身并没有用到词序&lt;/li>
&lt;li>在注意力层之后的残差处理与归一化处理，技术原理并不难，感兴趣的读者可以看参考资料&lt;/li>
&lt;/ul>
&lt;p>  现在我们已经对大模型的主要技术原理有了粗浅的了解，下面来看看深度学习与大模型的特点。&lt;/p>
&lt;h2 id="大模型的特点">大模型的特点
&lt;span>
&lt;a href="https://blog.deepin.org/posts/approaching-llms/#%e5%a4%a7%e6%a8%a1%e5%9e%8b%e7%9a%84%e7%89%b9%e7%82%b9">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>  从对深度学习以及大模型的初步应用与原理了解，其实我们已经可以得出一些结论。&lt;/p>
&lt;p>  首先，深度学习的算法模型是神经网络及其节点上的参数/权重构成的，深度学习是端到端的机器学习，它不用由人类专家预先设计特征算法，例如不用苦思冥想怎么设计一个提取图像中线条的算法，只需要用大量的高质量标注数据对其进行投喂，就有可能最终训练出质量不错的神经网络（中的各个参数）来。&lt;/p>
&lt;p>  其次，它是一个黑盒子，因为没有特征设计，因此这么多数值构成的神经网络，它为什么能达到最终的目标，为什么某个参数就必须是最后训练出来的那个数值，为什么有的时候识别会失败或者生成会出错，是否可以手工修改某些特定的参数使得其成功率上升，同时又不会导致原来成功的事例失败，这些问题在现阶段都是难以回答的，这就是深度学习的可解释性问题。&lt;/p>
&lt;p>  在ChatGPT之前，大模型其实也在发展，但是ChatGPT表现出来的多领域通用性和流畅性与之前的产品相比有明显的超越，所以造成了轰动。其后的GPT-4表现更为优异，在《Sparks of Artificial General Intelligence：Early Experiments with GPT-4》这篇论文里，微软团队给出了这样的一个示例：&lt;/p>
&lt;pre>&lt;code>提问：Can you write a proof that there are infinitely many primes, with every line that rhymes?
GPT4：Yes, I think I can,
Though it might take a clever plan.
I’ll start by noting Euclid’s proof,
Which shows that primes aren’t just aloof.
Assume we have a finite list,
Of primes, and that none have been missed.
Multiply them all together,
And add one, just to be clever.
The result will be a number,
That has no prime factors, oh wonder!
But every number has a prime,
That divides it, it’s just a matter of time.
So we’ve found a contradiction,
And our finite list needs eviction.
There must be infinitely many primes,
And that’s the end of my rhyming lines.
&lt;/code>&lt;/pre>&lt;p>  提问者让GPT-4写一首诗，来证明素数有无限多个，然后GPT-4很快就完成了这首诗，每两行押韵，而且用欧几里得的经典方法给出了证明。当然，在论文中还有很多让人印象深刻的例子，但是上面的例子已能说明GPT-4确实有了很强的能力。&lt;/p>
&lt;p>  GPT中的G表示生成（Generative），这表示它主要的工作方式是生成内容，内容在这里主要是自然语言文本。按照OpenAI首席执行官Sam Altman的说法，The most important difference between ChatGPT and GPT-4, is that the GPT-4 predicts the next word with greater accuracy，即 ChatGPT与GPT-4最大的差异就在于GPT-4在预测下一个词的准确度比ChatGPT更高。&lt;/p>
&lt;p>  GPT中的P表示预训练（Pretrain），即首先用大量语料训练出基础模型（foundation model），然后再用下游任务相关的语料进行精调（FT，即finetuning）。这些下游任务可能是文本分类、翻译、对话等等，这样就可以不用单独为某个特定任务从头训练了。我们可以把中学教育和通识教育看作是预训练，它为大学最终的专业选择，以及以后更细的工作分工打下了坚实的基础。反过来看，在小学年龄阶段没有上学可能会对以后的择业带来很大的限制，这也可以看成是大脑在应预训练的阶段没有进行有效的预训练导致的问题。&lt;/p>
&lt;p>  GPT中的T表示transformer，这个已经在上文中介绍了。&lt;/p>
&lt;p>  一般认为，GPT-4有更强能力的原因在于：&lt;/p>
&lt;ul>
&lt;li>它提供了大量的高质量数据，原始数据有45T，清洗后的语料是570G，清洗比例接近1%，这是之前几乎没有团队做到的&lt;/li>
&lt;li>数据中混合了大量的代码，原始数据中有830G代码，这一般被认为是推理能力提升的关键点之一，当然另一方面也大大提升了它的代码能力&lt;/li>
&lt;li>展开了大量不同种类的下游任务，如生成、问答、脑暴、闲聊、摘要、分类、提取等等，以上两点也属于多样化工作，它为GPT4的通用性打下了基础&lt;/li>
&lt;li>使用了基于人工反馈的增强学习（RLHF）方法，召集了40个众包团队，撰写了数十万的提示数据以对齐主流价值观&lt;/li>
&lt;/ul>
&lt;p>  一般认为，大模型的表现之所以如此智能，但是之前的小模型神经网络却那么智障，其原因可能在于涌现（emergence）。涌现可以简单认为是单个个体微观上简单的行为，在宏观上大量复合呈现出难以预料的规律。比如每只蚂蚁其行为其实是挺简单的，但是一群蚂蚁在一起，就可以表现出复杂的规律。又如每个神经元的行为都很简单，但是这么多神经元聚集在一起，就形成了聪明的人类大脑，这也算是一种涌现。&lt;/p>
&lt;p>  涌现最直观的例子可能就是康威的生命游戏（Conwey’s Life Game）了，这个游戏是在一个网格平面（类似围棋棋盘）上发生的，每个个里要么有一个存活的细胞，（用黑色格表示），要么就是一个死亡的细胞（用白色格表示），其规则也很简单，只有以下四条：&lt;/p>
&lt;ul>
&lt;li>当前细胞存活时，当周围存活细胞&amp;lt;2时，该细胞死亡（模拟生命数量稀少）&lt;/li>
&lt;li>当前细胞存活时，当周围有2个或3个存活细胞时，该细胞保持存活&lt;/li>
&lt;li>当前细胞存活时，当周围存活细胞&amp;gt;3时，该细胞死亡（模拟生命数量拥挤）&lt;/li>
&lt;li>当前细胞死亡时，当周围存活细胞=3时，该细胞复活（模拟繁殖）&lt;/li>
&lt;/ul>
&lt;p>  那么图5里的四个样式就表示绝对静止的细胞群体。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/approaching-llms/%e5%9b%be%e7%89%875.png" />&lt;figcaption>
&lt;h4>图5 康威生命游戏中绝对静止的细胞群体&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  对每个样式进行分析很快就会知道为什么它们会绝对静止。以第二个样式为例，其每个存活细胞周围都刚好有两个存活细胞，按照规则2，它们都应该保持存活。而任何一个死亡细胞周围都没有四个存活细胞，因此此样式将永远不变。&lt;/p>
&lt;p>  图6给出了震荡循环的细胞群体。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/approaching-llms/%e5%9b%be%e7%89%876.png" />&lt;figcaption>
&lt;h4>图6 康威生命游戏中震荡循环的细胞群体&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  按照康威生命游戏的规则，可以发现图6中的两个样式会演变几步之后又变成当前的样式。以第二个样式为例，其一共有左中右三个存活细胞。左侧与右侧的存活细胞附近只有一个存活细胞，因此按照规则1，会在下一轮死亡。中间的存活细胞附近有两个存活细胞，因此按照规则2，保持存活。同时又可以发现，中间的存活细胞上侧和下侧的死亡细胞由于其附近有三个存活细胞，因此根据规则4，在下一轮它们将复活。以此类推，样式会由横三转为纵三，又转回横三，永远震荡循环。&lt;/p>
&lt;p>  康威生命游戏有着远超过上述样式的复杂度，在宏观上甚至可以看到游走、巡回、扩张、凋零等多种细胞社群的样式，因此四条简单的微观规则就衍生出了让人事先难以预料的宏观样式上的复杂度，是涌现的一个生动形象的例子。&lt;/p>
&lt;p>  以ChatGPT与GPT-4为代表的大模型由于其使用了自然语言对话而引发了轰动，让普通人都能直观感受到大模型的魅力，但同时它作为一个基础设施，也提出了一个难题，就是它的编程接口是基于自然语言的，所以需要做所谓的提示工程（prompt engineering）。所谓提示工程，指的就是想让大模型好好干活，那就需要自己好好琢磨怎么和大模型好好说话。俗话说见人下菜，或者说见人说人话，见鬼说鬼话，那见了ChatGPT，当然就得说ChatGPT话了，不然它就没法理解问题，自然也没法给出好的回答了。Linux圈子里有Linus大佬的一句名言：“talk is cheap, show me your code”，中文翻译也很传神：“废话少说，放码过来”，俗一点的话那就是“少哔哔，秀代码”，但是自打GPT横空出世，以后可能就是“code is cheap, show me your talk”了，毕竟，给GPT一个提示，它可以还你百行代码。&lt;/p>
&lt;p>  不过神经网络毕竟是一种信息压缩，或说是一种函数拟合，因此中间肯定会有信息损失，或说是自己瞎想的填补空白，那就避免不了GPT一本正经的胡说八道，也就是所谓的幻觉（hallucination）了。幻觉是当前大模型应用的主要障碍之一，一般认为，大模型近期的发展将沿着消减幻觉、工具集成（即能使用外部工具）、多模态（即除了文本以外，也能理解和生成图形、语音、视频等内容）、垂直领域、类脑智能、具身（embodied）智能等方向发展。&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>深度学习入门</title><link>https://blog.deepin.org/posts/getting-started-with-deep-learning/</link><pubDate>Mon, 21 Aug 2023 08:22:31 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/getting-started-with-deep-learning/</guid><description>&lt;h2 id="背景">背景
&lt;span>
&lt;a href="https://blog.deepin.org/posts/getting-started-with-deep-learning/#%e8%83%8c%e6%99%af">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>  从去年底以来，AIGC 炙手可热，多个业界大佬都认为 AIGC 会给整个产业带来一场革命，甚至所有的软件都会用 AI 重写。从历史上来看，人机交互方式的变革往往会将操作系统带入下一个世代，著名的例子如从命令行界面的 DOS 到键鼠图形界面的 Windows，以及带来触控界面的 iPhone，领创者都成为了世界顶级企业，带动了整个生态的发展。&lt;/p>
&lt;p>  从技术上来看，AIGC 是基于大模型的，而大模型的基础是深度学习，因此，为了在产品上结合 AIGC，首先从技术上首先需要对深度学习进行有深度的学习。&lt;/p>
&lt;p>  对深度学习与大模型的探索将由一系列文章组成，本文是系列里的第一篇，主要关注的是深度学习的技术入门探索。&lt;/p>
&lt;h2 id="从神经元开始">从神经元开始
&lt;span>
&lt;a href="https://blog.deepin.org/posts/getting-started-with-deep-learning/#%e4%bb%8e%e7%a5%9e%e7%bb%8f%e5%85%83%e5%bc%80%e5%a7%8b">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>  回溯历史，深度学习起始于向人类的大脑学习如何学习。人类大脑皮质的思维活动就是通过大量中间神经元的极其复杂的反射活动，因此不妨先看看神经元的工作机制。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/getting-started-with-deep-learning/%e5%9b%be%e7%89%871.png" />&lt;figcaption>
&lt;h4>图1 神经元结构&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  图1给出了神经元的大体结构，左边是神经元的主体，其输入是左侧的多个树突，其输出是右侧的一个轴突。只有当输入树突的信号足够强烈的时候，输出轴突上才会有信号产生。受此启发，就可以设计一个最简单的有两个输入x&lt;sub>1&lt;/sub>与x&lt;sub>2&lt;/sub>，以及一个输出y的线性函数来模拟单个神经元，引入阈值θ，当 w&lt;sub>1&lt;/sub>x&lt;sub>1&lt;/sub> + w&lt;sub>2&lt;/sub>x&lt;sub>2&lt;/sub> ≥ θ时，y为1（表示有信号），否则y为0（表示无信号）。其中w&lt;sub>1&lt;/sub>与w&lt;sub>2&lt;/sub>分别是x&lt;sub>1&lt;/sub>与x&lt;sub>2&lt;/sub>的参数或权重（weight）。&lt;/p>
&lt;p>  有了这个函数，下面来看看它究竟能做什么。按照逻辑主义的设想，数学可以通过逻辑推衍出来，那么不妨看看，上面的函数是否可以表征出基本逻辑运算，如与、或、异或等，在这里x&lt;sub>1&lt;/sub>、x&lt;sub>2&lt;/sub>与y的取值都只能是0或1。&lt;/p>
&lt;p>  对于逻辑与来说，只有当x&lt;sub>1&lt;/sub>与x&lt;sub>2&lt;/sub>都是1的时候，y才是1，否则y是0，容易尝试得到一组可能的w&lt;sub>1&lt;/sub>、w&lt;sub>2&lt;/sub>与θ，分别是0.5、0.5与0.7，如图2所示。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/getting-started-with-deep-learning/%e5%9b%be%e7%89%872.png" />&lt;figcaption>
&lt;h4>图2 逻辑与的线性函数图&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  图2中横轴为x&lt;sub>1&lt;/sub>，纵轴为x&lt;sub>2&lt;/sub>，从图2中可以看到，(1, 1) 点为实心圆，表示y为1，在(0, 0)、(0, 1)与(1, 0)都是空心圆，表示y为0，中间的虚线表示w&lt;sub>1&lt;/sub>x&lt;sub>1&lt;/sub> + w&lt;sub>2&lt;/sub>x&lt;sub>2&lt;/sub> = θ这条直线，只要这条直线能将(1, 1)点与其它点划分到不同区域，则显然就可以找到至少一组w&lt;sub>1&lt;/sub>、w&lt;sub>2&lt;/sub>与θ满足条件。基于同样的分析，容易知道逻辑或也可以找到对应的w&lt;sub>1&lt;/sub>、w&lt;sub>2&lt;/sub>与θ。但是对于逻辑异或来说，问题就严重了，显然无法找到满足条件的w&lt;sub>1&lt;/sub>、w&lt;sub>2&lt;/sub>与θ，如图3所示。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/getting-started-with-deep-learning/%e5%9b%be%e7%89%873.png" />&lt;figcaption>
&lt;h4>图3 逻辑异或的函数图&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  逻辑异或是当x&lt;sub>1&lt;/sub>与x&lt;sub>2&lt;/sub>中一个为0，另一个为1时y才为1，否则y为0，因此在图3中，点(0,1)与点(1,0)为实心圆，而(0, 0)与(1, 1)为空心圆，显然是无法找到一条直线将两个实心圆与两个空心圆划分在两个不同区域的。因此，上述最朴素的线性神经元函数无法表示逻辑异或，也就意味着有大量的运算无法通过上述线性神经元函数来进行。&lt;/p>
&lt;h2 id="引入激活函数">引入激活函数
&lt;span>
&lt;a href="https://blog.deepin.org/posts/getting-started-with-deep-learning/#%e5%bc%95%e5%85%a5%e6%bf%80%e6%b4%bb%e5%87%bd%e6%95%b0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>  是否能改造上述函数，让它能支持所有运算，从而能承担学习的任务呢？至少，人脑肯定是能学会异或的。现在看来，主要是因为原始的神经元函数太线性导致的这个问题。因此，在深度学习中，就引入了非线性的激活函数（activation function），如图4所示。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/getting-started-with-deep-learning/%e5%9b%be%e7%89%874.png" />&lt;figcaption>
&lt;h4>图4 引入激活函数&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  在图4中，首先原函数被修改成了支持多个输入和多个输出的线性变换函数，这样就能处理更多种类的问题了。因为有了多个输入x&lt;sub>1&lt;/sub>、x&lt;sub>2&lt;/sub>&amp;hellip;x&lt;sub>m&lt;/sub>与多个输出h&lt;sub>1&lt;/sub>、h&lt;sub>2&lt;/sub>&amp;hellip;h&lt;sub>n&lt;/sub>，因此权重的下标也带有两个数字，以表示每个权重的作用，例如 w&lt;sub>12&lt;/sub> 是输入x&lt;sub>2&lt;/sub>与输出h&lt;sub>1&lt;/sub>间的权重。还有一个特殊的权重b&lt;sub>i&lt;/sub>，它被称为偏置（bias），是一个待确定的常数项。这样，h就等于相应的x与w相乘后再加上b。例如，h&lt;sub>i&lt;/sub> = x&lt;sub>i&lt;/sub>w&lt;sub>i1&lt;/sub> + x&lt;sub>2&lt;/sub>w&lt;sub>i2&lt;/sub> + &amp;hellip; + x&lt;sub>m&lt;/sub>w&lt;sub>im&lt;/sub> + b&lt;sub>i&lt;/sub>。
  经过线性变换后得到的输出h&lt;sub>1&lt;/sub>、h&lt;sub>2&lt;/sub>&amp;hellip;h&lt;sub>n&lt;/sub>只是中间过程的输出，在之后，还需要加入一个非线性的激活函数的处理，以得到最终的输出y&lt;sub>1&lt;/sub>～y&lt;sub>n&lt;/sub>，如图4所示。&lt;/p>
&lt;p>  在具体激活函数的选择上，比较常见的有 softmax、sigmoid 与 relu 等。其中 softmax 函数是多分类问题最常用的输出激活函数（多分类问题指的是一个问题有多个确定个数的可能答案，例如是/否问题是二分类问题，而分辨一个手写阿拉伯数字是哪个数就是一个十分类问题，因为可能答案有0～9一共十个），softmax也是包括ChatGPT在内的大模型使用的输出函数。
  使用了激活函数以后，神经网络就可以学习到所有函数了。下面来看一个经典的神经网络的例子，手写数字识别问题，或MNIST问题。MNIST涉及的手写数字在网上是公开的，如图5所示。程序员们可以先想想，如果自己来写一个程序识别手写数字会怎么写。可以识别手写数字的（一个）神经网络的结构如图6所示。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/getting-started-with-deep-learning/%e5%9b%be%e7%89%875.png" />&lt;figcaption>
&lt;h4>图5 MNIST手写数字样例&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/getting-started-with-deep-learning/%e5%9b%be%e7%89%876.png" />&lt;figcaption>
&lt;h4>图6 能识别手写数字的神经网络&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  可以看到图6的神经网络一共用到了三个线性变换，并使用了两个sigmoid 激活函数，以及最后的softmax激活函数，因此可以说这个神经网络是三层的。神经网络的输入（x&lt;sub>1&lt;/sub>～x&lt;sub>784&lt;/sub>）是一个长度为784的数组，其实就是一个28x28=784的手写数字的黑白图像。神经网络的输出（y&lt;sub>1&lt;/sub>～y&lt;sub>10&lt;/sub>）分别代表了0～9的阿拉伯数字，这是一个典型的十分类问题，因此使用softmax也是非常自然的。&lt;/p>
&lt;p>  图6中的神经网络一共有(784x50+50) + (50x100+100) + (100x10+10) = 45360个参数，对比ChatGPT上千亿个参数，这显然是一个微模型，但是它的识别能力却可以达到92.53%，也就是说一万个手写数字，它能正确识别出9253个来。
  那问题就来了，这45360个参数是怎么来的呢？肯定不能是随便什么 45360 个数都能带来这么高的识别率的，要解决这个问题，就需要看看神经网络是怎么学习的了。&lt;/p>
&lt;h2 id="神经网络的学习">神经网络的学习
&lt;span>
&lt;a href="https://blog.deepin.org/posts/getting-started-with-deep-learning/#%e7%a5%9e%e7%bb%8f%e7%bd%91%e7%bb%9c%e7%9a%84%e5%ad%a6%e4%b9%a0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>  在上面已经看到，神经网络里有大量的参数。在最开始，这些参数会被随机分配一些数字（当然如何随机分配也有讲究的，简洁起见，此处先不提），此外也需要准备大量的数据，这些数据一般是多个输入输出的对(x, t)。例如在上面的手写数字识别问题中，输入x就是一个28x28的手写数字图像，输出t就是这个图像对应的0～9中的一个数字。
  这些数据会被分成训练集与测试集。训练集中的数据用来训练神经网络，让神经网络中的参数最终达到正确的值。测试集中的数据用来测试训练后的神经网络，对比看训练后的神经网络在新的数据下得到的结果是否正确。&lt;/p>
&lt;p>  神经网络的训练过程可以大体分为下面几步：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>对训练集中的输入输出对(x, t)进行如下处理&lt;/p>
&lt;/li>
&lt;li>
&lt;p>将x输入到神经网络中，计算得到y&lt;/p>
&lt;/li>
&lt;li>
&lt;p>将y与正确的输出t进行运算得到损失L，损失的计算函数一般是均方差或交叉熵，前者针对的是回归问题（连续函数拟合），后者针对的是分类问题&lt;/p>
&lt;/li>
&lt;li>
&lt;p>根据L调整神经网络的参数，调整的方向是减少L，调整的方法是下面要讲的反向传播&lt;/p>
&lt;p>图7给出了神经网络训练的过程。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/getting-started-with-deep-learning/%e5%9b%be%e7%89%877.png" />&lt;figcaption>
&lt;h4>图7 神经网络训练过程&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  一旦训练完毕，使用的时候就不需要正确输出t，也不需要计算损失L和调整神经网络的参数了，这个过程被称为推理（inference），如图8所示。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/getting-started-with-deep-learning/%e5%9b%be%e7%89%878.png" />&lt;figcaption>
&lt;h4>图8 神经网络推理过程&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  顺便说一句，图中的深度神经网络与神经网络结构是一样的，但是层数较多，因此被称为深度神经网络。&lt;/p>
&lt;p>  下面，再来看看神经网络究竟是怎样通过损失L来调整网络参数的。最简单，也是最直观的方法就是将每个参数都稍微调大或者调小一点，看L会如何变化，如果L变小，则保持此参数的调整，如果L变大，则将此参数反过来调整。以上即正向调整法，思路清晰，操作方法简单，但是计算量极大，因为每调整一个参数就要重新计算一遍y与L。&lt;/p>
&lt;p>  另一种方法就是现在主流的反向传播（BP，backpropagation）法，此方法类似系统发生故障时的根因分析，首先分析最后一层的参数是怎样影响到L的，然后分析倒数第二层的参数是如何影响到最后一层的输入的，如此类推。在数学上，其实就是计算L对某个特定参数w的（偏）导数，因为导数就代表了w的变化会导致L如何变化。根据链式求导法则，L对w的导数等于L对中间变量h的导数乘以h对w的导数，前者相当于计算最后一层参数的导数，后者相当于计算倒数第二层参数的导数，两者相乘即为L对导数第二层参数的导数。&lt;/p>
&lt;p>  下面主要通过求导来展示反向传播，如果希望更直观一点，可以阅读计算图相关的资料。假设真实函数是y=2x+1，则待求函数为wx+b（当然w与b的真实值应该是2与1）。下面通过一组数据（训练集）来通过反向传播逐步计算更新w与b，看看它们否会逐渐逼近2与1。&lt;/p>
&lt;p>  由于这是一个回归问题，因此使用均方差(y-t)&amp;lt; sup&amp;gt;2&amp;lt; /sup&amp;gt;/2作为损失L的函数，显然L对y的导数是y-t，参数更新使用经典的梯度下降法（SGD），即参数新值=参数旧值 - 学习率x(L对参数的导数)，梯度下降有一个粗糙但是直观的理解，那就是学习应该向着导数（梯度）相反（下降）的方向走，在这里学习率这个参数设为0.01。&lt;/p>
&lt;p>  首先，将w与b随机化为0.5与0.6。&lt;/p>
&lt;p>  假设第一个训练对为(0, 1)，则 y = wx + b = 0.5·0 + 0.6 = 0.6，L对w的导数=L对y的导数乘以y对w的导数=(y-t)·x=(0.6-1)·0=0，L对b的导数=L对y的导数乘以y对b的导数=(y-t)·1=-0.4。则w的新值为w-0.01·0=0.5，b的新值为b-0.01·(-0.4)=0.604，显然新的w与b比原来的更接近(2, 1)。&lt;/p>
&lt;p>  若第二个训练对为(1, 2.9)（本来应为1与3，但是增加了一点误差干扰），可以以同样的方法得到新的w为0.51796，而新的b为0.62196，显然比上一对w与b又接近了2与1一点。&lt;/p>
&lt;p>  实际上，若继续增加2x+1附近的数据，可以发现到了十几对训练数对之后，w与b即可相当接近2与1了。&lt;/p>
&lt;p>  以上例子是为了直观感受反向传播的计算而给出的，实际上这种线性函数的回归可以通过数据集基于矩阵一次性算出来，而且训练本身也要考虑收敛的问题，因此实际的深度学习会更复杂一些，但是原理是类似的。&lt;/p>
&lt;p>  总地来说，深度神经网络是由多个层组成的，每一层均有前向（forward）推理的函数，用来从输入计算得到输出，这个过程即为推理。每一层也有反向（backward）传播的函数，用来从后一层传来的导数计算得到本层向前一层传递的导数，并同时更新本层的参数。如果是训练，则需要在最后一层再加上一个输入为t与y的损失层，输出为L，如图9所示。&lt;/p>
&lt;figure>&lt;img src="https://blog.deepin.org/posts/getting-started-with-deep-learning/%e5%9b%be%e7%89%879.png" />&lt;figcaption>
&lt;h4>图9 多层神经网络结构&lt;/h4>
&lt;/figcaption>
&lt;/figure>
&lt;p>  通过以上几乎标准化的神经网络层，深度学习的研究者就可以像搭积木一样对多个层进行排列组合，得到多种多样的深度神经网络，并首先通过反向传播训练出神经网络的参数，继而使用神经网络进行推理应用了。&lt;/p>
&lt;/li>
&lt;/ul></description><author>deepin 社区官方博客</author></item><item><title>DDE-Control-Center 自启动管理插件 项目文档</title><link>https://y-aang.github.io/posts/%E9%A1%B9%E7%9B%AE%E6%96%87%E6%A1%A3dde-control-center/?utm_source=atom_feed</link><pubDate>Mon, 14 Aug 2023 06:00:00 +0000</pubDate><guid isPermaLink="true">https://y-aang.github.io/posts/%E9%A1%B9%E7%9B%AE%E6%96%87%E6%A1%A3dde-control-center/</guid><description>&lt;p>队员：复旦大学 朱元依、沈扬、朱俊杰 &lt;br />
指导老师：张亮、陈辰 &lt;br />
企业导师：王子冲&lt;/p>
&lt;p>本项目为2023年操作系统大赛企业赛道赛题。&lt;/p>
&lt;p>项目链接：&lt;a href="https://github.com/PinappleUnderTheSea/os-final.git">DDE 控制中心自启动管理插件 github仓库&lt;/a>&lt;/p>
&lt;h2 id="1-摘要">1 摘要&lt;/h2>
&lt;p>&lt;code>Deepin&lt;/code>(原名&lt;code>Linux Deepin&lt;/code>)致力于为全球用户提供美观易用，安全可靠的&lt;code>Linux&lt;/code>发行版。该系统由深度科技自主开发，提供了美观易用、极简操作的桌面环境，主要由桌面、启动器、任务栏、控制中心、窗口管理器等组成。其中，控制中心是&lt;code>Deepin&lt;/code>桌面环境的核心组件之一，它是用于管理和配置操作系统各种设置的集成工具。然而，在该控制中心中，并未对用户提供自启动项提供的便捷管理界面。在本项目中，我们为&lt;code>Deepin&lt;/code>操作系统中自启动项的修改功能编写了简洁易用的控制中心插件，将自启动管理的系统功能集成到了控制中心中。该插件以单独的仓库提供，并能够单独构建，一键植入&lt;code>Deepin&lt;/code>控制中心中。&lt;/p>
&lt;h2 id="2-需求分析">2 需求分析&lt;/h2>
&lt;p>最终用户对自启动权限的管理目前只能通过&lt;code>dde-launcher&lt;/code>（启动器/“开始菜单”）的右键菜单进行管理，而控制中心作为控制系统的门户应用反而缺少此功能。&lt;/p>
&lt;p>&lt;strong>用户需求&lt;/strong>&lt;/p>
&lt;p>在官方发布的&lt;code>deepin23-Beta&lt;/code>版本中，对于用户程序与系统程序的自启动管理方法为：找到应用的可执行程序（通常为.desktop）类型的文件，通过右键打开功能菜单的方式设置为开机自启动。但由于操作系统自带的应用程序界面中所有程序都会被展示到，所以当操作系统中应用程序过多的时候用户很难统计到那些程序被设置成了开机自启动，同时对于用户不想参与管理的应用也会展示在应用菜单中。因此我们对于本项目开发所面向的需求是在控制中心（deepin-control-center）中在不影响原有插件所提供的设置服务的基础上，为用户提供一个额外的插件用于管理开机自启动软件，同时插件可以满足用户自行选择需要手动维护的自启动程序，对于用户希望自己管理是否自启动的软件显示在面板上可以对是否自启动进行开关，用户不希望管理的软件默认不自启动不显示到面板；面板也可以提供添加和删除的功能让用户挑选出自己想要在面板上操作的应用。&lt;/p>
&lt;p>&lt;strong>功能需求&lt;/strong>&lt;/p>
&lt;p>deepin作为国产开源的深度Linux桌面系统，不仅为用户提供了人性化、个性化以及对于中文等语言有良好支持的操作系统体验，也为Linux的开发者与学习者提供了控制中心（dde-control-center）与任务栏（dde-dock）等桌面控件的开发者接口与插件注入接口。除了可以不断的扩展完善用户需求与用户体验外，deepin深度桌面在操作系统学科的教学与深入理解方面也带来了很多的可能性与创造性。因此作为OS大赛的参与团队，我们不仅是在希望我们的开发会对deepin项目的完整性、deepin用户的体验感上带来一些帮助，同时也想通过我们自己的努力在学生的视角让操作系统的教学与后续学习有一个优秀的案例和一些开发相关的经验总结，使得我们理论层面的操作系统教学可以有更大的实践空间。&lt;/p>
&lt;h3 id="21-当前方案">2.1 当前方案&lt;/h3>
&lt;p>目前已有的自启动管理方法是在开始菜单中对菜单中所展示应用软件单独进行自启动的设置。具体的方法是对所希望设置自启动项的应用软件选中后右键，点击“设置开机自启动”即可在每次开机时自动打开该应用软件。&lt;/p>
&lt;p>&lt;img alt="默认自启动设置方式" src="https://y-aang.github.io/img/final/%E9%BB%98%E8%AE%A4%E8%87%AA%E5%90%AF%E5%8A%A8%E8%AE%BE%E7%BD%AE%E6%96%B9%E5%BC%8F.jpg" />&lt;/p>
&lt;p>然而，这种方法有两大明显的缺陷：（1）无法向用户展示所有的自启动项设置（2）大批量的自启动项修改极其不便。由此，催生了控制中心自启动管理插件的需求。&lt;/p>
&lt;h3 id="22-插件需求">2.2 插件需求&lt;/h3>
&lt;p>为了完成控制中心插件，我们对需求进行了更细致的刻画。经过总结后，插件的需求主要分为三条：&lt;/p>
&lt;p>1、完成一个控制中心插件，能够展示当前所有开机启动项的列表；
2、能够在插件中，通过用户界面的交互来管理（添加、删除、启用、禁用）开机启动项；
3、插件以单独的仓库提供，并能够单独构建，不需要合并入 dde-control-center 项目。&lt;/p>
&lt;p>其中，第一条需求是该插件的基础。自启动项的列表一方面为用户提供了清晰的展示界面，另一方面也是程序与用户交互，获取修改操作信息的基础。
第二条需求总结了该插件需要支持的功能，即添加、删除、启用、禁用，这些功能需要在前端设计对应的交互界面，同时在后端设计对应的操作接口，调用系统接口以修改自启动设置。
第三条需求与系统发布相关。目前最新的稳定发布版本是&lt;code>Deepin V23 Beta&lt;/code>，而官方版本已经在发布，若修改源码统一编译控制中心，会需要对当前已发行的操作系统版本进行修改，较为不便。故需要单独编译该插件，并将其装载到系统中的插件接口中。&lt;/p>
&lt;h2 id="3-相关资料调研">3 相关资料调研&lt;/h2>
&lt;h3 id="31-deepin开机自启动系统设置">3.1 Deepin开机自启动系统设置&lt;/h3>
&lt;p>&lt;code>Deepin&lt;/code>系统通过检测固定的目录，检测自启动项。通过放置应用程序的&lt;code>.desktop&lt;/code>文件在其中一个自动启动目录中，系统可以检测到该应用程序的自启动设置。通过修改&lt;code>.desktop&lt;/code>文件中的对应字段，可以修改对应应用程序的自启动设置。&lt;/p>
&lt;h4 id="311-自启动目录">3.1.1 自启动目录&lt;/h4>
&lt;p>在&lt;a href="https://standards.freedesktop.org/basedir-spec/">&amp;ldquo;desktop base directory specification&amp;rdquo;&lt;/a>中的&lt;a href="https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#referencing">&amp;ldquo;Referencing this specification&amp;rdquo;&lt;/a> 部分进行定义了自动启动目录是 &lt;code>$XDG_CONFIG_DIRS/autostart&lt;/code>。&lt;/p>
&lt;p>如果同一文件名位于多个自动启动目录下，只应使用最重要目录下的文件。&lt;/p>
&lt;p>示例：&lt;/p>
&lt;p>如果未设置&lt;code>$XDG_CONFIG_HOME&lt;/code>，用户主目录中的自动启动目录为&lt;code>~/.config/autostart/&lt;/code>。&lt;/p>
&lt;p>如果未设置&lt;code>$XDG_CONFIG_DIRS&lt;/code>，系统范围的自动启动目录为&lt;code>/etc/xdg/autostart/&lt;/code>。&lt;/p>
&lt;p>如果未设置&lt;code>$XDG_CONFIG_HOME&lt;/code>和&lt;code>$XDG_CONFIG_DIRS&lt;/code>，并且两个文件&lt;code>/etc/xdg/autostart/foo.desktop&lt;/code>和 &lt;code>~/.config/autostart/foo.desktop&lt;/code>存在，那么只有文件&lt;code>~/.config/autostart/foo.desktop&lt;/code>将被使用，因为&lt;code>~/.config/autostart/&lt;/code>比&lt;code>/etc/xdg/autostart/&lt;/code>更重要。&lt;/p>
&lt;h4 id="312-应用程序的desktop-文件">3.1.2 应用程序的&lt;code>.desktop &lt;/code>文件&lt;/h4>
&lt;p>一个应用程序的&lt;code>.desktop&lt;/code>文件必须符合&amp;quot;桌面入口规范&amp;quot;中定义的格式。所有关键字应按照定义进行解释，但以下情况除外，以便考虑到位于自动启动目录中的&lt;code>.desktop&lt;/code>文件不会显示在菜单中。&lt;/p>
&lt;h5 id="hidden关键字">&lt;code>Hidden&lt;/code>关键字&lt;/h5>
&lt;p>当&lt;code>.desktop&lt;/code>文件的&lt;code>Hidden&lt;/code>关键字设置为&lt;code>true&lt;/code>时，该&lt;code>.desktop&lt;/code>文件必须被忽略。当多个具有相同名称的&lt;code>.desktop&lt;/code>文件存在于多个目录中时，仅应考虑最重要的&lt;code>.desktop&lt;/code>文件中的&lt;code>Hidden&lt;/code>关键字：如果其设置为&lt;code>true&lt;/code>，则其他目录中具有相同名称的所有&lt;code>.desktop&lt;/code>文件也必须被忽略。&lt;/p>
&lt;h5 id="onlyshowin和notshowin关键字">&lt;code>OnlyShowIn&lt;/code>和&lt;code>NotShowIn&lt;/code>关键字&lt;/h5>
&lt;p>&lt;code>OnlyShowIn&lt;/code>项可以包含一个字符串列表，用于标识必须自动启动此应用程序的桌面环境，其他桌面环境不得自动启动此应用程序。&lt;/p>
&lt;p>&lt;code>NotShowIn&lt;/code>项可以包含一个字符串列表，用于标识不得自动启动此应用程序的桌面环境，其他桌面环境必须自动启动此应用程序。&lt;/p>
&lt;p>这两个关键字中的一个，要么是&lt;code>OnlyShowIn&lt;/code>，要么是&lt;code>NotShowIn&lt;/code>，可以出现在单个&lt;code>.desktop&lt;/code>文件中。&lt;/p>
&lt;h5 id="tryexec关键字">&lt;code>TryExec&lt;/code>关键字&lt;/h5>
&lt;p>带有非空&lt;code>TryExec&lt;/code>字段的&lt;code>.desktop&lt;/code>文件如果&lt;code>TryExec&lt;/code>关键字的值与已安装的可执行程序不匹配，则不得自动启动。&lt;code>TryExec&lt;/code>字段的值可以是绝对路径，也可以是没有任何路径组件的可执行文件名。如果指定了没有任何路径组件的可执行文件名，则会搜索&lt;code>$PATH&lt;/code>环境以找到匹配的可执行程序。&lt;/p>
&lt;h5 id="注意事项">注意事项&lt;/h5>
&lt;p>如果通过在系统范围的自动启动目录中安装&lt;code>.desktop&lt;/code>文件来自动启动应用程序，则个人用户可以通过在其个人自动启动目录中放置具有相同名称的&lt;code>.desktop&lt;/code>文件来禁用此应用程序的自动启动，并在其中包含&lt;code>Hidden=true&lt;/code>关键字。&lt;/p>
&lt;h3 id="32-控制中心插件开发">3.2 控制中心插件开发&lt;/h3>
&lt;h5 id="v23控制中心特性">V23控制中心特性&lt;/h5>
&lt;p>1、V23控制中心只负责框架设计，具体功能全部由插件实现；
2、V23控制中心支持多级插件系统，支持插件插入到任意位置中；
3、高度可定制，可定制任意插件是否显示，若插件支持，可定制任意插件内容是否显示。&lt;/p>
&lt;h5 id="v23控制中心插件安装路径说明">V23控制中心插件安装路径说明&lt;/h5>
&lt;p>1、控制中心会自动加载翻译，翻译目录需要严格放置在&lt;code>/$&amp;#123;CMAKE_INSTALL_PREFIX}/$&amp;#123;CMAKE_INSTALL_DATAROOTDIR}/dde-control-center/translations&lt;/code>下，控制中心会自动加载，同时，插件的翻译和名称也有要求，命名为&lt;code>$&amp;#123;Plugin_name}_&amp;#123;locale}.ts&lt;/code>，&lt;code>locale&lt;/code>就是多语言的翻译，翻译文件必须控制和插件名称相同；
2、控制中心的so应该放置在&lt;code>/$&amp;#123;CMAKE_INSTALL_PREFIX}/$&amp;#123;CMAKE_INSTA；LL_LIBDIR}/dde-control-center/modules&lt;/code>下，请使用构建系统的提供的&lt;code>gnuinstall&lt;/code>路径，上面举的例子是&lt;code>cmake&lt;/code>，&lt;code>mesonbuild&lt;/code>也有自己的逻辑。&lt;/p>
&lt;h5 id="v23控制中心开发接口说明">V23控制中心开发接口说明&lt;/h5>
&lt;p>1、&lt;code>ModuleObject&lt;/code>类用于构建每个页面元素，其是插件的核心；
2、&lt;code>PluginInterface&lt;/code>类用于规范插件信息，每个插件必须提供一个`ModuleObject对象。&lt;/p>
&lt;h5 id="标准开发流程示例">标准开发流程示例&lt;/h5>
&lt;p>1、继承&lt;code>PluginInterface&lt;/code>，实现其虚函数；
2、实例化一个根模块，根模块在初始化时不允许有耗时操作，若有耗时操作，应继承&lt;code>ModuleObject&lt;/code>然后实现&lt;code>active&lt;/code>方法，将耗时操作放入其中；
3、若根模块的子项是横向菜单列表，则可使用&lt;code>List&lt;/code>储存其基础信息，继承或使用&lt;code>HListModule&lt;/code>类，然后循环使用&lt;code>appendChild&lt;/code>方法将菜单添加到根模块中；
4、若根模块的子项是纵向菜单列表，则可使用&lt;code>List&lt;/code>储存其基础信息，继承或使用&lt;code>VListModule&lt;/code>类，然后循环使用&lt;code>appendChild&lt;/code>方法将菜单添加到根模块中；
5、以此类推，具体的某个子项菜单同样再次添加菜单列表，直到菜单列表的子项为&lt;code>PageModule&lt;/code>时为止；
6、准备一个以上的&lt;code>Module&lt;/code>继承自&lt;code>ModuleObject&lt;/code>，并实现其&lt;code>page()&lt;/code>方法，然后添加到&lt;code>PageModule&lt;/code>中，注意，&lt;code>page()&lt;/code>方法中需返回新的&lt;code>QWidget&lt;/code>对象；
7、当某个菜单为&lt;code>PageModule&lt;/code>时，使用其&lt;code>appendChild&lt;/code>方法将上方的&lt;code>Module&lt;/code>添加到其子项中，此时，控制中心会根据&lt;code>page&lt;/code>的大小添加滚动条，并将多个&lt;code>page&lt;/code>进行垂直排列进行显示。&lt;code>PageModule&lt;/code>持支嵌套，并且其有默认边距，如果嵌套使用，嵌套的&lt;code>PageModule&lt;/code>边距建议设置为0；
8、若某个&lt;code>VListModule&lt;/code>或&lt;code>PageModule&lt;/code>页面需要附加按钮时，可调其子项&lt;code>ModuleObject&lt;/code>的&lt;code>setExtra&lt;/code>，该&lt;code>ModuleObject&lt;/code>的&lt;code>page&lt;/code>提供按钮，这样该&lt;code>ModuleObject&lt;/code>将显示在&lt;code>VListModule&lt;/code>或&lt;code>PageModule&lt;/code>页面的最下方。&lt;/p>
&lt;h2 id="4-系统框架设计">4 系统框架设计&lt;/h2>
&lt;h3 id="41-项目组织方式">4.1 项目组织方式&lt;/h3>
&lt;h4 id="类图">类图&lt;/h4>
&lt;p>&lt;img alt="类图" src="https://y-aang.github.io/img/final/%E7%B1%BB%E5%9B%BE.jpg" />&lt;/p>
&lt;h4 id="项目文件组织">项目文件组织&lt;/h4>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>├── CMakeLists.txt
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>├── include
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ ├── interface
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ │ ├── ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ └── widgets
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ ├── ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>├── misc
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ ├── ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>├── shell.sh
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>├── src
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ ├── frame
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ │ ├── ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ ├── interface
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ │ ├── ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ ├── plugin-selfstartup
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ │ ├── operation
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ │ │ ├── ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ │ └── window
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ │ ├── ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ └── widgets
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│ ├── ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>└── translations
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ├── ...&lt;span style="color:#f92672">(&lt;/span>translation files&lt;span style="color:#f92672">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ├── desktop
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ├──...&lt;span style="color:#f92672">(&lt;/span>desktop translation files&lt;span style="color:#f92672">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="41-总体思路与系统框架">4.1 总体思路与系统框架&lt;/h3>
&lt;h4 id="411文件框架思路">4.1.1文件框架思路&lt;/h4>
&lt;p>通过阅读dde-control-center的源代码，src部分为插件的实现以及实现代码复用所存在的框架代码，代码的插件部分在src/plugin-selfstartup目录，分为operation和window部分，其中windows部分主要负责控制中心自启动项目的界面构成，operation部分负责参与控制中心对系统文件与配置的控制。translation文件夹为控制中心（dde-control-center）提供了不同语言环境下的支持，通过识别系统的语言环境选择展示到面板不同的语言。include与misc为项目注册到控制中心所必须包含的编译依赖文件。&lt;/p>
&lt;h4 id="412-设计思路">4.1.2 设计思路&lt;/h4>
&lt;p>我们在初赛中已经完成了通过注册到dde-dock实现的自启动插件，对于插件的界面以及界面上按钮对应的功能已经有了一个大致的构想，但由于dde-dock的插件是一个较为独立的结构，而dde-control-center里面的每一个插件都需要往控制中心里注册一个module并且通过rpc方法与dde-application-manager进行远程服务的交流，因此在实现细节上dde-control-center插件和dde-dock插件有着很大的区别。&lt;/p>
&lt;p>用户逻辑方面，我们首先在控制中心实现了管理自启动应用的面板，对于添加到维护列表的应用都会被展示到面板上，对于每一项应用都会提供&lt;code>Enable/Disable&lt;/code>的选择按钮和&lt;code>Delete&lt;/code>的删除按钮，用于管理是否开机自启动与不希望维护改应用的删除。同时在面板有一个添加按钮，用户点击后会打开文件对话框，用户可以自行从中选择自定义路径的应用程序添加到插件维护的列表中。&lt;/p>
&lt;p>插件运行逻辑方面，分别有添加、删除、开启/关闭（反转）逻辑，分别思路如下：&lt;/p>
&lt;ul>
&lt;li>添加逻辑：用户在点击添加按钮后会获取该文件的路径并读取&lt;code>FileInfo&lt;/code>，&lt;code>AddButtonWidget&lt;/code>发送&lt;code>requestCreateFile&lt;/code>信号携带参数&lt;code>Category&lt;/code>名称与&lt;code>FileInfo&lt;/code>到&lt;code>Worker&lt;/code>中；&lt;code>Worker&lt;/code>在&lt;code>/home/user/.config/autostart&lt;/code>文件下判断是否存在一个相同的应用信息，如果不存在则创建一个原&lt;code>.desktop&lt;/code>文件的副本并且添加一行&lt;code>Hidden=false&lt;/code>字段，把应用信息存到App结构体中，同时调用&lt;code>Worker&lt;/code>中对应的&lt;code>Category&lt;/code>的添加函数传入赋值好的App，&lt;code>Category&lt;/code>在内存中同步一份自启动应用信息，最后更新前端页面。&lt;/li>
&lt;li>删除逻辑：在用户点击应用对应行的删除按钮后获取到&lt;code>AppID&lt;/code>，通过&lt;code>getAppById&lt;/code>得到App信息后发送&lt;code>requestDelUserApp&lt;/code>信号到&lt;code>Worker&lt;/code>，&lt;code>Woker&lt;/code>找到&lt;code>autostart&lt;/code>文件夹中对应文件并将其删去，同时把App在对应的&lt;code>Category&lt;/code>中移除，最后更新前端页面。&lt;/li>
&lt;li>反转逻辑：在用户点击对应行的打勾按钮后想获取到AppId，通过&lt;code>getAppById&lt;/code>得到App信息后发送反转请求到&lt;code>Worker&lt;/code>，&lt;code>Worker&lt;/code>读取磁盘中&lt;code>autostart&lt;/code>里对应&lt;code>.desktop&lt;/code>文件文本找到&lt;code>Hidden&lt;/code>字段并将其反转，同时调用&lt;code>Category&lt;/code>把内存中App结构体的&lt;code>Hidden&lt;/code>成员反转，最后更新前端页面。&lt;/li>
&lt;li>启动逻辑：插件启动的时候会调用&lt;code>Category&lt;/code>的&lt;code>getAppItem&lt;/code>函数，该函数从&lt;code>autostart&lt;/code>文件夹中逐文件读取信息存在App结构体中，封装到&lt;code>m_appList&lt;/code>作为初始化时参与维护的应用程序，前端从&lt;code>m_appList&lt;/code>中把应用名称以及是否自启动信息列举到页面&lt;/li>
&lt;/ul>
&lt;h3 id="42-类功能说明">4.2 类功能说明&lt;/h3>
&lt;h5 id="operation部分">operation部分&lt;/h5>
&lt;pre tabindex="0">&lt;code>├── defappmodel.cpp
├── defappmodel.h
├── defappworker.cpp
├── defappworker.h
├── mimedbusproxy.cpp
├── mimedbusproxy.h
└── qrc
&lt;/code>&lt;/pre>&lt;p>operation部分是插件的后端部分，对于控制中心的每个插件都有&lt;code>Model&lt;/code>，&lt;code>Worker&lt;/code>和&lt;code>DBusProxy&lt;/code>三个部分：&lt;/p>
&lt;ul>
&lt;li>&lt;code>Model&lt;/code>：&lt;code>Model&lt;/code>部分通过继承&lt;code>QObject&lt;/code>注册到QT的项目中，私有变量&lt;code>Category&lt;/code>实现了自启动信息在内存中的一个副本用于前端的交互。&lt;/li>
&lt;li>&lt;code>Worker&lt;/code>：&lt;code>Worker&lt;/code>部分提供了插件对文件系统的操作。由于操作系统对于开机自启动的支持在于把对应的&lt;code>.desktop&lt;/code>文件拷贝到&lt;code>/home/user/.config/autostart&lt;/code>中并设置&lt;code>Hidden=false&lt;/code>，因此对于自启动应用管理的插件必须要对文件的读写提供支持，该支持由&lt;code>Woker&lt;/code>部分实现。&lt;/li>
&lt;li>&lt;code>DBusProxy&lt;/code>：由于插件要注册到控制中心并且对应用进行管理，因此需要向运行中的应用程序管理服务（&lt;code>dde-application-manager&lt;/code>）进行交互，管理服务提供了rpc的调用接口，插件通过&lt;code>DBusProxy&lt;/code>部分向管理服务发起远程请求。&lt;/li>
&lt;/ul>
&lt;h5 id="window部分">window部分&lt;/h5>
&lt;pre tabindex="0">&lt;code>├── selfstartup.json
├── selfstartup.cpp
├── selfstartupdetailwidget.h
├── selfstartupplugin.cpp
├── selfstartupplugin.h
└── widgets
├── addbuttonwidget.cpp
├── addbuttonwidget.h
├── category.cpp
└── category.h
&lt;/code>&lt;/pre>&lt;p>&lt;code>window&lt;/code>部分是插件的前端部分，由&lt;code>Plugin&lt;/code>，&lt;code>Detailwidget&lt;/code>，&lt;code>Addbuttonwidget&lt;/code>和&lt;code>Category&lt;/code>四个部分：&lt;/p>
&lt;ul>
&lt;li>&lt;code>Plugin&lt;/code>：&lt;code>Plugin&lt;/code>部分构造了自启动程序插件。包括插件接口的初始化，一级页面的初始化和二级页面的初始化。&lt;/li>
&lt;li>&lt;code>Detailwidget&lt;/code>：&lt;code>Detailwidget&lt;/code>部分构造了自启动程序插件的 &lt;code>app&lt;/code>条目。包括&lt;code>app&lt;/code>条目的外形、位置，&lt;code>app&lt;/code>条目的增删改查操作，以及与&lt;code>worker&lt;/code>和&lt;code>model&lt;/code>的交互操作（通过信号和槽函数实现）。&lt;/li>
&lt;li>&lt;code>Addbuttonwidget&lt;/code>：&lt;code>Addbuttonwidget&lt;/code>部分构造了自启动程序插件的加号按钮。包括加号按钮的外形、位置，新增&lt;code>app&lt;/code>的弹窗显示，新增&lt;code>app&lt;/code>的路径处理，以及与&lt;code>worker&lt;/code>和&lt;code>model&lt;/code>的交互操作（通过信号和槽函数实现）。&lt;/li>
&lt;li>&lt;code>Category&lt;/code>: &lt;code>category&lt;/code>部分为磁盘中autostart文件夹中重要信息在内存中的拷贝，用于插件的窗口部分直接获取到该文件夹中.desktop类型文件的重要字段展示到界面。&lt;/li>
&lt;/ul>
&lt;h3 id="43-实现描述">4.3 实现描述&lt;/h3>
&lt;h4 id="431-defappmodel">4.3.1 DefAppModel&lt;/h4>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>名称&lt;/th>
&lt;th>功能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>DefAppModel&lt;/td>
&lt;td>Model构造函数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~DefAppModel&lt;/td>
&lt;td>Model析构函数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>getModSelfSetUp&lt;/td>
&lt;td>返回SelfSetUp内存Category&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="432-defappworker">4.3.2 DefAppWorker&lt;/h4>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>名称&lt;/th>
&lt;th>功能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>DefAppWorker&lt;/td>
&lt;td>Worker构造函数，连接Worker和Model&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DefaultAppsCategory&lt;/td>
&lt;td>枚举类，用于实现插件的可扩展性，实现对不同类型软件的分类，默认状态为只有SelfSetUp类&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>active&lt;/td>
&lt;td>向应用程序管理服务发出blockSignal(false)消息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>deactive&lt;/td>
&lt;td>向应用程序管理服务发出blockSignal(true)消息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>onReverseUserApp&lt;/td>
&lt;td>对参与维护的自启动应用开关反转处理，把autostart中的.desktop文件Hidden字段反转并同步Category&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>onGetListApps&lt;/td>
&lt;td>与Model处理应用变化信号结束的信息提供的一个空接口，只用于承接信号处理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>onDelUserApp&lt;/td>
&lt;td>在autostart文件夹中删去用户不希望继续维护是否自启动的应用并同步Category&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>onAddUserFile&lt;/td>
&lt;td>向autostart中添加用户希望维护是否自启动的应用并同步Category&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>getCategory&lt;/td>
&lt;td>返回应用类型的分类&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="433-mimedbusproxy">4.3.3 MimeDBusProxy&lt;/h4>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>名称&lt;/th>
&lt;th>功能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>MimeDBusProxy&lt;/td>
&lt;td>DBusProxy构造函数。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DeleteApp&lt;/td>
&lt;td>向应用程序管理服务发送删除App请求。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AddUserApp&lt;/td>
&lt;td>向应用程序管理服务发送添加用户App请求。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ListApps&lt;/td>
&lt;td>向应用程序管理服务发送展示所有App请求。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Change&lt;/td>
&lt;td>向应用程序管理服务发送App变动处理请求。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="434-selfstartupdetailwidget">4.3.4 SelfStartupDetailWidget&lt;/h4>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>名称&lt;/th>
&lt;th>功能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>SelfStartupDetailWidget&lt;/td>
&lt;td>自启动软件条目窗口构造函数。初始化条目窗口中的文字不可编辑、icon大小、条目形状、条目不可移动，初始化存储软件列表的QStandardItemModel，初始化软件条目的布局。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~SelfStartupDetailWidget&lt;/td>
&lt;td>自启动软件条目窗口析构函数。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>setModel&lt;/td>
&lt;td>设置自启动软件条目窗口的当前model。根据当前窗口的分类，设置不同的窗口model（由于本插件目前只有一个分类，因此setModel功能相当于直接调用setCategory功能）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>setCategory&lt;/td>
&lt;td>设置自启动软件条目窗口的当前分类。将分类的增、删、改的信号和对应的自启动软件条目窗口的槽函数连接，将分类中的软件放入存储软件列表的QStandardItemModel中，并更新自启动软件条目窗口。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>updateListView&lt;/td>
&lt;td>更新自启动软件条目窗口。依次读取自启动软件条目窗口的当前model中的每一个软件状态，依照软件状态，更新窗口显示（显示是否自启动、软件名称、软件icon、删除按键）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>getAppIcon&lt;/td>
&lt;td>获取软件的icon。从系统中获取软件的icon，并统一调整为32*32大小。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>getAppById&lt;/td>
&lt;td>通过ID获取APP结构体。遍历分类中的app，返回对应的APP结构体。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>appendItemData&lt;/td>
&lt;td>向model中新增app信息。从APP结构体中获取app信息，向model中新增app，并更新总app数量。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>isDesktopOrBinaryFile&lt;/td>
&lt;td>判断文件是否属于桌面或二进制文件。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>isValid&lt;/td>
&lt;td>判断app是否有效。判断app的ID非空。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>reverseItem&lt;/td>
&lt;td>向category发出app自启动状态转换的信号。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>requestDelUserApp&lt;/td>
&lt;td>向category发出删除app的信号。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>onListViewClicked&lt;/td>
&lt;td>自启动软件条目窗口被点击后的槽函数。从自启动软件条目窗口获取app信息，并向category发出app自启动状态转换的信号。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>onDelBtnClicked&lt;/td>
&lt;td>自启动软件条目窗口删除按钮被点击后的槽函数。从自启动软件条目窗口获取app信息，并向category发出删除app的信号。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>onClearAll&lt;/td>
&lt;td>清空model中所有的app信息。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>getAppListview&lt;/td>
&lt;td>返回model中所有的app信息。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AppsItemChanged&lt;/td>
&lt;td>重置model中所有的app信息。依次将app_list中的app信息存入model中，并连接激活、点击信号。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>onReverseApp&lt;/td>
&lt;td>category返回app自启动状态转换信号的槽函数。更新对应model中app的自启动状态，并更新窗口。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>addItem&lt;/td>
&lt;td>category返回app新增信号的槽函数。向model中新增对应的app信息，并更新窗口。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>removeItem&lt;/td>
&lt;td>category返回app删减信号的槽函数。向model中删减对应的app信息，并更新窗口。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>showInvalidText&lt;/td>
&lt;td>设置自启动软件条目窗口的字体、图标的位置、大小。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="435-selfstartupplugin">4.3.5 SelfStartupPlugin&lt;/h4>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>名称&lt;/th>
&lt;th>功能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>SelfStartupPlugin&lt;/td>
&lt;td>自启动程序插件的构造函数。基于DCC_NAMESPACE::PluginInterface的接口。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>name&lt;/td>
&lt;td>返回自启动程序插件的名称。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>module&lt;/td>
&lt;td>自启动程序插件初始化函数。初始化自启动程序插件的一级页面、二级页面和加号按钮。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>location&lt;/td>
&lt;td>返回自启动程序插件的位置。即，在控制中心插件中的排序。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="436-selfstartupmodule">4.3.6 SelfStartupModule&lt;/h4>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>名称&lt;/th>
&lt;th>功能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>SelfStartupModule&lt;/td>
&lt;td>自启动程序插件一级页面的构造函数。初始化一级页面的名称，描述，图标，work，model。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~SelfStartupModule&lt;/td>
&lt;td>自启动程序插件一级页面的解构函数。向m_work、m_model发送删除信号。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>work&lt;/td>
&lt;td>返回m_work。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>model&lt;/td>
&lt;td>返回m_model。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>active&lt;/td>
&lt;td>激活m_work。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="437-selfstartupdetailmodule">4.3.7 SelfStartupDetailModule&lt;/h4>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>名称&lt;/th>
&lt;th>功能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>SelfStartupDetailModule&lt;/td>
&lt;td>自启动程序插件二级页面的构造函数。初始化二级页面的名称，分类，work，model。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>page&lt;/td>
&lt;td>自启动程序插件二级页面的初始化函数。初始化DetailWidget，并将DetailWidget的app状态修改信号、删除信号与work的槽函数相连接。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="438-category">4.3.8 Category&lt;/h4>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>名称&lt;/th>
&lt;th>功能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Category&lt;/td>
&lt;td>Category类构造函数，继承QObject类，每个Category类里面封装相同类型的应用信息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>getName&lt;/td>
&lt;td>获取当前Category分类的名称&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>setCategory&lt;/td>
&lt;td>设置当前Category的类型名称&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>getappItem&lt;/td>
&lt;td>获取当前Category封装的应用信息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>clear&lt;/td>
&lt;td>清空当前Category储存的应用信息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>addUserItem&lt;/td>
&lt;td>把传入应用信息存到Category中并向前端发送更新信号&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>delUserItem&lt;/td>
&lt;td>把目标应用从Category中删除并向前端发送更新信号&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>reverseUserItem&lt;/td>
&lt;td>设置目标应用Hidden字段反转并向前端发送更新信号&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="5-系统测试情况">5 系统测试情况&lt;/h2>
&lt;h3 id="51前端测试">5.1前端测试&lt;/h3>
&lt;p>完成插件安装后，可在 DDE 控制中心中看到名为“自启动程序”的管理选项：&lt;/p>
&lt;p>&lt;img alt="前端页面" src="https://y-aang.github.io/img/final/%E5%89%8D%E7%AB%AF%E9%A1%B5%E9%9D%A2.jpg" />&lt;/p>
&lt;h3 id="52-自启动管理功能测试">5.2 自启动管理功能测试&lt;/h3>
&lt;p>点击改图标，可以进入自启动管理界面：&lt;/p>
&lt;p>&lt;img alt="自启动管理页面" src="https://y-aang.github.io/img/final/%E8%87%AA%E5%90%AF%E5%8A%A8%E7%AE%A1%E7%90%86%E9%A1%B5%E9%9D%A2.jpg" />&lt;/p>
&lt;p>该界面展示了设置所有的自启动项的软件，如果在软件右边出现了蓝色勾，则说明该软件设置了开机自启动。可以通过点击蓝色的勾取消开机自启动的设置，再次点击则可恢复。由此实现了自启动管理的开启、禁用功能。&lt;/p>
&lt;p>底部的加号用于添加需要进行自启动管理的软件，点击后跳出选择窗口：&lt;/p>
&lt;p>&lt;img alt="应用选择页面" src="https://y-aang.github.io/img/final/%E5%BA%94%E7%94%A8%E9%80%89%E6%8B%A9%E9%A1%B5%E9%9D%A2.jpg" />&lt;/p>
&lt;p>进入软件所安装的文件夹，选择该软件，即可将其添加入自启动管理中。在自启动管理界面中，可以点击软件右侧灰色的叉号，以将该软件剔除出自启动管理的界面。由此实现了自启动管理的添加、删除功能。&lt;/p>
&lt;h2 id="6-插件的可扩展性">6 插件的可扩展性&lt;/h2>
&lt;p>本次项目的插件完整的覆盖了OSCOMP-proj223的所有需求，但由于只是一个短期开发的项目，因此有构想到了很多可以对插件进行扩展的方面；同时deepin作为开源社区也为开发者提供了良好的扩展接口，我们考虑了以下几点的扩展路径：&lt;/p>
&lt;h3 id="61-语言扩展">6.1 语言扩展&lt;/h3>
&lt;p>对于translation 板块，我们修改和编译仅仅使用了dde-control-center的zh_CN部分，限于语言广度我们只能提供插件的简体中文和英文模式，对于deepin可以支持的其他语言还有待扩展&lt;/p>
&lt;h3 id="62-用户体验扩展">6.2 用户体验扩展&lt;/h3>
&lt;p>由于Linux系统贯彻了&amp;quot;everything is a file&amp;quot;这一思想，因此整个操作系统的磁盘布局是文件化的，用户的应用可以安装到磁盘的任意地方，同时考虑到大部分Linux使用者为具有计算机基础能力的开发者，我们在插件中设计的添加应用按钮是让用户自主找到文件路径并添加。但是对于一般用户，这样的操作可能有些许复杂，所以我们考虑一个提高用户体验的方式：deepin中系统应用和从应用商店获取的用户程序分别分布在了两个文件夹中，如果用户没有自定义路径的话，我们的插件可以在用户请求添加的时候扫描这两个文件夹，提前给用户展示出可能用户希望添加的应用程序可供直接点击添加&lt;/p>
&lt;h3 id="63-实时性扩展">6.3 实时性扩展&lt;/h3>
&lt;p>我们测试了dde-control-center的大部分插件，我们发现几乎所有都没有实现实时扫描磁盘的功能——即如果我们在dde-control-center外部对插件所管理的磁盘进行了修改的话，前端的界面并不会感受到更新而刷新界面。以自启动为例，我们实现了管理插件，但是用户仍然可以在应用菜单栏从控制中心外部添加到自启动中，此时自启动并不会更新页面，需要放回上一级窗口重新启动插件进行扫描。对于这一项扩展，我们考虑了一种方法为新起一个线程不停的扫描autostart文件夹中的.desktop文件并与Category中的比较，如果出现了不一致则发送（Q_Emit）更新窗口的信号，这样可以实现不返回上一级的情况下更新。但该功能只是作为扩展性的一个构想，实际实现的话首先带来的用户体验收益并不是很大，且已经存在插件的情况下应用场景很小，同时开启一个持续扫描的线程对于内存与插件的效率有较大的开销。&lt;/p>
&lt;h3 id="64-并发性扩展">6.4 并发性扩展&lt;/h3>
&lt;p>插件的工作流程包含了修改磁盘与更新内存中的映射同时展示到面板上。对于磁盘的修改会涉及到一定次数的I/O，因此时间开销一定会比内存中信息更新更大的。因此我们思考了可以在此插件的基础上在新起一个进程，用rpc的方式对插件提供服务，服务内容包括对autostart文件夹的增删查改，插件只需要修改内存中的部分并作为rpc客户端向磁盘I/O的服务发送函数调用请求即可&lt;/p>
&lt;h3 id="65-一致性的扩展">6.5 一致性的扩展&lt;/h3>
&lt;p>因为插件会同时修改内存与磁盘的信息，并且保证二者相同，此时就会存在一定的一致性问题：&lt;/p>
&lt;ul>
&lt;li>磁盘和内存写入不同步，在其中一个进行的过程中发生了崩溃程序退出，此时是否成功写入成为一个一致性问题。我们的解决方式为先写入磁盘再写入内存，同时在重启插件的时候会重新扫描一次磁盘，这样只要写入磁盘的第一阶段成功之后即使崩溃也可以在重启的时候恢复数据&lt;/li>
&lt;li>多进程同时操作：如果有多个控制中心进程同时进行了文件的操作，采取的方式为读磁盘，写磁盘，写内存的操作，这样可以使得在读磁盘的时候如果有同名的应用被添加到里面会被正在写入（包括添加与删除操作）的进程阻塞读取，在写入过程结束之后才会触发读磁盘，然后写内存的时候判断是否存在，如果存在则跳过第三步，如果不存在则再写入磁盘&lt;/li>
&lt;/ul>
&lt;h2 id="附录a-插件安装">附录A 插件安装&lt;/h2>
&lt;h3 id="1-开发环境配置">1 开发环境配置&lt;/h3>
&lt;h4 id="11-配置-deepin-操作系统">1.1 配置 Deepin 操作系统&lt;/h4>
&lt;p>开发环境：Deepin V23Beta版&lt;/p>
&lt;p>系统架构：x86&lt;/p>
&lt;p>镜像下载链接：https://mirrors.ustc.edu.cn/deepin-cd/releases/23-Beta/&lt;/p>
&lt;p>虚拟机平台：WMware Workstation 16Pro&lt;/p>
&lt;p>操作系统环境搭建参考博客：https://blog.csdn.net/qq_44133136/article/details/105887560&lt;/p>
&lt;h4 id="12-配置-deepin-插件环境">1.2 配置 Deepin 插件环境&lt;/h4>
&lt;p>&lt;strong>安装依赖包&lt;/strong>：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>sudo apt build-dep .
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo apt install -y qt5-default
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo apt-get install dde-control-center-dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="13-插件安装测试">1.3 插件安装测试&lt;/h4>
&lt;h5 id="安装插件">安装插件：&lt;/h5>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>sudo sh install.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>安装成功后，打开控制中心，会看到以下自启动插件图标，即为安装成功：&lt;/p>
&lt;p>&lt;img alt="控制中心插件图标" src="https://y-aang.github.io/img/final/%E6%8E%A7%E5%88%B6%E4%B8%AD%E5%BF%83%E6%8F%92%E4%BB%B6%E5%9B%BE%E6%A0%87.jpg" />&lt;/p>
&lt;p>此时，如果进入&lt;code>/usr/lib/x86_64-linux-gnu/dde-control-center/modules/&lt;/code>文件夹，看到编译出的&lt;code>.so&lt;/code>文件已经被下载到该文件夹中：&lt;/p>
&lt;p>&lt;img alt="插件安装位置文件夹" src="https://y-aang.github.io/img/final/%E6%8F%92%E4%BB%B6%E5%AE%89%E8%A3%85%E4%BD%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E5%A4%B9.jpg" />&lt;/p>
&lt;h3 id="14-插件卸载测试">1.4 插件卸载测试&lt;/h3>
&lt;h5 id="卸载插件">卸载插件：&lt;/h5>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>sudo sh uninstall.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>重启控制中心，可以看到原本的“自启动管理”图标消失，即为卸载成功。&lt;/p>
&lt;h2 id="附录b-开发过程问题记录">附录B 开发过程问题记录&lt;/h2>
&lt;h3 id="1-开发环境配置问题">1 开发环境配置问题&lt;/h3>
&lt;h4 id="11-deepin-v20-beta与deepen-v209">1.1 Deepin V20 Beta与Deepen V20.9&lt;/h4>
&lt;p>在开发环境配置过程中，我们遇到了比较大的问题。&lt;/p>
&lt;p>我们起初顺延初赛的思路进行开发，在&lt;code>Deepin V20Beta&lt;/code>版中进行开发。我们完成相关依赖包的安装后可以顺利地编译我们所写的控制中心自启动插件。但我们发现安装插件后的控制中心只剩我们所编译的自启动插件。（如下图中仅剩测试编译的 Default Applications 和 Self Start-up 插件）&lt;/p>
&lt;p>&lt;img alt="失败编译结果-仅剩插件" src="https://y-aang.github.io/img/final/%E5%A4%B1%E8%B4%A5%E7%BC%96%E8%AF%91%E7%BB%93%E6%9E%9C-%E4%BB%85%E5%89%A9%E6%8F%92%E4%BB%B6.jpg" />&lt;/p>
&lt;p>我们推测是因为系统自动配置的控制中心框架与网上的控制中心开源代码版本不同，导致插件安装后框架复写。故我们只需重新编译控制中心的代码重新安装至虚拟机系统中即可完成测试。&lt;/p>
&lt;p>然而，在尝试编译控制中心时各种尝试均失败。报错DTK版本不符，未找出解决方法。在&lt;code>Deepen V20.9&lt;/code>中亦未成功。&lt;/p>
&lt;h4 id="12-deepin-v23beta">1.2 Deepin V23Beta&lt;/h4>
&lt;p>在遇到上述问题后，我们加入了&lt;code>Matrix&lt;/code>的Deepin开发者社区，在线上与其他开发者讨论我们所遇到的问题。&lt;/p>
&lt;p>经过与其他开发者的讨论，我们得知最新的开源版本的控制中心已不再支持 20 版的Deepin系统。&lt;/p>
&lt;p>于是我们重新安装最新版本的&lt;code>Deepin V23Beta&lt;/code>，成功的将我们所编写的插件植入控制中心中。&lt;/p>
&lt;h3 id="2-图标与文字颜色展示问题">2 图标与文字颜色展示问题&lt;/h3>
&lt;p>我们初步的demo展示出来的界面所有管理的应用程序都是统一显示的默认应用程序的图标，但我们还是希望要和deepin桌面上.desktop文件相同的图标；与此同时，我们发现发现插件中的应用程序名字都是红色，同时希望这里是一个经典的黑色，这两个都是关于item的展示问题。我们关注到了这一段代码：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-c++" data-lang="c++">&lt;span style="display:flex;">&lt;span>QIcon icon &lt;span style="color:#f92672">=&lt;/span> getAppIcon(iconName, QSize(&lt;span style="color:#ae81ff">32&lt;/span>, &lt;span style="color:#ae81ff">32&lt;/span>));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>act&lt;span style="color:#f92672">-&amp;gt;&lt;/span>setIcon(icon);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>act&lt;span style="color:#f92672">-&amp;gt;&lt;/span>setTextColorRole(DPalette&lt;span style="color:#f92672">::&lt;/span>TextTitle);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>act&lt;span style="color:#f92672">-&amp;gt;&lt;/span>setIconText(name);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>关于icon问题，我们可以对App结构体进行扩展，增加一条icon字段，通过读取带有图标应用程序.desktop文件中的icon字段存到当中，如果没有icon字段的程序设置为“application-default-icon”，并且在展示的时候调用&lt;code>act-&amp;gt;setIcon()&lt;/code>传入icon字段&lt;/li>
&lt;li>关于颜色问题，上述代码中看到了&lt;code>act-&amp;gt;setTextColorRole()&lt;/code>字段，里面传入了文字颜色类型的变量，在DPalette中定义了如下常量，初始选择的红色文字为&lt;code>TextWarning&lt;/code>，我们只需要替换为&lt;code>TextTitle&lt;/code>即可&lt;/li>
&lt;/ul>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>TypeName&lt;/th>
&lt;th>颜色类型&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>ItemBackground&lt;/td>
&lt;td>列表项的背景色&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TextTitle&lt;/td>
&lt;td>标题型文本的颜色&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TextTips&lt;/td>
&lt;td>提示性文本的颜色&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TextWarning&lt;/td>
&lt;td>警告类型的文本颜色&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>TextLively&lt;/td>
&lt;td>活跃式文本颜色（不受活动色影响）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LightLively&lt;/td>
&lt;td>活跃式按钮（recommend button）背景色中的亮色（不受活跃色影响）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DarkLively&lt;/td>
&lt;td>活跃式按钮（recommend button）背景色中的暗色，会从亮色渐变到暗色（不受活跃色影响）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>FrameBorder&lt;/td>
&lt;td>控件边框颜色&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>NColorTypes&lt;/td>
&lt;td>无颜色类型&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="3-翻译问题">3 翻译问题&lt;/h3>
&lt;p>翻译主要涉及到：插件名的翻译和自启动软件名的翻译。&lt;/p>
&lt;p>对于插件名的翻译。我们学习了&lt;code>DDE-NETWORK-CORE&lt;/code>仓库的做法，首先我们编写翻译成不同语言的&lt;code>.ts&lt;/code>翻译文件，使用&lt;code>CMakelists&lt;/code>中的&lt;code>qt5_add_translation&lt;/code>指令获得&lt;code>.qm&lt;/code>翻译文件，再将翻译的&lt;code>.qm&lt;/code>文件&lt;code>install&lt;/code>进入&lt;code>$&amp;#123;CMAKE_INSTALL_DATAROOTDIR}/dde-control-center/translations&lt;/code>（通常是&lt;code>$/usr/share/dde-control-center/translations&lt;/code>），在通过&lt;code>loadTranslator&lt;/code>载入系统对应语言的翻译文件。&lt;/p>
&lt;p>对于自启动软件名的翻译。考虑到部分软件缺失官方翻译名称，无法软件的信息中直接获取，我们目前只采用英文格式。&lt;/p>
&lt;h3 id="4-开机启动项处理思路问题">4 开机启动项处理思路问题&lt;/h3>
&lt;p>为了实现插件可视化管理软件的开机自启动，了解&lt;code>Deepin&lt;/code>系统开机自启动的功能实现是至关重要的。&lt;/p>
&lt;p>通过查阅资料与实践，我们了解到&lt;code>Deepin&lt;/code>系统包含自启动文件夹&lt;code>~/.config/autostart&lt;/code>，该文件夹类似于 Windows 下的启动文件夹，系统开机时会执行该文件夹下的每个 desktop 文件 Exec 参数指向的脚本或可执行文件。&lt;/p>
&lt;p>为了确认可行性，小组进行了该方法的验证。首先，通过&lt;code>Deepin&lt;/code>系统自带的修改开机自启动设置的方法，修改开机启动项（图中修改终端的自启动项）：&lt;/p>
&lt;p>&lt;img alt="默认自启动设置方式" src="https://y-aang.github.io/img/final/%E9%BB%98%E8%AE%A4%E8%87%AA%E5%90%AF%E5%8A%A8%E8%AE%BE%E7%BD%AE%E6%96%B9%E5%BC%8F.jpg" />&lt;/p>
&lt;p>随后检查自启动文件夹&lt;code>~/.config/autostart&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>yang@yang-PC:~/.config/autostart$ ls
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>deepin-terminal.desktop org.deepin.browser.desktop
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>发现&lt;code>deepin-terminal.desktop&lt;/code>文件被添加入了该自启动文件夹中，并且会在开机时自启动。&lt;/p>
&lt;p>而取消终端的自启动时，&lt;code>~/.config/autostart&lt;/code>中已有的&lt;code>deepin-terminal.desktop&lt;/code>文件并不会被移除，而是其中的&lt;code>Hidden&lt;/code>的字段会被修改为&lt;code>false&lt;/code>，表示取消开机自启动设置。&lt;/p>
&lt;p>由此，可以通过在插件中检查所有&lt;code>~/.config/autostart&lt;/code>文件夹中&lt;code>.desktop&lt;/code>文件的&lt;code>Hidden&lt;/code>字段来搜索系统所有的自启动软件；也可以通过添加&lt;code>.desktop&lt;/code>文件、修改&lt;code>Hidden&lt;/code>字段的方式进行开机自启动设置的修改。&lt;/p>
&lt;h3 id="5-rpc远程通信问题">5 rpc远程通信问题&lt;/h3>
&lt;ol>
&lt;li>&lt;strong>远程过程调用：&lt;/strong> RPC允许一个程序调用另一个程序在远程机器或进程上执行的过程（函数或方法），就像本地调用一样。这样，开发者可以透明地在分布式系统中调用远程的功能，无需关注底层网络细节。&lt;/li>
&lt;li>&lt;strong>通信协议：&lt;/strong> RPC通信需要定义一个协议，该协议规定了消息的格式、编码方式、序列化和反序列化方法等。通过确定的request和response协议格式制定规定了客户端和服务器之间如何进行数据交换。&lt;/li>
&lt;li>&lt;strong>序列化和反序列化：&lt;/strong> 在RPC通信中，数据需要在网络上传输，因此需要将数据进行序列化（编码）以便在网络上传输，然后在接收端进行反序列化（解码）以还原数据。序列化和反序列化过程确保数据的正确传输和解释。&lt;/li>
&lt;li>&lt;strong>数据传输：&lt;/strong> RPC通信依赖底层的网络传输协议，如TCP或HTTP。客户端和服务器通过网络传输消息，以实现远程过程调用。&lt;/li>
&lt;li>&lt;strong>错误处理：&lt;/strong> RPC通信中需要处理各种可能的错误情况，例如网络连接中断、超时、服务不可用等。合理的错误处理可以保证系统的可靠性和鲁棒性。&lt;/li>
&lt;li>&lt;strong>服务注册与发现：&lt;/strong> 在dde-application-manager中以字符串命名注册了远程调用函数的服务，dde-control-center中关于对应用的变更可以通过rpc远程调用实现就和&lt;/li>
&lt;li>&lt;strong>安全性：&lt;/strong> RPC通信涉及跨网络的数据传输，因此安全性是一个重要考虑因素。加密、认证和授权等机制可以确保通信的安全性。&lt;/li>
&lt;/ol>
&lt;h3 id="6-插件描述缺失问题">6 插件描述缺失问题&lt;/h3>
&lt;p>&lt;img alt="添加描述前" src="https://y-aang.github.io/img/final/%E6%B7%BB%E5%8A%A0%E6%8F%8F%E8%BF%B0%E5%89%8D.png" />&lt;/p>
&lt;p>在加载插件后，我们发现插件缺失了描述信息，与其他的插件格式明显由很大的不同，通过&lt;code>Matrix&lt;/code>上交流发现，是插件缺少了&lt;code>setDescription&lt;/code>的代码，因此，正确加入描述后，我们获得了以下结果。&lt;/p>
&lt;p>&lt;img alt="添加描述后" src="https://y-aang.github.io/img/final/%E6%B7%BB%E5%8A%A0%E6%8F%8F%E8%BF%B0%E5%90%8E.png" />&lt;/p>
&lt;h2 id="附录c-开发计划">附录C 开发计划&lt;/h2>
&lt;h3 id="第一步49418">第一步（4/9～4/18）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 调研&lt;code>Deepin&lt;/code>、&lt;code>dde-dock&lt;/code>、&lt;code>QT&lt;/code>框架等相关内容&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 设计项目方案&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 分工&lt;/li>
&lt;/ul>
&lt;h3 id="第二步41952">第二步（4/19～5/2）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 搭建主体插件类的框架&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 设计启动项管理窗口的前端展示页面&lt;/li>
&lt;/ul>
&lt;h3 id="第三步53513">第三步（5/3～5/13）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 开发部件类接口&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 完善插件类功能&lt;/li>
&lt;/ul>
&lt;h3 id="第四步514521">第四步（5/14～5/21）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 插件类右键功能开发&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 完成配置文件&lt;/li>
&lt;/ul>
&lt;h3 id="第五步522531">第五步（5/22～5/31）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> Debug&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 撰写文档&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>（以上DDE-Dock自启动插件开发均在初赛完成，项目地址见末尾）&lt;/strong>&lt;/p>
&lt;h3 id="第六步62678">第六步（6/26～7/8）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 调研&lt;code>DDE Control Center&lt;/code>框架等相关内容&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 设计前端界面&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 分工&lt;/li>
&lt;/ul>
&lt;h3 id="第七步79715">第七步（7/9～7/15）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 编译教程中的&lt;code>Hello World&lt;/code>控制中心插件&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 设计插件架构&lt;/li>
&lt;/ul>
&lt;h3 id="第八步716722">第八步（7/16～7/22）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 配置环境，编译V20示例插件&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 设计后端接口&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 修改&lt;code>window&lt;/code>、&lt;code>operation&lt;/code>、&lt;code>category&lt;/code>下的文件&lt;/li>
&lt;/ul>
&lt;h3 id="第九步723729">第九步（7/23～7/29）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 配置环境，编译&lt;code>Default-App&lt;/code>与&lt;code>Self Start-up&lt;/code>插件&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> Debug&lt;/li>
&lt;/ul>
&lt;h3 id="第十步730810">第十步（7/30～8/10）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 修改翻译、文字颜色问题&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 撰写文档&lt;/li>
&lt;/ul>
&lt;h2 id="附录d-参考资料">附录D 参考资料&lt;/h2>
&lt;h3 id="文档">文档&lt;/h3>
&lt;p>Qt 插件标准：https://wiki.qt.io/Plugins&lt;/p>
&lt;p>deepin V23 &lt;code>dde-control-center&lt;/code>文档：&lt;a href="https://linuxdeepin.github.io/dde-control-center/index.html">dde-control-center: dde-control-center (linuxdeepin.github.io)&lt;/a>&lt;/p>
&lt;p>&lt;code>dde-control-center&lt;/code>控制中心插件开发示例：&lt;a href="https://docs.deepin.org/info/%E5%BC%80%E5%8F%91%E5%85%A5%E9%97%A8/%E5%9F%BA%E7%A1%80%E7%8E%AF%E5%A2%83/DTK/%E6%A1%88%E4%BE%8B/%E6%A1%88%E4%BE%8B/%E6%8E%A7%E5%88%B6%E4%B8%AD%E5%BF%83%E6%8F%92%E4%BB%B6">控制中心插件 - deepin开发者平台&lt;/a>&lt;/p>
&lt;p>&lt;code>qt-5&lt;/code>手册：https://doc.qt.io/qt-5/&lt;/p>
&lt;p>deepin 应用自启动说明：https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html&lt;/p>
&lt;h3 id="博客">博客&lt;/h3>
&lt;p>关于&lt;code>deepin&lt;/code>开机自启动项的讨论：https://bbs.deepin.org/zh/post/169824、https://blog.csdn.net/qq_21137441/article/details/124825726&lt;/p>
&lt;h3 id="仓库">仓库&lt;/h3>
&lt;p>&lt;code>dde-control-center&lt;/code>仓库：https://github.com/linuxdeepin/dde-control-center.git&lt;/p>
&lt;p>其他开发者的插件项目：https://github.com/linuxdeepin/dde-network-core/tree/master&lt;/p>
&lt;p>说明：由于控制中心的插件对于外观的统一性具有较高的要求，因此，我们仓库中的&lt;code>include/interface&lt;/code>，&lt;code>include/widgets&lt;/code>，&lt;code>src/interface&lt;/code>，&lt;code>src/widgets&lt;/code>，&lt;code>src/frame&lt;/code>下的文件均来自dde-control-center源代码仓库，以保证插件接口的一致性和外观的统一性。&lt;/p></description><author>dde 自启动管理插件开发小组博客</author></item><item><title>DDE v23 正式版移植简要指南</title><link>https://deepin-community.github.io/sig-dde-porting/posts/dde-v23-porting-guide/?utm_source=atom_feed</link><pubDate>Mon, 14 Aug 2023 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/dde-v23-porting-guide/</guid><description>&lt;p>&lt;em>编辑的话请把自己的名字加到作者名单里&lt;/em>&lt;/p>
&lt;p>DDE v23 首个正式版即将随 deepin v23 发布（你阅读到这个文章的时候可能已经发布了）。为了方便各个其它发行版的包维护者可以更方便的移植 DDE 到对应的发行版，这里提供一篇简要的移植指南，用以描述常见的移植问题和解决方案。&lt;/p>
&lt;blockquote>
&lt;p>下面对项目名称的称呼均以 GitHub 对应的原始仓库名为准。
&amp;#123;.note}&lt;/p>&lt;/blockquote>
&lt;h2 id="概览">概览&lt;/h2>
&lt;p>即便本次从版本号字面来看可能并没有较大变动，但事实上，本次相比 beta3 -&amp;gt; rc 而言仍然是存在比较大的变化的。本次中，dde-shell 加载托盘插件的策略做了大幅调整，转变为通过 dde-tray-loader 加载插件，托盘区域的插件也放弃了原有的插件，转而移植并使用了来自原 UOS 20 专业版的托盘插件。此外，为了为后续的应用权限管控做准备，本次也对包括 dde-launchpad、dde-shell 等在内的项目调整了其 &lt;a href="https://github.com/linuxdeepin/deepin-specifications/blob/master/unstable/%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%A7%84%E8%8C%83.md">DSG 配置文件&lt;/a> 所使用的应用 ID（&lt;code>DSG_APP_ID&lt;/code>），故对于移植到其它发行版的情况，若存在相应的 DConfig OEM 配置则也需进行调整。另外，为了解决一些已知的开源合规问题，我们也将原本位于 dtkcore 中的日志部分分离为了一个单独的组件，名为 dtklog。&lt;/p>
&lt;p>由于这些项目的版本间互相影响，我们强烈建议移植人员参照 deepin v23 正式版所使用的包版本进行打包（也务必遵循依赖顺序打包）。下面会对主要的部分进行详细说明。&lt;/p>
&lt;p>需要注意的是，由于此文章编写时间早于版本发布时间，故最终版本镜像中使用的版本可能高于下面列出的版本。我们尽可能确保此文章的准确性，但若您需要获取 ISO 镜像中使用的确切软件版本列表，请挂载 ISO 后参阅 &lt;code>LIVE/FILESYSTEM.MANIFEST&lt;/code> (也可能是 &lt;code>LIVE/FILESYS0.MAN&lt;/code>）路径对应的文件的内容。&lt;/p>
&lt;h2 id="主要组件">主要组件&lt;/h2>
&lt;h3 id="dtk-与-dtk6">DTK 与 DTK6&lt;/h3>
&lt;p>DTK 是 DDE 组件与应用的基础依赖，适用于 RC 的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>dtkcommon&lt;/td>
&lt;td>5.6.32&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtklog&lt;/td>
&lt;td>0.0.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkcore&lt;/td>
&lt;td>5.6.32&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkgui&lt;/td>
&lt;td>5.6.32&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkwidget&lt;/td>
&lt;td>5.6.32&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkdeclarative&lt;/td>
&lt;td>5.6.32&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5integration&lt;/td>
&lt;td>5.6.32&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5platform-plugins&lt;/td>
&lt;td>5.6.32&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6log&lt;/td>
&lt;td>0.0.1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6core&lt;/td>
&lt;td>6.0.18&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6gui&lt;/td>
&lt;td>6.0.18&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6widget&lt;/td>
&lt;td>6.0.18&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6declarative&lt;/td>
&lt;td>6.0.18&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6integration&lt;/td>
&lt;td>6.0.18&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6platform-plugins&lt;/td>
&lt;td>6.0.18&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>除新增的 dtklog 外，本次 DTK 版本号以及相对应的平台插件等版本号均已对齐，可直接参照打包。&lt;/p>
&lt;p>deepin-kwin wayland 功能已经废弃，未来将由 treeland 替代。目前 dwayland 包已经不再使用，依赖此包的应用比如 qt5platform-plugins， 不应该继续编译依赖 dwayland 的功能，可参照 &lt;a href="https://github.com/linuxdeepin/developer-center/issues/7217">linuxdeepin/developer-center#7217&lt;/a> 打对应的 patch 规避。&lt;/p>
&lt;p>dtk6 曾对 Qt 6.2, Qt 6.4 和 Qt 6.6 均进行过适配，我们目前的研发与测试主要使用 Qt 6.6 版本，但当前主干也包含了对 Qt 6.7 的支持。如仍发现有 Qt 版本支持问题可在 DDE 移植群（地址见文末）反馈。&lt;/p>
&lt;p>目前，使用 dtk6 的正式组件有 dde-application-manager，dde-launchpad 与 dde-shell， 需要注意 dde-shell 的托盘组件 dde-tray-loader 仍然需要使用 qt5。&lt;/p>
&lt;h3 id="dde-主要组件">DDE 主要组件&lt;/h3>
&lt;p>下面仅涉及变化较大或影响较广的组件。其余未涉及的组件可正常参照最新 tag 进行打包与移植。&lt;/p>
&lt;p>下面涉及到的组件的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>deepin-osconfig&lt;/td>
&lt;td>2024.08.06&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-app-services&lt;/td>
&lt;td>1.0.25&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-session&lt;/td>
&lt;td>1.2.13&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-manager&lt;/td>
&lt;td>1.2.15&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-tray-loader&lt;/td>
&lt;td>0.0.8&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-shell&lt;/td>
&lt;td>0.0.40&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launchpad&lt;/td>
&lt;td>0.8.4&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-wizard&lt;/td>
&lt;td>0.1.10&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>deepin-wayland-protocols&lt;/td>
&lt;td>1.10.0.28&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>deepin-kwin&lt;/td>
&lt;td>5.27.2.206&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launcher&lt;/td>
&lt;td>被 dde-launchpad 取代，不再使用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-dock&lt;/td>
&lt;td>被 dde-shell 取代，不再使用&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="deepin-osconfig">deepin-osconfig&lt;/h3>
&lt;p>此仓库存放了适用于 deepin 发行版的“OEM”配置。此配置一般 &lt;strong>不需要被其它发行版原样移植&lt;/strong>，但考虑到本次存在针对 DSG 应用 ID 的变化调整，故将其列在此处，仅供移植人员参考对应的修改内容，以便当自己所移植到的目标发行版存在 OEM 配置时做出相应的调整。&lt;/p>
&lt;h3 id="dde-application-manager">dde-application-manager&lt;/h3>
&lt;p>负责启动和管理 DDE 桌面环境存在的应用程序列表的组件。此组件在 rc 阶段无太大变化（但仍总是建议升级）。&lt;/p>
&lt;h3 id="dde-tray-loader">dde-tray-loader&lt;/h3>
&lt;p>这是一个新增的组件，旨在提供 DDE 的任务栏托盘部分的各个托盘插件。此项目提供了 xembed、SNI 托盘的加载支持，以及旧式 dde-dock 托盘插件的加载支持（因为并非直接兼容的旧式插件，故此项目同时也提供了这些插件）。&lt;/p>
&lt;p>此项目需要配合新的 dde-shell 一同使用。&lt;/p>
&lt;h3 id="dde-shell">dde-shell&lt;/h3>
&lt;p>dde-shell 旨在将 DDE 桌面环境插件化与模块化，降低开发难度，使各个组件的替换变得更加容易，并且提供更好的桌面环境集成支持。&lt;/p>
&lt;p>相较于 RC 阶段，此组件的变化主要在于任务栏托盘区域的加载逻辑的大幅调整与相关动画的调整，以及 shell 本体功能上的支撑调优。对于托盘区域，请参见新增项目 dde-tray-loader 的描述。另外，RC 阶段内附的一些第三方库（例如 networkmanager-qt）已被移除，故一般无需刻意留意是否存在 vendor libs 的问题。&lt;/p>
&lt;h3 id="dde-launchpad">dde-launchpad&lt;/h3>
&lt;p>dde-launchpad 相较于 RC 阶段变化并不大，调整大多与缺陷修复以及动画调整有关。需要注意的是，此应用的 DConfig DSG 应用 ID 有变化，故对于移植人员，若原有主动提供针对启动器的 OEM 配置，则需注意修改配置文件放置的对应路径的变化（对应文档也已更新）。&lt;/p>
&lt;h3 id="deepin-kwin">deepin-kwin&lt;/h3>
&lt;p>启动器与 shell 均依赖窗管提供一些支持（例如窗口组件的状态、位于的工作区以及层级关系的控制等），故请同时确保 deepin-kwin 使用的版本。&lt;/p>
&lt;p>需要注意的是，截至编写此文档时，GitHub 中 deepin-kwin 仓库的最新 tag 落后于上述所列的 deepin-kwin 版本，我们正在与相关项目组协商，若您阅读至此时仍无法在 GitHub 获取到对应的版本且 GitHub 所获取的最新版本存在较大问题，则请暂时先从 deepin v23 (beige) 的软件仓库获取对应源码。&lt;/p>
&lt;h3 id="dde-session">dde-session&lt;/h3>
&lt;p>此项目在 RC 阶段无缺陷修复之外的较大变动。&lt;/p>
&lt;p>仍需重申，我们已在 beta3 阶段放弃了对 deepin-kwin wayland 的支持，DDE 后续所有 wayland 相关的支持均由 treeland 提供。&lt;/p>
&lt;h2 id="技术预览组件">技术预览组件&lt;/h2>
&lt;p>为全力确保正式版的版本发布，原本涉及的技术预览组件在 RC 至 release 的这个阶段均无较大进展，故不再于此罗列。&lt;/p>
&lt;h2 id="获取移植帮助">获取移植帮助&lt;/h2>
&lt;p>如果您希望得到移植相关的帮助，请考虑加入我们 DDE 移植小组的在线交流群（下列房间有桥接，任选其一即可），一起展开相关的交流：&lt;/p>
&lt;ul>
&lt;li>Matrix 群：&lt;code>#dde-port:deepin.org&lt;/code>&lt;/li>
&lt;li>Telegram 群：&lt;a href="https://t.me/ddeport">https://t.me/ddeport&lt;/a>&lt;/li>
&lt;/ul></description><author>DDE 移植兴趣小组</author></item><item><title>开发一个 KWin 特效插件</title><link>https://blog.mkacg.com/2023/06/25/How-to-develop-a-kwin-special-effects-plugin/</link><pubDate>Sun, 25 Jun 2023 05:25:55 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2023/06/25/How-to-develop-a-kwin-special-effects-plugin/</guid><description>&lt;p>KWin 是 KDE 开发的窗口管理器，提供了非常丰富的插件，可以对功能进行大量的定制。&lt;/p>&lt;p>本篇文章是对窗口特效插件的开发介绍。&lt;/p>&lt;h2 id="插件开发">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#插件开发" title="插件开发">&lt;/a>插件开发&lt;/h2>&lt;h3 id="插件定义">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#插件定义" title="插件定义">&lt;/a>插件定义&lt;/h3>&lt;p>KWin 的插件通常可以使用一些宏辅助生成代码，例如使用 &lt;code>KPluginFactory&lt;/code> 进行插件的定义，内容是用来生成插件的入口类。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="meta">#&lt;span class="keyword">define&lt;/span> EffectPluginFactory_iid &lt;span class="string">&amp;quot;org.kde.kwin.EffectPluginFactory&amp;quot;&lt;/span> KWIN_PLUGIN_VERSION_STRING&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta">#&lt;span class="keyword">define&lt;/span> KWIN_PLUGIN_FACTORY_NAME KPLUGINFACTORY_PLUGIN_CLASS_INTERNAL_NAME&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta">#&lt;span class="keyword">define&lt;/span> KWIN_EFFECT_FACTORY_SUPPORTED_ENABLED(className, jsonFile, supported, enabled ) \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> class KWIN_PLUGIN_FACTORY_NAME : public KWin::EffectPluginFactory \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> &amp;#123; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> Q_OBJECT \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> Q_PLUGIN_METADATA(IID EffectPluginFactory_iid FILE jsonFile) \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> Q_INTERFACES(KPluginFactory) \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> public: \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> explicit KWIN_PLUGIN_FACTORY_NAME() &amp;#123;&amp;#125; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> ~KWIN_PLUGIN_FACTORY_NAME() &amp;#123;&amp;#125; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> bool isSupported() const override &amp;#123; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> supported \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> &amp;#125; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> bool enabledByDefault() const override &amp;#123; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> enabled \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> &amp;#125; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> KWin::Effect *createEffect() const override &amp;#123; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> return new className(); \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> &amp;#125; \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> &amp;#125;;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta">#&lt;span class="keyword">define&lt;/span> KWIN_EFFECT_FACTORY_ENABLED(className, jsonFile, enabled ) \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> KWIN_EFFECT_FACTORY_SUPPORTED_ENABLED(className, jsonFile, return true;, enabled )&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta">#&lt;span class="keyword">define&lt;/span> KWIN_EFFECT_FACTORY_SUPPORTED(className, jsonFile, supported ) \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> KWIN_EFFECT_FACTORY_SUPPORTED_ENABLED(className, jsonFile, supported, return true; )&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta">#&lt;span class="keyword">define&lt;/span> KWIN_EFFECT_FACTORY(className, jsonFile ) \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta"> KWIN_EFFECT_FACTORY_SUPPORTED_ENABLED(className, jsonFile, return true;, return true; )&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>大部分宏只是为了方便结构修改，我们只需要使用 &lt;code>K_PLUGIN_FACTORY&lt;/code> 进行插件定义即可。&lt;/p>&lt;p>假设我们开发了一个插件，名字叫 demo，我们只需要在 main.cpp 中使用 &lt;code>KWIN_EFFECT_FACTORY_SUPPORTED&lt;/code> 定义&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="built_in">KWIN_EFFECT_FACTORY_SUPPORTED&lt;/span>(&lt;/span>&lt;br />&lt;span class="line"> Demo,&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;metadata.json&amp;quot;&lt;/span>,&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">)&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>代码展开后是这样的。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="keyword">class&lt;/span> &lt;span class="title class_">KPLUGINFACTORY_PLUGIN_CLASS_INTERNAL_NAME&lt;/span> : &lt;span class="keyword">public&lt;/span> KWin::EffectPluginFactory \&lt;/span>&lt;br />&lt;span class="line">&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="function">Q_OBJECT&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function"> &lt;span class="title">Q_PLUGIN_METADATA&lt;/span>&lt;span class="params">(IID EffectPluginFactory_iid FILE &lt;span class="string">&amp;quot;metadata.json&amp;quot;&lt;/span>)&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function"> &lt;span class="title">Q_INTERFACES&lt;/span>&lt;span class="params">(KPluginFactory)&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">&lt;span class="keyword">public&lt;/span>: \&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function"> explicit KPLUGINFACTORY_PLUGIN_CLASS_INTERNAL_NAME() &amp;#123;&lt;/span>&amp;#125;&lt;/span>&lt;br />&lt;span class="line"> ~&lt;span class="built_in">KPLUGINFACTORY_PLUGIN_CLASS_INTERNAL_NAME&lt;/span>() &amp;#123;&amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="function">&lt;span class="type">bool&lt;/span> &lt;span class="title">isSupported&lt;/span>&lt;span class="params">()&lt;/span> &lt;span class="type">const&lt;/span> &lt;span class="keyword">override&lt;/span> &lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="function">&lt;span class="type">bool&lt;/span> &lt;span class="title">enabledByDefault&lt;/span>&lt;span class="params">()&lt;/span> &lt;span class="type">const&lt;/span> &lt;span class="keyword">override&lt;/span> &lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="literal">true&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="function">KWin::Effect *&lt;span class="title">createEffect&lt;/span>&lt;span class="params">()&lt;/span> &lt;span class="type">const&lt;/span> &lt;span class="keyword">override&lt;/span> &lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">Demo&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>可以看到，其实 &lt;code>KWIN_EFFECT_FACTORY_SUPPORTED&lt;/code> 只是为我们生成了工厂函数，辅助生成了一些必要的重载。&lt;/p>&lt;p>&lt;code>metadata.json&lt;/code> 文件是用来作为插件的描述信息使用的。&lt;/p>&lt;figure class="highlight json">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;KPlugin&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;Category&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;Accessibility&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;Description&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;Allow clip of window content&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;EnabledByDefault&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="literal">&lt;span class="keyword">true&lt;/span>&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;Id&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;scissor&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;License&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;GPL&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;Name&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;ScissorWindow&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;Name[zh_CN]&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;窗口圆角&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">&amp;#125;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;org.kde.kwin.effect&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">&amp;quot;enabledByDefaultMethod&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="literal">&lt;span class="keyword">true&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="punctuation">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="punctuation">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h3 id="特效插件">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#特效插件" title="特效插件">&lt;/a>特效插件&lt;/h3>&lt;p>特效插件是一类可以改变窗口画面的插件，例如我们可以在插件里对窗口进行贴图、变形和裁切，在 DDE 中，就使用特效插件完成了圆角裁切和窗口模糊。&lt;/p>&lt;p>这里使用圆角裁切插件作为例子，首先使用 &lt;code>KWIN_EFFECT_FACTORY_SUPPORTED&lt;/code> 宏对插件进行定义， &lt;code>KWIN_EFFECT_FACTORY_SUPPORTED&lt;/code> 接受一个 class 作为返回的接口类，它需要继承自 &lt;code>Effect&lt;/code>，第二个参数是元信息的 json 文件，第三个参数是返回是否支持，在启用插件时可对当前环境进行判断，例如插件需要使用 &lt;code>OpenGL&lt;/code> 对图形进行一些操作，但是当前环境不支持 &lt;code>OpenGL&lt;/code>，那么插件就不会启用。&lt;/p>&lt;figure class="highlight json">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">#include &lt;span class="string">&amp;quot;scissorwindow.h&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">namespace KWin&lt;/span>&lt;br />&lt;span class="line">&lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">KWIN_EFFECT_FACTORY_SUPPORTED(ScissorWindow&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="string">&amp;quot;metadata.json.stripped&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> return ScissorWindow&lt;span class="punctuation">:&lt;/span>&lt;span class="punctuation">:&lt;/span>supported();)&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="punctuation">&amp;#125;&lt;/span> &lt;span class="comment">// namespace KWin&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">#include &lt;span class="string">&amp;quot;main.moc&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>在 Effect 类中有几个不同阶段的方法可以重载。&lt;/p>&lt;ul>&lt;li>prePaintScreen&lt;ul>&lt;li>设置是否变换窗口或整个屏幕&lt;/li>&lt;li>更改将要绘制的屏幕区域&lt;/li>&lt;li>做各种内务处理任务，比如初始化你的效果变量&lt;br /> 用于即将到来的绘画过程或更新动画的进度&lt;/li>&lt;/ul>&lt;/li>&lt;li>paintScreen&lt;ul>&lt;li>在窗口上画东西（调用后画 effect-&amp;gt;paintScreen())&lt;/li>&lt;li>绘制多个桌面和&amp;#x2f;或同一桌面的多个副本&lt;/li>&lt;/ul>&lt;/li>&lt;li>postPaintScreen&lt;ul>&lt;li>在动画的情况下安排下一次重绘，不应该在这里画任何东西。&lt;/li>&lt;/ul>&lt;/li>&lt;li>prePaintWindow&lt;ul>&lt;li>启用或禁用窗口的绘制（例如启用最小化窗口的绘制）&lt;/li>&lt;li>将窗口设置为半透明&lt;/li>&lt;li>设置要转换的窗口&lt;/li>&lt;li>请求将窗口分成多个部分&lt;/li>&lt;/ul>&lt;/li>&lt;li>paintWindow&lt;ul>&lt;li>做各种转换&lt;/li>&lt;li>改变窗口的不透明度&lt;/li>&lt;li>改变亮度和&amp;#x2f;或饱和度，如果支持的话&lt;/li>&lt;/ul>&lt;/li>&lt;li>postPaintWindow&lt;ul>&lt;li>在动画的情况下为单个窗口安排下一次重绘&lt;br /> 不应该在这里画任何东西。&lt;/li>&lt;/ul>&lt;/li>&lt;li>paintEffectFrame&lt;ul>&lt;li>在绘制 EffectFrame 之前直接调用此方法。&lt;/li>&lt;li>如果需要绑定shader或者执行，可以实现这个方法帧渲染前的其他操作。&lt;/li>&lt;/ul>&lt;/li>&lt;li>drawWindow&lt;ul>&lt;li>可以调用以绘制一个窗口的多个副本（例如缩略图）。&lt;/li>&lt;li>可以在这里改变窗口的不透明度&amp;#x2f;亮度&amp;#x2f;等，但不能做任何转换。&lt;/li>&lt;li>在基于 OpenGL 的合成中，框架确保上下文是最新的&lt;/li>&lt;/ul>&lt;/li>&lt;/ul>&lt;p>在方法名称中可以看出，在场景及窗口绘制的过程中，分别可以在实际绘制的前后分别执行一些动作，圆角插件就是在 &lt;code>drawWindow&lt;/code> 函数中，使用 &lt;code>OpenGL&lt;/code> 对窗口使用着色器进行窗口裁切，并绘制到屏幕上。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="function">&lt;span class="type">void&lt;/span> &lt;span class="title">ScissorWindow::drawWindow&lt;/span>&lt;span class="params">(EffectWindow *w, &lt;span class="type">int&lt;/span> mask, &lt;span class="type">const&lt;/span> QRegion&amp;amp; region, WindowPaintData &amp;amp;data)&lt;/span> &lt;/span>&amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (w-&amp;gt;&lt;span class="built_in">isDesktop&lt;/span>() || &lt;span class="built_in">isMaximized&lt;/span>(w)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> effects-&amp;gt;&lt;span class="built_in">drawWindow&lt;/span>(w, mask, region, data);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> QPointF cornerRadius;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> QVariant valueRadius = w-&amp;gt;&lt;span class="built_in">data&lt;/span>(WindowRadiusRole);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (valueRadius.&lt;span class="built_in">isValid&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> cornerRadius = w-&amp;gt;&lt;span class="built_in">data&lt;/span>(WindowRadiusRole).&lt;span class="built_in">toPointF&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> qreal xMin&amp;#123; std::&lt;span class="built_in">min&lt;/span>(cornerRadius.&lt;span class="built_in">x&lt;/span>(), w-&amp;gt;&lt;span class="built_in">width&lt;/span>() / &lt;span class="number">2.0&lt;/span>) &amp;#125;;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> qreal yMin&amp;#123; std::&lt;span class="built_in">min&lt;/span>(cornerRadius.&lt;span class="built_in">y&lt;/span>(), w-&amp;gt;&lt;span class="built_in">height&lt;/span>() / &lt;span class="number">2.0&lt;/span>) &amp;#125;;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> qreal minRadius&amp;#123; std::&lt;span class="built_in">min&lt;/span>(xMin, yMin) &amp;#125;;&lt;/span>&lt;br />&lt;span class="line"> cornerRadius = &lt;span class="built_in">QPointF&lt;/span>(minRadius, minRadius);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (cornerRadius.&lt;span class="built_in">x&lt;/span>() &amp;lt; &lt;span class="number">2&lt;/span> &amp;amp;&amp;amp; cornerRadius.&lt;span class="built_in">y&lt;/span>() &amp;lt; &lt;span class="number">2&lt;/span>) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> effects-&amp;gt;&lt;span class="built_in">drawWindow&lt;/span>(w, mask, region, data);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="type">const&lt;/span> QString&amp;amp; key = &lt;span class="built_in">QString&lt;/span>(&lt;span class="string">&amp;quot;%1+%2&amp;quot;&lt;/span>).&lt;span class="built_in">arg&lt;/span>(cornerRadius.&lt;span class="built_in">toPoint&lt;/span>().&lt;span class="built_in">x&lt;/span>()).&lt;span class="built_in">arg&lt;/span>(cornerRadius.&lt;span class="built_in">toPoint&lt;/span>().&lt;span class="built_in">y&lt;/span>()&lt;/span>&lt;br />&lt;span class="line"> );&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (!m_texMaskMap.&lt;span class="built_in">count&lt;/span>(key)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">QImage &lt;span class="title">img&lt;/span>&lt;span class="params">(QSize(radius.x() * &lt;span class="number">2&lt;/span>, radius.y() * &lt;span class="number">2&lt;/span>), QImage::Format_RGBA8888)&lt;/span>&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> img.&lt;span class="built_in">fill&lt;/span>(&lt;span class="built_in">QColor&lt;/span>(&lt;span class="number">0&lt;/span>, &lt;span class="number">0&lt;/span>, &lt;span class="number">0&lt;/span>, &lt;span class="number">0&lt;/span>));&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="function">QPainter &lt;span class="title">painter&lt;/span>&lt;span class="params">(&amp;amp;img)&lt;/span>&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> painter.&lt;span class="built_in">setPen&lt;/span>(Qt::NoPen);&lt;/span>&lt;br />&lt;span class="line"> painter.&lt;span class="built_in">setBrush&lt;/span>(&lt;span class="built_in">QColor&lt;/span>(&lt;span class="number">255&lt;/span>, &lt;span class="number">255&lt;/span>, &lt;span class="number">255&lt;/span>, &lt;span class="number">255&lt;/span>));&lt;/span>&lt;br />&lt;span class="line"> painter.&lt;span class="built_in">setRenderHint&lt;/span>(QPainter::Antialiasing);&lt;/span>&lt;br />&lt;span class="line"> painter.&lt;span class="built_in">drawEllipse&lt;/span>(&lt;span class="number">0&lt;/span>, &lt;span class="number">0&lt;/span>, radius.&lt;span class="built_in">x&lt;/span>() * &lt;span class="number">2&lt;/span>, radius.&lt;span class="built_in">y&lt;/span>() * &lt;span class="number">2&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> painter.&lt;span class="built_in">end&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> m_texMaskMap[key] = &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">GLTexture&lt;/span>(img.&lt;span class="built_in">copy&lt;/span>(&lt;span class="number">0&lt;/span>, &lt;span class="number">0&lt;/span>, radius.&lt;span class="built_in">x&lt;/span>(), radius.&lt;span class="built_in">y&lt;/span>()));&lt;/span>&lt;br />&lt;span class="line"> m_texMaskMap[key]-&amp;gt;&lt;span class="built_in">setFilter&lt;/span>(GL_LINEAR);&lt;/span>&lt;br />&lt;span class="line"> m_texMaskMap[key]-&amp;gt;&lt;span class="built_in">setWrapMode&lt;/span>(GL_CLAMP_TO_EDGE);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> ShaderManager::&lt;span class="built_in">instance&lt;/span>()-&amp;gt;&lt;span class="built_in">pushShader&lt;/span>(m_filletOptimizeShader);&lt;/span>&lt;br />&lt;span class="line"> m_filletOptimizeShader-&amp;gt;&lt;span class="built_in">setUniform&lt;/span>(&lt;span class="string">&amp;quot;typ1&amp;quot;&lt;/span>, &lt;span class="number">1&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> m_filletOptimizeShader-&amp;gt;&lt;span class="built_in">setUniform&lt;/span>(&lt;span class="string">&amp;quot;sampler&amp;quot;&lt;/span>, &lt;span class="number">0&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> m_filletOptimizeShader-&amp;gt;&lt;span class="built_in">setUniform&lt;/span>(&lt;span class="string">&amp;quot;msk1&amp;quot;&lt;/span>, &lt;span class="number">1&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> m_filletOptimizeShader-&amp;gt;&lt;span class="built_in">setUniform&lt;/span>(&lt;span class="string">&amp;quot;k&amp;quot;&lt;/span>, &lt;span class="built_in">QVector2D&lt;/span>(w-&amp;gt;&lt;span class="built_in">width&lt;/span>() / cornerRadius.&lt;span class="built_in">x&lt;/span>(), w-&amp;gt;&lt;span class="built_in">height&lt;/span>() / cornerRadius.&lt;span class="built_in">y&lt;/span>()));&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (w-&amp;gt;&lt;span class="built_in">hasDecoration&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> m_filletOptimizeShader-&amp;gt;&lt;span class="built_in">setUniform&lt;/span>(&lt;span class="string">&amp;quot;typ2&amp;quot;&lt;/span>, &lt;span class="number">0&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125; &lt;span class="keyword">else&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> m_filletOptimizeShader-&amp;gt;&lt;span class="built_in">setUniform&lt;/span>(&lt;span class="string">&amp;quot;typ2&amp;quot;&lt;/span>, &lt;span class="number">1&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">auto&lt;/span> old_shader = data.shader;&lt;/span>&lt;br />&lt;span class="line"> data.shader = m_filletOptimizeShader;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">glActiveTexture&lt;/span>(GL_TEXTURE1);&lt;/span>&lt;br />&lt;span class="line"> m_texMaskMap[key]-&amp;gt;&lt;span class="built_in">bind&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">glActiveTexture&lt;/span>(GL_TEXTURE0);&lt;/span>&lt;br />&lt;span class="line"> effects-&amp;gt;&lt;span class="built_in">drawWindow&lt;/span>(w, mask, region, data);&lt;/span>&lt;br />&lt;span class="line"> ShaderManager::&lt;span class="built_in">instance&lt;/span>()-&amp;gt;&lt;span class="built_in">popShader&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> data.shader = old_shader;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">glActiveTexture&lt;/span>(GL_TEXTURE1);&lt;/span>&lt;br />&lt;span class="line"> m_texMaskMap[key]-&amp;gt;&lt;span class="built_in">unbind&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">glActiveTexture&lt;/span>(GL_TEXTURE0);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>如果窗口是桌面类型，或者已经最大化了，则无需处理，直接返回 Effect 原本的处理函数。&lt;/p>&lt;p>之后尝试从窗口属性中取出圆角大小的值，如果没有设置圆角大小，或者值小于2，则无需处理。&lt;/p>&lt;p>尝试查询缓存，在这里为窗口的四个角构建一份遮罩对象并缓存，使用 &lt;code>OpenGL&lt;/code> 将遮罩和着色器进行关联，激活两个材质分别绘制窗口内容和四个角的遮罩，在着色器中完成窗口圆角的半透明效果。&lt;/p>&lt;p>限于篇幅，本文不展开介绍如何实现圆角插件的全部实现过程，仅挑选关键步骤。&lt;/p>&lt;h2 id="安装">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#安装" title="安装">&lt;/a>安装&lt;/h2>&lt;p>将动态库复制到 &lt;code>/usr/share/kwin/effects/plugins&lt;/code> ，并使用 &lt;code>DBus&lt;/code> 激活插件。&lt;/p>&lt;figure class="highlight bash">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">qdbus --literal org.kde.KWin /Effects org.kde.kwin.Effects.loadEffect scissor&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure></description><author>justforlxz</author></item><item><title>V23 Beta DDE Wayland解读</title><link>https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/</link><pubDate>Thu, 01 Jun 2023 05:37:41 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/</guid><description>&lt;p>在 V23 beta 版本中，DDE 试验性的开启了 Wayland 的支持，允许用户在 Wayland 协议下的桌面工作环境启动 。本篇文章会向大家介绍一下 Wayland 是什么，我们尝试做了什么改变，以及 DDE Wayland 未来会支持哪些新特性。（注：单独提出 Wayland， 通常和 Wayland 合成器、Wayland 服务器、显示服务器被视为同一个内容；X Window System 和 X11 也被视为同一个内容。）&lt;/p>
&lt;h2 id="什么是-wayland">什么是 Wayland？
&lt;span>
&lt;a href="https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/#%e4%bb%80%e4%b9%88%e6%98%af-wayland">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>Wayland 是一个通信协议，规定了显示服务器与客户端之间的通信方式，而使用这个协议的显示服务器称为 Wayland Compositor。Wayland 只专注于图形，并希望使用其他库与输入硬件进行通信，以降低自身的复杂度。Wayland 最大的好处也是大家都推崇的原因，那就是 Wayland 在设计上会考虑安全，例如默认不允许窗口获取其他窗口的数据，合成器和窗口管理器的合并也降低了对系统资源的消耗。&lt;/p>
&lt;h3 id="wayland-与-x-window-system-有什么不同">Wayland 与 X Window System 有什么不同？
&lt;span>
&lt;a href="https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/#wayland-%e4%b8%8e-x-window-system-%e6%9c%89%e4%bb%80%e4%b9%88%e4%b8%8d%e5%90%8c">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>Wayland 与 X Window System 的最大不同在于，Wayland 与 X Window System 的最大不同在于，它的窗口管理器和 Wayland Server 在同一个进程，并且客户端能够通过 EGL 以及一些 Wayland 特定的 EGL 扩充组件直接在显示内存中绘制自己的缓冲区。 窗口管理器简化成显示管理服务，专门负责绘制那些屏幕上的程序。 这比 X Window System 中的窗口管理器要更简单、高效。&lt;/p>
&lt;h3 id="wayland-协议有哪些组成">Wayland 协议有哪些组成？
&lt;span>
&lt;a href="https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/#wayland-%e5%8d%8f%e8%ae%ae%e6%9c%89%e5%93%aa%e4%ba%9b%e7%bb%84%e6%88%90">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;h4 id="1协议概述">1.协议概述
&lt;span>
&lt;a href="https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/#1%e5%8d%8f%e8%ae%ae%e6%a6%82%e8%bf%b0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>Wayland 协议被描述为异步面向对象协议。协议是异步的，这意味着不必等待回复或者响应 ACK，避免了往返时间并提高性能。协议封装为面向对象的设计，则是面向对象的设计方式，能很好的对服务器上不同窗口数据及接口进行封装。&lt;/p>
&lt;p>Wayland 合成器可以定义和公开自己的附加接口，被称为扩展协议，不同的 Wayland 会提供功能完全不同，甚至功能相反的协议，这带来了很大灵活性，但使用客户端时需要自行判断。&lt;/p>
&lt;h4 id="2协议架构">2.协议架构
&lt;span>
&lt;a href="https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/#2%e5%8d%8f%e8%ae%ae%e6%9e%b6%e6%9e%84">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>Wayland 协议是一种“客户端 —— 服务器”模型，客户端是请求在屏幕上显示画面的图形应用程序，服务器是控制应用程序显示在屏幕上的管理程序。Wayland 参考实现被设计成两层协议，既：&lt;/p>
&lt;ul>
&lt;li>下层协议：处理客户端和服务器之间的进程间通信，以及在内部的数据封装处理。&lt;/li>
&lt;li>上层协议：处理客户端和服务器交换的数据，以实现窗口系统的基本功能，这一层被实现为异步面向对象协议。
下层协议是使用 C 语言开发的，而上层协议是根据 XML 格式的协议描述文件自动生成，每当 XML 协议的描述发生变化时，就可以重新生成该协议的源代码，这使得协议非常灵活、可扩展性好且防止出错。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>如下 Wayland 工作原理图：&lt;/strong>&lt;/p>
&lt;p>&lt;img alt="wayland.png" src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Wayland_display_server_protocol.svg/700px-Wayland_display_server_protocol.svg.png" />&lt;/p>
&lt;p>&lt;strong>以下对应图中所标编号作说明：&lt;/strong>&lt;/p>
&lt;p>&lt;strong>①&lt;/strong>Linux 内核中的 evdev 模块接收事件并将它们发送到 Wayland 合成器。&lt;/p>
&lt;p>&lt;strong>②&lt;/strong>Wayland 合成器查看场景图并确定哪个窗口应接收事件。场景图对应于屏幕上显示的内容，Wayland 合成器显示对应用事件的场景图中元素的转换。因此，Wayland 合成器可以反向变换以找到正确的窗口，并将屏幕上的坐标转换为窗口中的坐标。&lt;/p>
&lt;p>&lt;strong>③&lt;/strong>当客户端收到事件时，Wayland 的客户端只需通过 EGL 渲染并向合成器发送请求以通知更新的范围即可。&lt;/p>
&lt;p>&lt;strong>④&lt;/strong>Wayland 合成器 从客户端收集更改请求并重新配置屏幕。 然后，合成器直接发出 ioctl 让 KMS 重绘屏幕。&lt;/p>
&lt;p>当了解了 Wayland 相关基本介绍之后，基于它我们在 DDE 上将会作哪些适配功过呢？将从以下几个方面说一下我们在 Wayland 技术预览版里面所做的适配工作。&lt;/p>
&lt;h2 id="dde-适配-wayland-都做了哪些工作">DDE 适配 Wayland 都做了哪些工作
&lt;span>
&lt;a href="https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/#dde-%e9%80%82%e9%85%8d-wayland-%e9%83%bd%e5%81%9a%e4%ba%86%e5%93%aa%e4%ba%9b%e5%b7%a5%e4%bd%9c">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>DDE 原本设计为在 X11 协议下工作，很多组件直接或间接依赖 X11 的接口，多数组件依赖的功能并没有在 XWayland 中提供，所以就需要进行一些修改。&lt;/p>
&lt;p>首先在 Qt 插件中实现和 Wayland 特定性相关的功能，Qt 提供了一个 Wayland Shell Integration 的插件，允许我们在这里调用 DDE Wayland 合成器提供的扩展协议。&lt;/p>
&lt;p>Qt 已提供核心协议的适配，所以 DDE 只需要在现有框架下实现扩展协议即可。&lt;/p>
&lt;p>目前 DDE 提供的扩展协议有以下几个方面：&lt;/p>
&lt;ul>
&lt;li>设置圆角窗口&lt;/li>
&lt;li>请求获取窗口数据（截图权限）&lt;/li>
&lt;li>划分工作区可用区域
DTK 程序可以通过设置窗口的属性，或者使用 DTK 提供的平台接口，即可调用扩展的 Wayland 协议，非 DTK 程序则需要手动使用扩展协议的 XML 文件进行代码生成调用。&lt;/li>
&lt;/ul>
&lt;h2 id="dde-wayland-未来会支持什么特性">DDE Wayland 未来会支持什么特性？
&lt;span>
&lt;a href="https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/#dde-wayland-%e6%9c%aa%e6%9d%a5%e4%bc%9a%e6%94%af%e6%8c%81%e4%bb%80%e4%b9%88%e7%89%b9%e6%80%a7">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="1hdr-支持">1.HDR 支持
&lt;span>
&lt;a href="https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/#1hdr-%e6%94%af%e6%8c%81">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>高动态范围（HDR）是比平时更高的动态范围。HDR 的内容过于深入，在这里只简单的进行说明。&lt;/p>
&lt;p>HDR 可以保存更多的内容信息，在支持 HDR 的屏幕上观看 HDR 的内容，可以获得更好的体验，DDE 目前正在准备支持 HDR 内容的输出，这会让 DDE 拥有更好的显示效果。&lt;/p>
&lt;h3 id="2成体系的窗口动画">2.成体系的窗口动画
&lt;span>
&lt;a href="https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/#2%e6%88%90%e4%bd%93%e7%b3%bb%e7%9a%84%e7%aa%97%e5%8f%a3%e5%8a%a8%e7%94%bb">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>在 X11 下，由于窗口管理和画面合成管理是两个进程，并且在启动速度上存在差异，所以只能采用一些“巧妙”的设计来规避视觉错误。&lt;/p>
&lt;p>但是在 Wayland 下，窗口管理器和窗口合成器被合并成一个进程，那么启动后就可以立即使用动画效果，例如可以设计视觉效果更好的登录动画。&lt;/p>
&lt;p>目前 DDE 的窗口动画支持并不多，且大部分是单调的线性动画，有些情况还需要客户端自己实现虚假动画，例如任务栏发生位置变化时，桌面的图标会进行计算，并自行改变大小，这引入了非必要的依赖。如果使用窗口动画，桌面并不需要关心任何外部因素，只需要设置自己在可用工作区域最大化，当任务栏发生位置改变时，合成器会自动调整桌面的大小，并产生相应的窗口动画。&lt;/p>
&lt;p>未来 DDE 会使用更多窗口动画来减少组件之间的依赖，以及实现更多更好的视觉效果。&lt;/p>
&lt;h2 id="最后总结">最后总结
&lt;span>
&lt;a href="https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/#%e6%9c%80%e5%90%8e%e6%80%bb%e7%bb%93">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>阅读至此，是不是对 Wayland 的概念及工作原理更加清晰啦？如果您有什么疑问也欢迎与我们互动探讨 Wayland。&lt;/p>
&lt;p>坦白说，DDE 的 Wayland 支持还处于初步阶段（&lt;strong>技术预览版，请谨慎使用&lt;/strong>），未来我们会使用更多合成器提供的功能，来为桌面环境降低开发难度，提升性能。提供更好的体验。&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>DDE-Dock 自启动管理插件 项目文档</title><link>https://y-aang.github.io/posts/%E9%A1%B9%E7%9B%AE%E6%96%87%E6%A1%A3dde-dock/?utm_source=atom_feed</link><pubDate>Tue, 30 May 2023 06:00:00 +0000</pubDate><guid isPermaLink="true">https://y-aang.github.io/posts/%E9%A1%B9%E7%9B%AE%E6%96%87%E6%A1%A3dde-dock/</guid><description>&lt;p>队员：复旦大学 朱元依、沈扬、朱俊杰 &lt;br />
指导老师：张亮、陈辰 &lt;br />
企业导师：王子冲&lt;/p>
&lt;p>本项目为2023年操作系统大赛企业赛道赛题。&lt;/p>
&lt;p>项目链接：&lt;a href="https://github.com/PinappleUnderTheSea/os223.git">DDE 自启动管理插件 github仓库&lt;/a>&lt;/p>
&lt;h2 id="一目标描述">一、目标描述&lt;/h2>
&lt;p>我们小组的选题为&lt;a href="https://github.com/oscomp/proj223-control-center-startup-management-plugin">proj223-control-center-startup-management-plugin&lt;/a>&lt;/p>
&lt;p>DDE 深度桌面环境的控制中心提供了插件功能，以便第三方开发者可以扩展其功能并额外的功能组件添加到控制中心之中。我们将在本项目中为 DDE 桌面环境的控制中心编写一个自启动管理插件。&lt;/p>
&lt;h2 id="二比赛题目分析和相关资料调研">二、比赛题目分析和相关资料调研&lt;/h2>
&lt;p>为了明确题目设置的原因与需求，我们请教了了企业导师，得知：目前用户对自启动权限的管理目前只能通过 dde-launcher （启动器/“开始菜单”）的右键菜单进行管理；考虑到此功能本身也并不复杂，所以设置了此课题，旨在解决这样的问题。&lt;/p>
&lt;p>而对于此开发任务的具体实施，主要需要对&lt;code>dde-dock&lt;/code>插件运行的原理和&lt;code>deepin&lt;/code>开机自启动的设置方式进行调研，以便进行开发。调研的具体内容包括：&lt;/p>
&lt;p>了解了Qt 插件标准（&lt;a href="https://wiki.qt.io/Plugins">Qt 插件标准&lt;/a>）&lt;/p>
&lt;p>阅读整理了&lt;code>dde-dock&lt;/code>官方仓库中的插件工作原理（&lt;a href="https://github.com/linuxdeepin/dde-dock/blob/master/plugins/plugin-guide/plugins-developer-guide.md">插件工作原理&lt;/a>）&lt;/p>
&lt;p>学习其他开发者的插件项目（&lt;a href="https://github.com/sonichy/CMDU_DDE_DOCK">CMDU_DDE_DOCK&lt;/a>）&lt;/p>
&lt;p>阅读整理了各个论坛中关于&lt;code>deepin&lt;/code>开机自启动项的讨论（&lt;a href="https://bbs.deepin.org/zh/post/169824">169824&lt;/a>、&lt;a href="https://blog.csdn.net/qq_21137441/article/details/124825726">124825726&lt;/a>）&lt;/p>
&lt;h2 id="三系统框架设计">三、系统框架设计&lt;/h2>
&lt;h3 id="1项目整体结构设计">1、项目整体结构设计&lt;/h3>
&lt;p>符合&lt;code>dde-dock&lt;/code>提供的接口与插件开发规范，本项目将分为插件类（&lt;code>SelfStartPlugin&lt;/code>）、部件类（&lt;code>MainWidget&lt;/code>）和自启动管理窗口（&lt;code>AppletWidget&lt;/code>）分别进行功能的实现。其中，项目的结构和各对象所包含的数据结构与方法如下图所示：&lt;/p>
&lt;p>&lt;img alt="类图" src="https://y-aang.github.io/img/%E7%B1%BB%E5%9B%BE.jpg" />&lt;/p>
&lt;h3 id="2类功能说明">2、类功能说明&lt;/h3>
&lt;p>插件类&lt;code>SelfStartPlugin&lt;/code>负责实现插件与&lt;code>dde-dock&lt;/code>交互所必须的接口；部件类&lt;code>MainWidget&lt;/code>负责在&lt;code>dde-dock&lt;/code>中展示该插件的图标；自启动管理窗口&lt;code>AppletWidget&lt;/code>负责实现核心功能：读取&lt;code>Deepin&lt;/code>系统的开机自启动项、同时对各个软件的开机自启动进行管理（包括添加、删除、启用、禁用）。&lt;/p>
&lt;h3 id="3实现描述">3、实现描述&lt;/h3>
&lt;h4 id="1插件类">（1）插件类&lt;/h4>
&lt;p>在插件类中，主要实现了&lt;code>dde-dock&lt;/code>中 &lt;code>PluginItemInterface&lt;/code>相关的接口，便于系统加载并实现插件的功能。接口包括以下内容：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>名称&lt;/th>
&lt;th style="text-align:left">功能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>SelfStarupPlugin&lt;/td>
&lt;td style="text-align:left">类的初始化函数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>pluginName&lt;/td>
&lt;td style="text-align:left">返回插件名称，用于在 dde-dock 内部管理插件时使用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>init&lt;/td>
&lt;td style="text-align:left">插件初始化入口函数，参数 proxyInter 可认为是主程序的进程&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>pluginDisplayName&lt;/td>
&lt;td style="text-align:left">返回插件名称，用于在界面上显示&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>itemWidget&lt;/td>
&lt;td style="text-align:left">返回插件主控件，用于显示在 dde-dock 面板上&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>itemPopupApplet&lt;/td>
&lt;td style="text-align:left">返回鼠标左键点击插件主控件后弹出的控件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>flags&lt;/td>
&lt;td style="text-align:left">用于返回插件的属性，例如插件显示的位置，插件占几列，插件是否支持拖动等&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>pluginIsAllowDisable&lt;/td>
&lt;td style="text-align:left">返回插件是否允许被禁用（默认不允许被禁用）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>pluginIsDisable&lt;/td>
&lt;td style="text-align:left">返回插件当前是否处于被禁用状态&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>pluginStateSwitched&lt;/td>
&lt;td style="text-align:left">当插件的禁用状态被用户改变时此接口被调用&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>itemContextMenu&lt;/td>
&lt;td style="text-align:left">返回鼠标左键点击插件主控件后要执行的命令数据&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>invokedMenuItem&lt;/td>
&lt;td style="text-align:left">返回鼠标右键点击插件主控件后要显示的菜单数据&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="2部件类">（2）部件类&lt;/h4>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>名称&lt;/th>
&lt;th>功能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>MainWidget&lt;/td>
&lt;td>&lt;code>MainWidget&lt;/code>部件的初始化函数，设置&lt;code>dde-dock&lt;/code>图标的基础样式与文字内容&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~MainWidget&lt;/td>
&lt;td>&lt;code>MainWidget&lt;/code>部件的析构函数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>sizeHint&lt;/td>
&lt;td>设置图标大小的函数&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h5 id="插件图标">插件图标&lt;/h5>
&lt;p>&lt;img alt="图标" src="https://y-aang.github.io/img/%E5%9B%BE%E6%A0%87.png" />&lt;/p>
&lt;p>最左侧的&lt;code>SELF_STARTUP&lt;/code>图标为该插件图标。&lt;/p>
&lt;h4 id="3自启动管理窗口">（3）自启动管理窗口&lt;/h4>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>名称&lt;/th>
&lt;th>功能&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>AppletWidget&lt;/td>
&lt;td>&lt;code>AppletWidget&lt;/code>的初始化函数，负责绘制与用户交互的开机自启动项展示表格与修改设置选项&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>~AppletWidget&lt;/td>
&lt;td>&lt;code>AppletWidget&lt;/code>部件的析构函数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>searchAll&lt;/td>
&lt;td>搜索所有可设置开机自启动的软件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>update&lt;/td>
&lt;td>更新被自启动的软件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>readfiles&lt;/td>
&lt;td>工具函数，用于读取文件中的内容&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>disable&lt;/td>
&lt;td>禁用开机自启动设置的后端接口&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>enable&lt;/td>
&lt;td>启用开机自启动设置的后端接口&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>add&lt;/td>
&lt;td>添加开机自启动设置的后端接口&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>delete&lt;/td>
&lt;td>删除开机自启动设置的后端接口&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>getFileName&lt;/td>
&lt;td>通过绝对路径找到文件的名字&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>getAllFiles&lt;/td>
&lt;td>递归获取到某目录中所有文件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>globalSearch&lt;/td>
&lt;td>在所有文件中找到.desktop类型文件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Manual&lt;/td>
&lt;td>寻找拟添加软件的函数，给用户在系统中寻找想要添加自启动项的软件&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>showApps&lt;/td>
&lt;td>Debug工具，在Debug信息中打印软件名称&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>showPaths&lt;/td>
&lt;td>Debug工具，在Debug信息中打印路径名称&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>onButtonClicked&lt;/td>
&lt;td>自启动管理窗口启用/禁用按钮的处理函数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>addButtonClicked&lt;/td>
&lt;td>自启动管理窗口添加按钮的处理函数&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>delButtonClicked&lt;/td>
&lt;td>自启动管理窗口删除按钮的处理函数&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h5 id="自启动管理窗口的前端页面">自启动管理窗口的前端页面&lt;/h5>
&lt;p>在&lt;code>AppletWidget&lt;/code>的构造函数中，我们实现了便于用户查看系统开机自启动设置信息列表、并便于管理的前端展示页面：&lt;/p>
&lt;p>&lt;img alt="前端界面" src="https://y-aang.github.io/img/%E5%89%8D%E7%AB%AF%E7%95%8C%E9%9D%A2.png" />&lt;/p>
&lt;h5 id="开机自启动管理的后端接口">开机自启动管理的后端接口&lt;/h5>
&lt;p>该部件中，主要设置了四个函数接口实现与操作系统的交互，分别是searchAll、update、disable、enable&lt;/p>
&lt;p>首先要在MainWidget启动的时候在&lt;code>/home&lt;/code>中读到用户名username&lt;/p>
&lt;h6 id="searchall">searchAll()&lt;/h6>
&lt;p>在&lt;code>opt/apps&lt;/code>中找到所有用户下载程序的文件夹，读入文件夹名称&lt;code>subdir&lt;/code>，再到&lt;code>opts/appp/subdir/entries/application&lt;/code>中找到&lt;code>.desktop&lt;/code>启动文件，解析文件内容，读入name字段并把&lt;code>name - path&lt;/code>存到MainWidge类中，并且返回所有找到的app名称&lt;/p>
&lt;h6 id="update">update()&lt;/h6>
&lt;p>根据启动时读到的username在&lt;code>/data/home/username/.config/autostart&lt;/code>里面找到里面所有的&lt;code>.desktop&lt;/code>文件，分别读取并获取状态Hidden是否为false，把所有Hidden字段为False的插件（被设置成了开机自启动）在MainWidget的selfSetUp成员中设置为&lt;code>pair&amp;lt;name, true&amp;gt;&lt;/code>&lt;/p>
&lt;h6 id="disable">disable()&lt;/h6>
&lt;p>功能设计为禁用自启动。通过&lt;code>name_path&lt;/code>中对name的索引找到路径path，对设置为自启动的应用path会在&lt;code>/data/home/username/.config/autostart/*.desktop&lt;/code>中。读取该&lt;code>.desktop&lt;/code>文件修改Hidden字段为True即可&lt;/p>
&lt;h6 id="enable">enable()&lt;/h6>
&lt;p>功能设计为启用自启动。对于之前添加过的应用，&lt;code>name_path&lt;/code>中得到的路径是在&lt;code>/data/home/username/.config/autostart&lt;/code>中，此时与&lt;code>disable()&lt;/code>过程相同，把Hidden字段设置成false即可；对于之前没有添加过的应用，&lt;code>name_path&lt;/code>中的路径会在&lt;code>opts/appp/subdir/entries/application/appname/entries/application/*.desktop&lt;/code>中，并且该文件是只读的。需要读取该文件并写入到&lt;code>/data/home/username/.config/autostart/*.desktop&lt;/code>中，并且在文件的第一个分区里面写入一行&amp;quot;Hidden=false&amp;quot;&lt;/p>
&lt;h6 id="add">Add()&lt;/h6>
&lt;p>用户点击后打开文件资源管理器对话框，从中选择希望添加的可执行文件并返回文件路径。如果该文件是一个&lt;code>.desktop&lt;/code>类型文件则添加到autostart中，如果不是则会在autostart中创建一个新的&lt;code>.desktop&lt;/code>文件，并且把&lt;code>Exec=&lt;/code>行设置为该可执行文件的路径&lt;/p>
&lt;h6 id="delete">Delete()&lt;/h6>
&lt;p>从管理窗口中删除某应用的管理。传入参数是应用名称，找到该应用的&lt;code>.desktop&lt;/code>文件路径并从autostart中删除，并且在数据结构&lt;code>name_path&lt;/code>和&lt;code>selfSetUp&lt;/code>中删去该部分信息&lt;/p>
&lt;h2 id="四开发计划">四、开发计划&lt;/h2>
&lt;h3 id="第一步49418">第一步（4/9～4/18）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 调研&lt;code>Deepin&lt;/code>、&lt;code>dde-dock&lt;/code>、&lt;code>QT&lt;/code>框架等相关内容&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 设计项目方案&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 分工&lt;/li>
&lt;/ul>
&lt;h3 id="第二步41952">第二步（4/19～5/2）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 搭建主体插件类的框架&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 设计启动项管理窗口的前端展示页面&lt;/li>
&lt;/ul>
&lt;h3 id="第三步53513">第三步（5/3～5/13）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 开发部件类接口&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 完善插件类功能&lt;/li>
&lt;/ul>
&lt;h3 id="第四步514521">第四步（5/14～5/21）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 插件类右键功能开发&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 完成配置文件&lt;/li>
&lt;/ul>
&lt;h3 id="第五步522531">第五步（5/22～5/31）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> Debug&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 撰写文档&lt;/li>
&lt;/ul>
&lt;h2 id="五比赛过程中的重要进展">五、比赛过程中的重要进展&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>日期&lt;/th>
&lt;th>进展&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>4/17&lt;/td>
&lt;td>完成开发环境配置&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>4/27&lt;/td>
&lt;td>完成插件框架设计&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5/3&lt;/td>
&lt;td>完成自启动管理界面的实现&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5/8&lt;/td>
&lt;td>完成自启动管理接口实现的讨论&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5/12&lt;/td>
&lt;td>完成自启动操作项的接口设计与实现&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5/21&lt;/td>
&lt;td>完成配置文件，并通过cmake编译&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5/22&lt;/td>
&lt;td>完成图标的Debug，前端界面展示正常&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5/29&lt;/td>
&lt;td>完成自启动操作接口Debug&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="六系统测试情况">六、系统测试情况&lt;/h2>
&lt;h3 id="1前端测试">1、前端测试&lt;/h3>
&lt;p>在项目总路径下运行&lt;code>install.sh&lt;/code>脚本，可完成插件的编译与安装：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>sh install.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>该脚本使用&lt;code>cmake&lt;/code>编译代码，并安装到&lt;code>dde-dock&lt;/code>插件文件夹中。运行后，可以正常展示自启动管理页面。点击加号后，用户可以在弹窗中选择想要添加自启动项的软件。&lt;/p>
&lt;p>&lt;img alt="结果" src="https://y-aang.github.io/img/%E7%BB%93%E6%9E%9C.png" />&lt;/p>
&lt;p>右键功能展示正常：&lt;/p>
&lt;p>&lt;img alt="右键" src="https://y-aang.github.io/img/%E5%8F%B3%E9%94%AE.png" />&lt;/p>
&lt;h3 id="2自启动管理功能测试">2、自启动管理功能测试&lt;/h3>
&lt;p>以浏览器为例。根据自启动管理窗口的设置，我们尝试添加并启用浏览器的自启动项。关机重启后，浏览器完成自启动。同理，删除、禁用功能均通过测试。&lt;/p>
&lt;h2 id="七遇到的主要问题和解决方法">七、遇到的主要问题和解决方法&lt;/h2>
&lt;h3 id="1开发环境配置">1、开发环境配置&lt;/h3>
&lt;h4 id="1配置-deepin-操作系统">（1）、配置 Deepin 操作系统&lt;/h4>
&lt;p>开发环境：Deepin 20Beta版&lt;/p>
&lt;p>系统架构：x86&lt;/p>
&lt;p>镜像下载链接：&lt;a href="https://uni.mirrors.163.com/deepin-cd/20/deepin-desktop-community-1003-amd64.iso">Deepin 操作系统下载链接&lt;/a>&lt;/p>
&lt;p>虚拟机平台：WMware Workstation 16Pro&lt;/p>
&lt;p>操作系统环境搭建参考博客：&lt;a href="https://blog.csdn.net/qq_44133136/article/details/105887560">环境搭建博客&lt;/a>&lt;/p>
&lt;h4 id="2配置-deepin-插件开发环境">（2）、配置 Deepin 插件开发环境&lt;/h4>
&lt;h5 id="安装基本开发环境">安装基本开发环境：&lt;/h5>
&lt;p>安装包 &lt;code>build-essential&lt;/code>、&lt;code>git&lt;/code>、&lt;code>g++&lt;/code>、&lt;code>cmake&lt;/code>、&lt;code>dde&lt;/code>、&lt;code>dtk&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>sudo apt install build-essential git g++ cmake
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo apt install dde-dock-dev libdtkwidget-dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="安装-qt-开发环境">安装 QT 开发环境：&lt;/h5>
&lt;p>安装 &lt;code>qt5-default&lt;/code>、&lt;code>qt5-doc&lt;/code>、&lt;code>qtcreator&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>sudo apt install qt5-default qt5-doc qtcreator
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>依照上述方法，可在虚拟机中运行&lt;code>qtcreator&lt;/code>，并在&lt;code>qtcreator&lt;/code>中对插件进行测试&lt;/p>
&lt;p>&lt;img alt="image-20230513155011159" src="https://y-aang.github.io/img/QT_IDE.png" />&lt;/p>
&lt;h4 id="3插件安装测试">（3）、插件安装测试&lt;/h4>
&lt;p>为了测试所配置的虚拟机环境可用于 DDE 插件的开发，在环境配置中，本小组选取了&lt;code>Github&lt;/code>仓库中的插件&lt;code>dde-sys-monitor-plugin&lt;/code>（项目地址：&lt;a href="https://github.com/q77190858/dde-sys-monitor-plugin">dde-sys-monitor-plugin插件仓库&lt;/a>）进行试运行。&lt;/p>
&lt;p>根据上述方法配置插件开发环境后，可按照&lt;code>dde-sys-monitor-plugin&lt;/code>中的提示信息顺利运行该插件。这表明开发环境配置已完成。&lt;/p>
&lt;h4 id="4环境配置中遇到的问题">（4）环境配置中遇到的问题&lt;/h4>
&lt;h5 id="安装sudo-apt-install-libdtkwidget-dev-libdtkgui-dev-libdtkcore-dev出错">安装sudo apt install libdtkwidget-dev libdtkgui-dev libdtkcore-dev出错&lt;/h5>
&lt;p>正试图覆盖 /usr/lib/x86_64-linux-gnu/qt5/mkspecs/features/dtk_install_dconfig.prf，它同时被包含于软件包 libdtkcommon-dev 5.5.23-1&lt;/p>
&lt;p>在处理时有错误发生：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>/var/cache/apt/archives/libdtkcore-dev_5.6.4-1+rb1_amd64.deb
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>E: Sub-process /usr/bin/dpkg returned an error code &lt;span style="color:#f92672">(&lt;/span>1&lt;span style="color:#f92672">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>解决方法：&lt;/p>
&lt;p>使用sudo apttitude install libdtkwidget-dev libdtkgui-dev libdtkcore-dev
选择第二种解决方式（先卸载nY再重新安装sudo apt install libdtkwidget-dev libdtkgui-dev libdtkcore-dev）&lt;/p>
&lt;h3 id="2开机启动项处理思路">2、开机启动项处理思路&lt;/h3>
&lt;p>为了实现插件可视化管理软件的开机自启动，了解&lt;code>Deepin&lt;/code>系统开机自启动的功能实现是至关重要的。&lt;/p>
&lt;p>通过查阅资料与实践，我们了解到&lt;code>Deepin&lt;/code>系统包含自启动文件夹&lt;code>~/.config/autostart&lt;/code>，该文件夹类似于 Windows 下的启动文件夹，系统开机时会执行该文件夹下的每个 desktop 文件 Exec 参数指向的脚本或可执行文件。&lt;/p>
&lt;p>为了确认可行性，小组进行了该方法的验证。首先，通过&lt;code>Deepin&lt;/code>系统自带的修改开机自启动设置的方法，修改开机启动项（图中修改&lt;code>终端&lt;/code>的自启动项）：&lt;/p>
&lt;p>&lt;img alt="deepin自启动修改" src="https://y-aang.github.io/img/deepin%E8%87%AA%E5%90%AF%E5%8A%A8%E4%BF%AE%E6%94%B9.png" />&lt;/p>
&lt;p>随后检查自启动文件夹&lt;code>~/.config/autostart&lt;/code>：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>yang@yang-PC:~/.config/autostart$ ls
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>deepin-terminal.desktop org.deepin.browser.desktop
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>发现&lt;code>deepin-terminal.desktop&lt;/code>文件被添加入了该自启动文件夹中，并且会在开机时自启动。&lt;/p>
&lt;p>而取消终端的自启动时，&lt;code>~/.config/autostart&lt;/code>中已有的&lt;code>deepin-terminal.desktop&lt;/code>文件并不会被移除，而是其中的&lt;code>Hidden&lt;/code>的字段会被修改为&lt;code>false&lt;/code>，表示取消开机自启动设置。&lt;/p>
&lt;p>由此，可以通过在插件中检查所有&lt;code>~/.config/autostart&lt;/code>文件夹中&lt;code>.desktop&lt;/code>文件的&lt;code>Hidden&lt;/code>字段来搜索系统所有的自启动软件；也可以通过添加&lt;code>.desktop&lt;/code>文件、修改&lt;code>Hidden&lt;/code>字段的方式进行开机自启动设置的修改。&lt;/p>
&lt;h3 id="3与-deepin-接口设计">3、与 deepin 接口设计&lt;/h3>
&lt;h4 id="1关于开机自启动项">（1）、关于开机自启动项&lt;/h4>
&lt;p>在查找deepin如何实现开机自启动时，看到了在deepin文件夹中有多个名为autostart的文件夹，其中有一部分包含了系统文件（例如终端、输入法等），但是在我们通过“开始”菜单修改其是否自启动性质时发现并为出现变化，并且我们自行在“应用商店”里面下载的软件并没有在设置为开机自启动后进入到该文件夹。经过在网络上搜索了关于deepin开发的一些讨论资料以及学习了一些关于启动文件&lt;code>.desktop&lt;/code>文件功能并通过vscode对是否开机自启动设置后的文件进行比较后，找到了文件路径位于&lt;code>/data/home/username/.config/autostart&lt;/code>中，并且在文件中有Hidden字段使得不用每次取消自启动是都要删除文件并且下次设置又重新加入&lt;/p>
&lt;h4 id="2关于查找应用程序">（2）、关于查找应用程序&lt;/h4>
&lt;p>一开始的想法是在系统目录下整体查找，找到系统中所有的&lt;code>.desktop&lt;/code>文件，但显然这样过于繁琐且效率低下。后来在通过打开每个引用的&lt;code>.desktop&lt;/code>文件后发现exec项均在&lt;code>/opt/apps&lt;/code>中，打开该文件夹可以发现里面有所有用户下载的应用程序及其依赖等文件的文件夹，并在打开每个文件夹后可以找到启动文件均在&lt;code>entries/application&lt;/code>中，并且可能有多个&lt;code>.desktop&lt;/code>文件，所以把自启动的工作变为了对启动器文件用文本方式打开后的解析与修改&lt;/p>
&lt;h4 id="3关于函数接口设计">（3）、关于函数接口设计&lt;/h4>
&lt;p>对QT库中的数据类型和class的使用不太熟悉，需要根据官方文档对提供的api和C++模式的代码进行替换。但熟悉使用之后理解到了QT数据类型的多样性以及提供的方法会更加完备，在做开发时避免了很多自己重写方法的复杂流程。&lt;/p>
&lt;h3 id="4cmake中遇到的问题">4、cmake中遇到的问题&lt;/h3>
&lt;h4 id="1cmake-出现-dbuscore-找不到">（1）、cmake 出现 dbus、core 找不到&lt;/h4>
&lt;p>解决方法：find_package()的REQUIRED后面新增找不到的对应的包&lt;/p>
&lt;h4 id="2cmake-出现-foundfalse">（2）、cmake 出现 FOUND=FALSE&lt;/h4>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span> /usr/lib/x86_64-linux-gnu/cmake/DtkWidget/DtkWidgetConfig.cmake
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> but it set DtkWidget_FOUND to FALSE so package &lt;span style="color:#e6db74">"DtkWidget"&lt;/span> is considered to
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> be NOT FOUND.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>解决方法：使用.pro文件设置cmake需要的相关参数。&lt;/p>
&lt;h2 id="八分工和协作">八、分工和协作&lt;/h2>
&lt;p>朱元依：插件类框架开发、部件类前端开发&lt;/p>
&lt;p>沈扬：自启动管理功能逻辑设计、插件类右键功能开发&lt;/p>
&lt;p>朱俊杰：自启动管理窗口后端接口开发（添加、删除、启用、禁用）&lt;/p>
&lt;h2 id="九提交仓库目录和文件描述">九、提交仓库目录和文件描述&lt;/h2>
&lt;p>. &lt;br />
├── CMakeLists.txt &lt;br />
├── README.md &lt;br />
├── aboutdialog.cpp    #关于窗口的实现文件 &lt;br />
├── aboutdialog.h     #关于窗口的头文件 &lt;br />
├── aboutdialog.ui     #关于窗口的UI文件 &lt;br />
├── appletwidget.cpp   #自启动管理窗口的实现文件 &lt;br />
├── appletwidget.h    #自启动管理窗口的头文件 &lt;br />
├── images        #图片 &lt;br />
│ ├── QT_IDE.png &lt;br />
│ ├── QT_前端.png &lt;br />
│ ├── deepin自启动修改.png &lt;br />
│ ├── 类图.jpg &lt;br />
│ ├── 右键.png &lt;br />
│ ├── 图标.png &lt;br />
│ ├── 结果.png &lt;br />
│ ├── 中期类图.jpg &lt;br />
│ ├── 中期测试.png &lt;br />
│ └── 前端界面.png &lt;br />
├── install.sh        #插件安装脚本 &lt;br />
├── main_aboutdialog_test.cpp #关于窗口的测试文件 &lt;br />
├── main_test.cpp      #测试文件 &lt;br />
├── mainwidget.cpp     #插件类的实现文件 &lt;br />
├── mainwidget.h      #插件类的头文件 &lt;br />
├── self_startup.json     #插件的元数据文件，指明了当前插件所使用的 dde-dock 的接口版本 &lt;br />
├── self_startup.pro     #辅助 cmake 的配置文件 &lt;br />
├── self_startup.qrc     #用于展示插件图片 &lt;br />
├── selfstartupplugin.cpp   #部件类的实现文件 &lt;br />
├── selfstartupplugin.h    #部件类的头文件 &lt;br />
├── uninstall.sh        #插件卸载脚本 &lt;br />
├── 初赛报告.md &lt;br />
└── 过程文档.md&lt;/p>
&lt;h2 id="十比赛收获">十、比赛收获&lt;/h2>
&lt;p>借由为&lt;code>deepin&lt;/code>的&lt;code>dde-dock&lt;/code>编写插件的机会，我们小组了解了&lt;code>deepin&lt;/code>系统相关的接口、&lt;code>dde-dock&lt;/code>插件加载原理、开发逻辑等等操作系统相关的知识；同时我们在合作开发的过程中熟悉了软件工程的开发规范。小组同学在比赛中均受益匪浅。&lt;/p>
&lt;h2 id="十一与企业导师的沟通情况">十一、与企业导师的沟通情况&lt;/h2>
&lt;p>我们已于企业导师（王子冲）通过电子邮件进行联系。王导师向我们推荐了 deepin 开源社区各种公开渠道（如实时聊天渠道 Matrix、开发者社区讨论板等），鼓励我们在开发过程中将所遇到的问题在社区研发话题板块中进行公开探索。&lt;/p>
&lt;p>此外，王导师还耐心的向我们介绍了该课题的设置原因，这对与我们在项目设计的过程中了解用户需求起到了很大的作用。&lt;/p></description><author>dde 自启动管理插件开发小组博客</author></item><item><title>deepin-for-arm64支持 - Zeno-sole</title><link>https://www.cnblogs.com/zeno404/p/17439916.html</link><pubDate>Mon, 29 May 2023 03:15:00 +0000</pubDate><guid isPermaLink="true">https://www.cnblogs.com/zeno404/p/17439916.html</guid><description>【摘要】arm64架构支持 v23仓库已经支持arm64和amd64架构软件包，arm64架构的基础环境已经具备，现在就差镜像制作工具的支持了，镜像构建工具的目标是构建出标准pc镜像。为此我借来一台紫光 飞腾D2000机器进行arm64的适配工作，这台机器有相对标准的UEFI固件，目前已经支持UEFI安装， &lt;a href="https://www.cnblogs.com/zeno404/p/17439916.html" target="_blank">阅读全文&lt;/a></description><author>zeno</author></item><item><title>DDE v23 RC 移植简要指南</title><link>https://deepin-community.github.io/sig-dde-porting/posts/dde-v23rc-porting-guide/?utm_source=atom_feed</link><pubDate>Sun, 14 May 2023 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/dde-v23rc-porting-guide/</guid><description>&lt;p>&lt;em>编辑的话请把自己的名字加到作者名单里&lt;/em>&lt;/p>
&lt;p>DDE v23 RC 即将随 deepin v23 RC 发布（你阅读到这个文章的时候可能已经发布了）。为了方便各个其它发行版的包维护者可以更方便的移植 DDE 到对应的发行版，这里提供一篇简要的移植指南，用以描述常见的移植问题和解决方案。&lt;/p>
&lt;blockquote>
&lt;p>下面对项目名称的称呼均以 GitHub 对应的原始仓库名为准。
&amp;#123;.note}&lt;/p>&lt;/blockquote>
&lt;h2 id="概览">概览&lt;/h2>
&lt;p>相比 beta2 -&amp;gt; beta3 而言，原本处于技术预览状态的 dde-shell 项目现已开始逐步取代部分旧的 DDE 组件。DDE 此次 beta3 -&amp;gt; RC 的更新中，dde-shell 取代了 dde-dock 项目，dde-launchpad 也开始转为使用 dde-shell 的对应插件版本。同时，由于对 Qt6 与 DTK6 使用的增加，我们也对 DTK 进行了大量的问题修复，这些修复也被对应的组件（dde-launchpad 与 dde-shell）依赖。&lt;/p>
&lt;p>由于这些项目的版本间互相影响，我们建议移植人员参照 deepin v23 RC 所使用的包版本进行打包，下面会对主要的部分进行详细说明。&lt;/p>
&lt;p>需要注意的是，由于此文章编写时间早于版本发布时间，故最终版本镜像中使用的版本可能高于下面列出的版本。我们尽可能确保此文章的准确性，但若您需要获取 ISO 镜像中使用的确切软件版本列表，请挂载 ISO 后参阅 &lt;code>LIVE/FILESYS0.MAN&lt;/code> 路径对应的文件的内容。&lt;/p>
&lt;h2 id="主要组件">主要组件&lt;/h2>
&lt;h3 id="dtk-与-dtk6">DTK 与 DTK6&lt;/h3>
&lt;p>DTK 是 DDE 组件与应用的基础依赖，适用于 RC 的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>dtkcommon&lt;/td>
&lt;td>5.6.29&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkcore&lt;/td>
&lt;td>5.6.29&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkgui&lt;/td>
&lt;td>5.6.29&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkwidget&lt;/td>
&lt;td>5.6.29&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkdeclarative&lt;/td>
&lt;td>5.6.29&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5integration&lt;/td>
&lt;td>5.6.29&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5platform-plugins&lt;/td>
&lt;td>5.6.29&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6core&lt;/td>
&lt;td>6.0.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6gui&lt;/td>
&lt;td>6.0.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6widget&lt;/td>
&lt;td>6.0.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6declarative&lt;/td>
&lt;td>6.0.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6integration&lt;/td>
&lt;td>6.0.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6platform-plugins&lt;/td>
&lt;td>6.0.16&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>本次 DTK 版本号以及相对应的平台插件等版本号均已对齐，可直接参照打包。&lt;/p>
&lt;p>关于 qt5platform-plugins，现有的 dwayland 插件可能对非 DDE 环境（例如 KDE）的 wayland 用户存在影响，可参照 &lt;a href="https://github.com/linuxdeepin/developer-center/issues/7217">linuxdeepin/developer-center#7217&lt;/a> 打对应的 patch 规避影响。&lt;/p>
&lt;p>dtk6 曾对 Qt 6.2, Qt 6.4 和 Qt 6.6 均进行过适配，我们目前的研发与测试主要使用 Qt 6.6 版本。对于 Qt 6.7 我们暂未进行主动支持，对于 Qt 6.7 的相关处理请先参照 &lt;a href="https://gitlab.archlinux.org/archlinux/packaging/packages/deepin-qt6platform-plugins/-/blob/9b6252bc386659bf7f586578dea394be6ff40462/qt-6.7.patch">Arch Linux 目前使用的 &lt;code>qt-6.7.patch&lt;/code>&lt;/a>。&lt;/p>
&lt;p>目前，使用 dtk6 的正式组件有 dde-application-manager，dde-launchpad 与 dde-shell，技术预览组件有 treeland。&lt;/p>
&lt;h3 id="dde-主要组件">DDE 主要组件&lt;/h3>
&lt;p>下面仅涉及变化较大或影响较广的组件。其余未涉及的组件可正常参照最新 tag 进行打包与移植。&lt;/p>
&lt;p>下面涉及到的组件的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>dde-session&lt;/td>
&lt;td>1.2.9&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-manager&lt;/td>
&lt;td>1.2.12&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-shell&lt;/td>
&lt;td>0.0.23&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-launchpad&lt;/td>
&lt;td>0.6.12&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-application-wizard&lt;/td>
&lt;td>0.1.5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dde-dock&lt;/td>
&lt;td>被 dde-shell 取代，不再使用&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h4 id="dde-application-manager">dde-application-manager&lt;/h4>
&lt;p>本次此组件并不存在如 beta2 -&amp;gt; beta3 阶段时的巨大变化，但由于此组件仍为诸多组件的核心依赖，并且此组件也因其它组件需要而增加了一些新的接口，故建议总是使用最新版本。&lt;/p>
&lt;h4 id="dde-shell">dde-shell&lt;/h4>
&lt;p>dde-shell 旨在将 DDE 桌面环境插件化与模块化，降低开发难度，使各个组件的替换变得更加容易，并且提供更好的桌面环境集成支持。RC 阶段，dde-shell 已经可以满足原计划的部分目标。现 DDE 环境下，dde-shell 已取代 dde-dock 来负责管理整个 dock 区域，并且 dde-launchpad 也提供了（且默认使用）对应的 dde-shell 插件，用以展示启动器相关的界面。&lt;/p>
&lt;p>尽管 dde-shell 也包含了一份通知中心插件，但这个插件并不会在 RC 中被启用。&lt;/p>
&lt;p>出于初期快速开发目的，dde-shell 本身内附了诸多其它项目的托盘插件代码以及一些第三方库，这部分的代码会在后续版本迭代中逐渐被调整或移除。对于这些代码，可在移植过程中先行酌情调整或移除。项目内附的 networkmanager-qt 依赖也会在后续移除，转而使用系统版本。由于 RC 版本不会涵盖此变更，若有需要，请参见 &lt;a href="https://github.com/linuxdeepin/dde-shell/pull/286">linuxdeepin/dde-shell#286&lt;/a>。&lt;/p>
&lt;h4 id="dde-launchpad">dde-launchpad&lt;/h4>
&lt;p>RC 版本中，dde-launchpad 不再支持 Qt 5 构建，也不再直接支持以独立进程的形式被最终用户使用（事实上仍然支持，但仅供开发调试场景下使用）而仅支持以 dde-shell 插件的形式被最终用户使用。因而，打包 dde-launchpad 现需要先打包 dde-shell，并确保用户最终使用的是 dde-shell。&lt;/p>
&lt;h4 id="dde-session">dde-session&lt;/h4>
&lt;p>在之前的版本中，dde-session 提供的相关 systemd 服务依赖关系会导致进入 KDE 环境时也会拉起 DDE 相关的服务，最终由于拉起失败导致 KDE 环境也无法进入。在 RC 版本的 dde-session 中，此问题应当已被解决。&lt;/p>
&lt;p>另外重申，我们已在 beta3 阶段放弃了对 deepin-kwin wayland 的支持，DDE 后续所有 wayland 相关的支持均由 treeland 提供。&lt;/p>
&lt;h2 id="技术预览组件">技术预览组件&lt;/h2>
&lt;p>技术预览阶段的组件均可酌情决定是否打包。需要注意的是，这些组件并不一定在 RC 阶段存在改善。&lt;/p>
&lt;h3 id="treeland--ddm">treeland / ddm&lt;/h3>
&lt;p>treeland 目前是技术预览阶段的项目，兼顾 Wayland 窗口合成器和显示管理功能。treeland 除需要 dtk6declarative 外，还依赖新项目包括 &lt;a href="https://github.com/vioken/waylib">waylib&lt;/a> 和 &lt;a href="https://github.com/vioken/qwlroots">qwlroots&lt;/a>。需要使用 Qt 版本 &amp;gt;= 6.6.0， wlroots 版本 &amp;gt;= 0.17.0 编译 qwlroots/waylib。&lt;/p>
&lt;p>ddm 的仓库也在 RC 阶段进行了拆分，现在 ddm 与 treeland 已是分别独立的两个仓库进行维护了。&lt;/p>
&lt;h3 id="deepin-im">deepin-im&lt;/h3>
&lt;p>treeland 目前是技术预览阶段的项目，提供了输入法的抽象层。目前，deepin-im 仅期望在 treeland 环境下使用。它会设置 &lt;code>QT_IM_MODULE&lt;/code> 等环境变量的值来影响实际使用的输入法模块。&lt;/p>
&lt;p>RC 版本中，并未涉及对 deepin-im 组件的改善。&lt;/p>
&lt;h2 id="获取移植帮助">获取移植帮助&lt;/h2>
&lt;p>如果您希望得到移植相关的帮助，请考虑加入我们 DDE 移植小组的在线交流群（下列房间有桥接，任选其一即可），一起展开相关的交流：&lt;/p>
&lt;ul>
&lt;li>Matrix 群：&lt;code>#dde-port:deepin.org&lt;/code>&lt;/li>
&lt;li>Telegram 群：&lt;a href="https://t.me/ddeport">https://t.me/ddeport&lt;/a>&lt;/li>
&lt;/ul></description><author>DDE 移植兴趣小组</author></item><item><title>Kitty configuration for macOS</title><link>https://blog.mkacg.com/2023/04/17/Kitty-configuration-for-macOS/</link><pubDate>Mon, 17 Apr 2023 08:39:51 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2023/04/17/Kitty-configuration-for-macOS/</guid><description>&lt;p>I’ve been using Kitty for a couple of days. I use it because there is a plugin for nvim that can seamlessly switch focus with kitty, so I don’t need to repeat the settings, I like it very much.&lt;/p>&lt;p>After a period of use. I have completed part of the configuration, and now I want to share it.&lt;/p>&lt;p>I use different folders for related functions. Such as themes, tabs, windows, and shortcuts.&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="meta prompt_"># &lt;/span>&lt;span class="language-bash">lxz @ lxzMacBook-Pro &lt;span class="keyword">in&lt;/span> ~/.dot/kitty/.config/kitty on git:master o [16:38:58]&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="meta prompt_">$ &lt;/span>&lt;span class="language-bash">tree&lt;/span>&lt;/span>&lt;br />&lt;span class="line">.&lt;/span>&lt;br />&lt;span class="line">├── kitty.conf&lt;/span>&lt;br />&lt;span class="line">├── kitty.d&lt;/span>&lt;br />&lt;span class="line">│   ├── init&lt;/span>&lt;br />&lt;span class="line">│   │   └── init.conf&lt;/span>&lt;br />&lt;span class="line">│   ├── keybind&lt;/span>&lt;br />&lt;span class="line">│   │   ├── init.conf&lt;/span>&lt;br />&lt;span class="line">│   │   ├── nvim.conf&lt;/span>&lt;br />&lt;span class="line">│   │   ├── tab.conf&lt;/span>&lt;br />&lt;span class="line">│   │   └── window.conf&lt;/span>&lt;br />&lt;span class="line">│   ├── session&lt;/span>&lt;br />&lt;span class="line">│   │   └── init.conf&lt;/span>&lt;br />&lt;span class="line">│   └── theme&lt;/span>&lt;br />&lt;span class="line">│   ├── background.conf&lt;/span>&lt;br />&lt;span class="line">│   ├── color.conf&lt;/span>&lt;br />&lt;span class="line">│   ├── font.conf&lt;/span>&lt;br />&lt;span class="line">│   ├── tabbar.conf&lt;/span>&lt;br />&lt;span class="line">│   └── window.conf&lt;/span>&lt;br />&lt;span class="line">└── session.conf&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">6 directories, 13 files&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h2 id="Some-screenshot">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Some-screenshot" title="Some screenshot">&lt;/a>Some screenshot&lt;/h2>&lt;h3 id="session-look-at-the-lower-left-corner">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#session-look-at-the-lower-left-corner" title="session (look at the lower left corner)">&lt;/a>session (look at the lower left corner)&lt;/h3>&lt;img alt="session" src="https://blog.mkacg.com/rss/2023-04-17-17.52.14.png" />&lt;img alt="session" src="https://blog.mkacg.com/rss/2023-04-17-17.59.50.png" />&lt;h3 id="multi-splits">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#multi-splits" title="multi splits">&lt;/a>multi splits&lt;/h3>&lt;img alt="multi splits" src="https://blog.mkacg.com/rss/2023-04-17-17.57.06.png" />&lt;h2 id="Base-settings">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Base-settings" title="Base settings">&lt;/a>Base settings&lt;/h2>&lt;p>In kitty.conf, I just set to load configuration files in other directories.&lt;/p>&lt;figure class="highlight text">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">globinclude kitty.d/**/*.conf&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>I won’t show the configuration after splitting, just give a hint according to the function.&lt;/p>&lt;h2 id="Init">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Init" title="Init">&lt;/a>Init&lt;/h2>&lt;p>In init.conf, I set some default variables.&lt;/p>&lt;figure class="highlight text">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">term xterm-256color&lt;/span>&lt;br />&lt;span class="line">shell_integration enabled&lt;/span>&lt;br />&lt;span class="line">allow_hyperlinks yes&lt;/span>&lt;br />&lt;span class="line">editor nvim&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h2 id="Theme-settings">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Theme-settings" title="Theme settings">&lt;/a>Theme settings&lt;/h2>&lt;h3 id="Tabs-settings">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Tabs-settings" title="Tabs settings">&lt;/a>Tabs settings&lt;/h3>&lt;p>In tabs settings, I like &lt;code>powerline&lt;/code> style.&lt;/p>&lt;figure class="highlight text">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">tab_bar_style powerline&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h3 id="Windows-settings">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Windows-settings" title="Windows settings">&lt;/a>Windows settings&lt;/h3>&lt;figure class="highlight text">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">window_border_width 0.5pt&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">window_resize_step_cells 2&lt;/span>&lt;br />&lt;span class="line">window_resize_step_lines 2&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">initial_window_width 640&lt;/span>&lt;br />&lt;span class="line">initial_window_height 400&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">draw_minimal_borders yes&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">inactive_text_alpha 0.7&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">hide_window_decorations no&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">macos_titlebar_color background&lt;/span>&lt;br />&lt;span class="line">macos_thicken_font 0.75&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">active_border_color none&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"># default layout is vertical splits only&lt;/span>&lt;br />&lt;span class="line">enabled_layouts splits&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">enable_audio_bell no&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h3 id="Fonts-settings">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Fonts-settings" title="Fonts settings">&lt;/a>Fonts settings&lt;/h3>&lt;figure class="highlight text">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">font_family FiraCode Nerd Font Mono Retina&lt;/span>&lt;br />&lt;span class="line">font_size 16.0&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h3 id="Color-settings">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Color-settings" title="Color settings">&lt;/a>Color settings&lt;/h3>&lt;figure class="highlight text">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line"># Dark One Nuanced by ariasuni, https://store.kde.org/p/1225908&lt;/span>&lt;br />&lt;span class="line"># Imported from KDE .colorscheme format by thematdev, https://thematdev.org&lt;/span>&lt;br />&lt;span class="line"># For migrating your schemes from Konsole format see&lt;/span>&lt;br />&lt;span class="line"># https://git.thematdev.org/thematdev/konsole-scheme-migration&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"># importing Background&lt;/span>&lt;br />&lt;span class="line">background #282c34&lt;/span>&lt;br />&lt;span class="line"># importing BackgroundFaint&lt;/span>&lt;br />&lt;span class="line"># importing BackgroundIntense&lt;/span>&lt;br />&lt;span class="line"># importing Color0&lt;/span>&lt;br />&lt;span class="line">color0 #3f4451&lt;/span>&lt;br />&lt;span class="line"># importing Color0Faint&lt;/span>&lt;br />&lt;span class="line">color16 #282c34&lt;/span>&lt;br />&lt;span class="line"># importing Color0Intense&lt;/span>&lt;br />&lt;span class="line">color8 #4f5666&lt;/span>&lt;br />&lt;span class="line"># importing Color1&lt;/span>&lt;br />&lt;span class="line">color1 #e06c75&lt;/span>&lt;br />&lt;span class="line"># importing Color1Faint&lt;/span>&lt;br />&lt;span class="line">color17 #c25d66&lt;/span>&lt;br />&lt;span class="line"># importing Color1Intense&lt;/span>&lt;br />&lt;span class="line">color9 #ff7b86&lt;/span>&lt;br />&lt;span class="line"># importing Color2&lt;/span>&lt;br />&lt;span class="line">color2 #98c379&lt;/span>&lt;br />&lt;span class="line"># importing Color2Faint&lt;/span>&lt;br />&lt;span class="line">color18 #82a566&lt;/span>&lt;br />&lt;span class="line"># importing Color2Intense&lt;/span>&lt;br />&lt;span class="line">color10 #b1e18b&lt;/span>&lt;br />&lt;span class="line"># importing Color3&lt;/span>&lt;br />&lt;span class="line">color3 #d19a66&lt;/span>&lt;br />&lt;span class="line"># importing Color3Faint&lt;/span>&lt;br />&lt;span class="line">color19 #b38257&lt;/span>&lt;br />&lt;span class="line"># importing Color3Intense&lt;/span>&lt;br />&lt;span class="line">color11 #efb074&lt;/span>&lt;br />&lt;span class="line"># importing Color4&lt;/span>&lt;br />&lt;span class="line">color4 #61afef&lt;/span>&lt;br />&lt;span class="line"># importing Color4Faint&lt;/span>&lt;br />&lt;span class="line">color20 #5499d1&lt;/span>&lt;br />&lt;span class="line"># importing Color4Intense&lt;/span>&lt;br />&lt;span class="line">color12 #67cdff&lt;/span>&lt;br />&lt;span class="line"># importing Color5&lt;/span>&lt;br />&lt;span class="line">color5 #c678dd&lt;/span>&lt;br />&lt;span class="line"># importing Color5Faint&lt;/span>&lt;br />&lt;span class="line">color21 #a966bd&lt;/span>&lt;br />&lt;span class="line"># importing Color5Intense&lt;/span>&lt;br />&lt;span class="line">color13 #e48bff&lt;/span>&lt;br />&lt;span class="line"># importing Color6&lt;/span>&lt;br />&lt;span class="line">color6 #56b6c2&lt;/span>&lt;br />&lt;span class="line"># importing Color6Faint&lt;/span>&lt;br />&lt;span class="line">color22 #44919a&lt;/span>&lt;br />&lt;span class="line"># importing Color6Intense&lt;/span>&lt;br />&lt;span class="line">color14 #63d4e0&lt;/span>&lt;br />&lt;span class="line"># importing Color7&lt;/span>&lt;br />&lt;span class="line">color7 #e6e6e6&lt;/span>&lt;br />&lt;span class="line"># importing Color7Faint&lt;/span>&lt;br />&lt;span class="line">color23 #c8c8c8&lt;/span>&lt;br />&lt;span class="line"># importing Color7Intense&lt;/span>&lt;br />&lt;span class="line">color15 #ffffff&lt;/span>&lt;br />&lt;span class="line"># importing Foreground&lt;/span>&lt;br />&lt;span class="line">foreground #abb2bf&lt;/span>&lt;br />&lt;span class="line"># importing ForegroundFaint&lt;/span>&lt;br />&lt;span class="line"># importing ForegroundIntense&lt;/span>&lt;br />&lt;span class="line"># importing General&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h2 id="Shortcuts-settings">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Shortcuts-settings" title="Shortcuts settings">&lt;/a>Shortcuts settings&lt;/h2>&lt;h3 id="Init-1">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Init-1" title="Init">&lt;/a>Init&lt;/h3>&lt;figure class="highlight text">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line"># clear the terminal screen&lt;/span>&lt;br />&lt;span class="line">map cmd+k combine : clear_terminal scrollback active : send_text normal,application \x0c&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"># jump to beginning and end of word&lt;/span>&lt;br />&lt;span class="line">map alt+left send_text all \x1b\x62&lt;/span>&lt;br />&lt;span class="line">map alt+right send_text all \x1b\x66&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"># jump to beginning and end of line&lt;/span>&lt;br />&lt;span class="line">map cmd+left send_text all \x01&lt;/span>&lt;br />&lt;span class="line">map cmd+right send_text all \x05&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"># Map cmd + &amp;lt;num&amp;gt; to corresponding tabs&lt;/span>&lt;br />&lt;span class="line">map cmd+1 goto_tab 1&lt;/span>&lt;br />&lt;span class="line">map cmd+2 goto_tab 2&lt;/span>&lt;br />&lt;span class="line">map cmd+3 goto_tab 3&lt;/span>&lt;br />&lt;span class="line">map cmd+4 goto_tab 4&lt;/span>&lt;br />&lt;span class="line">map cmd+5 goto_tab 5&lt;/span>&lt;br />&lt;span class="line">map cmd+6 goto_tab 6&lt;/span>&lt;br />&lt;span class="line">map cmd+7 goto_tab 7&lt;/span>&lt;br />&lt;span class="line">map cmd+8 goto_tab 8&lt;/span>&lt;br />&lt;span class="line">map cmd+9 goto_tab 9&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"># changing font sizes&lt;/span>&lt;br />&lt;span class="line">map cmd+equal change_font_size all +2.0&lt;/span>&lt;br />&lt;span class="line">map cmd+minus change_font_size all -2.0&lt;/span>&lt;br />&lt;span class="line">map cmd+0 change_font_size all 0&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">map cmd+c copy_to_clipboard&lt;/span>&lt;br />&lt;span class="line">map cmd+v paste_from_clipboard&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h3 id="Tab">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Tab" title="Tab">&lt;/a>Tab&lt;/h3>&lt;figure class="highlight text">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">map alt+1 goto_tab 1&lt;/span>&lt;br />&lt;span class="line">map alt+2 goto_tab 2&lt;/span>&lt;br />&lt;span class="line">map alt+3 goto_tab 3&lt;/span>&lt;br />&lt;span class="line">map alt+4 goto_tab 4&lt;/span>&lt;br />&lt;span class="line">map alt+5 goto_tab 5&lt;/span>&lt;br />&lt;span class="line">map alt+6 goto_tab 6&lt;/span>&lt;br />&lt;span class="line">map alt+7 goto_tab 7&lt;/span>&lt;br />&lt;span class="line">map alt+8 goto_tab 8&lt;/span>&lt;br />&lt;span class="line">map alt+9 goto_tab 9&lt;/span>&lt;br />&lt;span class="line">map alt+0 goto_tab 0&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"># open new tab with cmd+t&lt;/span>&lt;br />&lt;span class="line">map cmd+t new_tab&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"># switch between next and previous splits&lt;/span>&lt;br />&lt;span class="line">map cmd+] next_window&lt;/span>&lt;br />&lt;span class="line">map cmd+[ previous_window&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h3 id="Window">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Window" title="Window">&lt;/a>Window&lt;/h3>&lt;figure class="highlight text">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line"># open new split (window) with cmd+d retaining the cwd&lt;/span>&lt;br />&lt;span class="line">map cmd+w close_window&lt;/span>&lt;br />&lt;span class="line">map cmd+shif+n new_os_window&lt;/span>&lt;br />&lt;span class="line">map cmd+d launch --location=hsplit --cwd=current&lt;/span>&lt;br />&lt;span class="line">map cmd+shift+d launch --location=vsplit --cwd=current&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h3 id="Neovim">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Neovim" title="Neovim">&lt;/a>Neovim&lt;/h3>&lt;figure class="highlight text">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">map ctrl+j kitten pass_keys.py neighboring_window bottom ctrl+j &amp;quot;^.* - nvim$&amp;quot;&lt;/span>&lt;br />&lt;span class="line">map ctrl+k kitten pass_keys.py neighboring_window top ctrl+k &amp;quot;^.* - nvim$&amp;quot;&lt;/span>&lt;br />&lt;span class="line">map ctrl+h kitten pass_keys.py neighboring_window left ctrl+h &amp;quot;^.* - nvim$&amp;quot;&lt;/span>&lt;br />&lt;span class="line">map ctrl+l kitten pass_keys.py neighboring_window right ctrl+l &amp;quot;^.* - nvim$&amp;quot;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"># the 3 here is the resize amount, adjust as needed&lt;/span>&lt;br />&lt;span class="line">map alt+j kitten pass_keys.py relative_resize down 3 alt+j &amp;quot;^.* - nvim$&amp;quot;&lt;/span>&lt;br />&lt;span class="line">map alt+k kitten pass_keys.py relative_resize up 3 alt+k &amp;quot;^.* - nvim$&amp;quot;&lt;/span>&lt;br />&lt;span class="line">map alt+h kitten pass_keys.py relative_resize left 3 alt+h &amp;quot;^.* - nvim$&amp;quot;&lt;/span>&lt;br />&lt;span class="line">map alt+l kitten pass_keys.py relative_resize right 3 alt+l &amp;quot;^.* - nvim$&amp;quot;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>Moving in shell and nvim.&lt;/p>&lt;img alt="nvim-integration" src="https://blog.mkacg.com/rss/nvim-integration.gif" />&lt;h2 id="Session">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Session" title="Session">&lt;/a>Session&lt;/h2>&lt;p>Kitty supports session management, I added some default sessions, and opened session sockets for nvim.&lt;/p>&lt;p>The session.conf at the root is the location configuration of the session.&lt;/p>&lt;figure class="highlight text">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">new_tab home&lt;/span>&lt;br />&lt;span class="line">layout splits&lt;/span>&lt;br />&lt;span class="line">cd ~&lt;/span>&lt;br />&lt;span class="line">launch zsh&lt;/span>&lt;br />&lt;span class="line">focus&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">new_tab work&lt;/span>&lt;br />&lt;span class="line">cd ~/Develop/linuxdeepin/&lt;/span>&lt;br />&lt;span class="line">launch zsh&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">new_tab nvim&lt;/span>&lt;br />&lt;span class="line">cd ~/.config/nvim&lt;/span>&lt;br />&lt;span class="line">launch zsh&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h3 id="Init-2">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Init-2" title="Init">&lt;/a>Init&lt;/h3>&lt;figure class="highlight text">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">startup_session session.conf&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"># Other unix systems:&lt;/span>&lt;br />&lt;span class="line">allow_remote_control yes&lt;/span>&lt;br />&lt;span class="line">listen_on unix:/tmp/.kitty&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure></description><author>justforlxz</author></item><item><title>dde-nixos 近期进展公告</title><link>https://deepin-community.github.io/sig-dde-porting/posts/dde-nixos-2023-3/?utm_source=atom_feed</link><pubDate>Mon, 10 Apr 2023 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/dde-nixos-2023-3/</guid><description>&lt;p>目前 dde-nixos 已经分叉，mian 分支进行 v23 的维护，目前主要更新了 dtk 和部分 deepin 开头的应用， dde 开头的核心应用移植暂未实现，dbus 接口不兼容，因此目前不可日常使用。&lt;/p>
&lt;p>gomod 分支用于测试使用 buildGoModule 完成构建，仅验证可行性，实际使用还需要调整硬编码相关的 patch。&lt;/p>
&lt;p>日常使用 DDE 需要切换 v20 分支，会优先使用已经提交到上游的应用：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-nix" data-lang="nix">&lt;span style="display:flex;">&lt;span> dde-nixos &lt;span style="color:#f92672">=&lt;/span> &amp;#123;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">"github:linuxdeepin/dde-nixos/v20"&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> inputs&lt;span style="color:#f92672">.&lt;/span>nixpkgs&lt;span style="color:#f92672">.&lt;/span>follows &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">"nixpkgs"&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> };
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 v20 分支，dtk 使用 5.6.3 不再升级，deepin 应用会保持最新的 v20 版的最新版本（不会上 6.0.0），dde 应用冻结为 1 月份打包时测试可用的版本，一般不再升级：&lt;/p>
&lt;p>既除了 deepin 应用，其他应用只有在 v23 移植完成后再更新。&lt;/p>
&lt;p>目前 NixOS 23.05 — Feature Freeze &amp;amp; Release Blockers 已经开始，进度请关注：
&lt;a href="https://github.com/NixOS/nixpkgs/issues/224457#issuecomment-1501383113">https://github.com/NixOS/nixpkgs/issues/224457#issuecomment-1501383113&lt;/a>&lt;/p>
&lt;p>向上游贡献的主要调整：&lt;/p>
&lt;ol>
&lt;li>调整 patch&lt;/li>
&lt;/ol>
&lt;p>在 dde-nixos 中，编写了 getPatchFrom， replaceAll 等函数帮助 patch 硬编码路径，但打包时为上游添加函数是难以接受的，因此所有的 patch 都需要使用 substituteInPlace 重写：&lt;/p>
&lt;p>一个典型的例子是：https://github.com/NixOS/nixpkgs/pull/217806&lt;/p>
&lt;ol start="2">
&lt;li>改善对交叉编译&lt;/li>
&lt;/ol>
&lt;p>所有 deepin 启用 strictDeps，调整了 nativeBuildInputs 和 buildInputs 不规范的地方，（使用 qmake 的除外，会造成 qtwebengine 找不到，且上游已经不太关心 qmake）&lt;/p>
&lt;p>strictDeps 下无法传播 qtimageformats 问题 ，由 NickCao &lt;a href="https://github.com/NixOS/nixpkgs/pull/213926">解决&lt;/a>&lt;/p>
&lt;p>ps：可以使用 &lt;code>nix-build -A pkgsCross.riscv64.deepin.dtkcore&lt;/code> 尝试交叉编译。目前 x86_64 和 aarch64 可正常编译。&lt;/p></description><author>DDE 移植兴趣小组</author></item><item><title>obs 添加玲珑构建支持</title><link>https://wrj97.com/posts/obs-build-linglong/</link><pubDate>Fri, 07 Apr 2023 00:00:00 +0000</pubDate><guid isPermaLink="true">https://wrj97.com/posts/obs-build-linglong/</guid><description>&lt;p>&lt;a href="https://openbuildservice.org/">obs&lt;/a> 全称 Open Build Service，是一个开放的构建平台。相较于其他构建工具有以下优点：&lt;/p>
&lt;ul>
&lt;li>支持跨平台构建（x86、arm64 等）&lt;/li>
&lt;li>支持多种虚拟环境（kvm、lxc、chroot 等）&lt;/li>
&lt;li>支持软件包构建（deb、rpm、pkg 等）&lt;/li>
&lt;li>支持容器构建（flatpak、appimage、docker 等）&lt;/li>
&lt;li>支持发型版镜像构建（debian、windows 等）&lt;/li>
&lt;/ul>
&lt;p>&lt;a href="https://linglong.dev/">玲珑&lt;/a> 是一种新型的独立包管理工具集，致力于治理 Linux 系统下传统软件包格式复杂、交叉的依赖关系导致的各种兼容性问题，以及过于松散的权限管控导致的安全风险。&lt;/p>
&lt;p>本文介绍怎么给 obs 添加玲珑构建支持，供以参考实现你自己的 obs 构建服务。&lt;/p></description><author>myml</author></item><item><title>第一届小浣熊杯修 bug 大赛</title><link>https://blog.deepin.org/posts/the-1st-racoon-cup-bug-hunter-game/</link><pubDate>Thu, 09 Mar 2023 16:29:07 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/the-1st-racoon-cup-bug-hunter-game/</guid><description>&lt;p>deepin v23 beta 的发布在即，为了能够使相关的 bug 能够得以更快解决，并促进研发团队的协作变得更高效，我们（开源社区中心）决定在 deepin 员工内部举办小规模的 bug hunting 性质的比赛，并命名该比赛为“小浣熊杯修 bug 大赛”。而首届“小浣熊杯”也于昨天顺利落幕，那么就让我们一起了解一下这个比赛吧！&lt;/p>
&lt;h2 id="比赛介绍">比赛介绍
&lt;span>
&lt;a href="https://blog.deepin.org/posts/the-1st-racoon-cup-bug-hunter-game/#%e6%af%94%e8%b5%9b%e4%bb%8b%e7%bb%8d">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>如上所述，“小浣熊杯“的比赛大致内容即为在比赛时间内对已有缺陷进行修复。我们将所有参赛的员工划分为多个组，每个组除研发外也配备一个测试人员。在比赛时间周期内，研发从指定的缺陷看板中挑选自己”中意“的 BUG 进行修复，并在修复后将修复公布在相关群内，由 &lt;strong>其他组&lt;/strong> 的测试人员进行测试。当修复被测试人员验证没有问题后，即可进行计分。最终，会以本组研发人员所修复的数量与测试人员所完成的测试数量相组合，并计算小组人均得分，最后以小组人均得分的高低决定最终排名。&lt;/p>
&lt;p>小组得分的计算公示为：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">小组得分 = (本组研发修复的缺陷数量 + 本组测试所验证的缺陷数量 / 3) / 小组总人数
&lt;/code>&lt;/pre>&lt;/div>&lt;p>比赛会在 &lt;a href="https://github.com/linuxdeepin/.bug-game/">https://github.com/linuxdeepin/.bug-game/&lt;/a> 中进行，每次比赛会创建一个看板来跟进整个比赛的实时情况，并创建一个对应的 issue 记录相关进展与结果。&lt;/p>
&lt;p>另外，考虑到比赛过程中对缺陷的修复不需要经过其他研发人员的 code review ，因而可能存在实际的代码质量问题，故相关的 PR 均不要求在比赛结束前合入，相关提交仍需按照正常流程，经过有效 review 获得 approval 后合入。&lt;/p>
&lt;h2 id="首届状况介绍">首届状况介绍
&lt;span>
&lt;a href="https://blog.deepin.org/posts/the-1st-racoon-cup-bug-hunter-game/#%e9%a6%96%e5%b1%8a%e7%8a%b6%e5%86%b5%e4%bb%8b%e7%bb%8d">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>&lt;img alt="比赛过程看板截图" src="https://user-images.githubusercontent.com/13449038/223312494-6039b4a2-309a-44be-a8ac-61edffa9f963.png" />&lt;/p>
&lt;p>首届比赛共划分了四个小队参赛，比赛时间从 3 月 7 日开始，为期两天。比赛过程与结果在 &lt;a href="https://github.com/linuxdeepin/.bug-game/issues/1">这个 Issue&lt;/a> 中汇总，最终的缺陷修复情况也可以参见 &lt;a href="https://github.com/orgs/linuxdeepin/projects/26">这个看板&lt;/a>。比赛过程中“修 bug”队一度领先，随后被”进击的小浣熊“队反超，最终经过了两天的”激烈比拼“后，本次比赛总计处理了 42 个 Issue，由”进击的小浣熊“队以 18.333 分的总积分获得人均积分第一夺冠。“修 bug”队紧随其后，“呆呆鹅” 与 “bug 收割小分队” 获得随后的名次。&lt;/p>
&lt;p>比赛过程中，各个小组对已有 bug 的挑选与“占坑”以及测试人员对新提交修复的“抢单”是过程中最有趣的事情之一。快速挑选便于修复的 BUG 并进行有效的修复成为了获胜的关键之一，根据表象快速分析推测问题的能力，以及在陌生项目&lt;sup id="fnref:1">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/the-1st-racoon-cup-bug-hunter-game/#fn:1" role="doc-noteref">1&lt;/a>&lt;/sup>中快速尝试定位和修复问题的能力也变得至关重要。作为花絮，有的小组也在选择 BUG 的过程中连续发现自己所选择的缺陷实际早已在版本迭代中被修复，耗费了较多时间而造成了相对的失利，但这个过程也对现存 BUG 的有效性验证有很大的帮助&lt;sup id="fnref:2">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/the-1st-racoon-cup-bug-hunter-game/#fn:2" role="doc-noteref">2&lt;/a>&lt;/sup>。&lt;/p>
&lt;p>赛后，我们进行了比赛的颁奖，获胜队伍获得了比赛限定奖杯与荣誉证书，以及一个 deepin 主题背包。获奖队伍也进行了合影：&lt;/p>
&lt;p>&lt;img alt="荣誉证书" src="https://user-images.githubusercontent.com/10095765/224207416-e1fa516d-8993-4d1c-8d1f-81114c0766ce.png" />&lt;/p>
&lt;p>&lt;img alt="颁奖截图" src="https://user-images.githubusercontent.com/13449038/223911631-376c0d58-17a5-4401-843d-414dc287b7be.png" />&lt;/p>
&lt;p>无论是否获奖，我们都感谢各个参赛队伍的积极参与，也希望各位能在后续的比赛中能够获得优异的成绩。&lt;/p>
&lt;section class="footnotes" role="doc-endnotes">
&lt;hr />
&lt;ol>
&lt;li id="fn:1" role="doc-endnote">
&lt;p>注：缺陷的修复不限于自己所维护的项目，研发人员也可以尝试修复由其他项目组所维护的缺陷。&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/the-1st-racoon-cup-bug-hunter-game/#fnref:1" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2" role="doc-endnote">
&lt;p>或许在后续的比赛中应当为此类也算作计分项。&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/the-1st-racoon-cup-bug-hunter-game/#fnref:2" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/section></description><author>deepin 社区官方博客</author></item><item><title>关于 deepin 社区博客</title><link>https://blog.deepin.org/about/</link><pubDate>Tue, 14 Feb 2023 18:23:58 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/about/</guid><description>&lt;h2 id="deepin-社区博客">deepin 社区博客
&lt;span>
&lt;a href="https://blog.deepin.org/about/#deepin-%e7%a4%be%e5%8c%ba%e5%8d%9a%e5%ae%a2">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>deepin 社区公共博客平台。&lt;/p>
&lt;h2 id="用法说明">用法说明
&lt;span>
&lt;a href="https://blog.deepin.org/about/#%e7%94%a8%e6%b3%95%e8%af%b4%e6%98%8e">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>此博客平台使用了 &lt;a href="https://gohugo.io/">HUGO&lt;/a> 作为文章平台工具，进行博客文章的静态生成，并最终利用 GitHub Pages 呈现网页。故整个过程你只需按照 HUGO 的正常使用方式进行使用即可。&lt;/p>
&lt;p>若未安装 HUGO，您可以从 &lt;a href="https://github.com/gohugoio/hugo/releases">HUGO 官方 GitHub 仓库的 Release 页面&lt;/a>获取适用于您设备的 HUGO 版本，并放置其于 PATH 内以便调用。&lt;/p>
&lt;p>安装就绪后，大致常用命令如下（在此源码仓库的根目录下执行）：&lt;/p>
&lt;pre>&lt;code>$ hugo server # 启动一个本地服务器，预览目前状态下的网站内容
$ hugo new posts/my-post.md # 创建一篇新文章，以便进行编辑
$ hugo server -D # 启用本地服务器，并且能够预览状态为草稿（`draft: true`）的文章
$ hugo -D # 生成静态页面（如果需要），生成的文件将位于 public 目录下
&lt;/code>&lt;/pre>&lt;p>创建文章时，创建格式为 &lt;code>&amp;lt;分类&amp;gt;/&amp;lt;文件名&amp;gt;.&amp;lt;格式&amp;gt;&lt;/code>，上面给出的例子中，分类为 posts，文件名为 my-post，格式为 md （markdown）。创建文章后，默认会使用 &lt;a href="https://gohugo.io/content-management/front-matter/">YAML front-matter&lt;/a> 标记文章的一些元信息，请留意 draft 草稿状态的文章最终不会显示。&lt;/p>
&lt;p>若要获取 HUGO，请参考&lt;a href="https://gohugo.io/getting-started/installing">官方文档给出的安装方式&lt;/a>。&lt;/p>
&lt;h2 id="文章内容指导建议">文章内容指导建议
&lt;span>
&lt;a href="https://blog.deepin.org/about/#%e6%96%87%e7%ab%a0%e5%86%85%e5%ae%b9%e6%8c%87%e5%af%bc%e5%bb%ba%e8%ae%ae">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>向此博客平台投递文章需要经过相关人员的 review，合入后即投递完成。需要注意的是，向此处投递的文章必须是与 deepin 社区发展所相关的文章。对于与 deepin 开源社区贡献相关的个人感想或随笔等博客，可以考虑发布到 planet.deepin.org 聚合平台，此博客平台的文章也会出现 planet.deepin.org 之中。&lt;/p>
&lt;p>在发布您的文章时，建议使用标签来标记您文章所相关的主题，以便读者更方便的查阅您的文章。例如 &lt;code>tags: [&amp;quot;持续集成&amp;quot;]&lt;/code> 或 &lt;code>tags: [&amp;quot;指南文档&amp;quot;, &amp;quot;CMake&amp;quot;]&lt;/code>。另外也建议在文章的元信息中附带作者信息，例如 &lt;code>authors: [&amp;quot;张三&amp;quot;]&lt;/code>（可为多人）。&lt;/p>
&lt;h2 id="改善此项目的样式风格">改善此项目的样式风格
&lt;span>
&lt;a href="https://blog.deepin.org/about/#%e6%94%b9%e5%96%84%e6%ad%a4%e9%a1%b9%e7%9b%ae%e7%9a%84%e6%a0%b7%e5%bc%8f%e9%a3%8e%e6%a0%bc">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>此项目使用了 &lt;a href="https://themes.gohugo.io/hugo-geekblog/">geekblog&lt;/a> 主题（基于 &lt;code>v0.5.3&lt;/code>，有改动）。若你有任何建议，可以进行讨论并修改或提供更适合的主题。&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>deepin-riscv64构建环境搭建 - Zeno-sole</title><link>https://www.cnblogs.com/zeno404/p/17089158.html</link><pubDate>Fri, 03 Feb 2023 09:25:00 +0000</pubDate><guid isPermaLink="true">https://www.cnblogs.com/zeno404/p/17089158.html</guid><description>【摘要】pbuilder + qemu 以下操作基于debian11环境,其他环境下需注意qemu版本是否在5.2版本以上 sudo apt install pbuilder qemu qemu-user-static pigz 配置 /etc/pbuilderrc MIRRORSITE=https://m &lt;a href="https://www.cnblogs.com/zeno404/p/17089158.html" target="_blank">阅读全文&lt;/a></description><author>zeno</author></item><item><title>DDE v23 beta 3 移植简要指南</title><link>https://deepin-community.github.io/sig-dde-porting/posts/dde-v23beta3-porting-guide/?utm_source=atom_feed</link><pubDate>Thu, 02 Feb 2023 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/dde-v23beta3-porting-guide/</guid><description>&lt;p>&lt;em>编辑的话请把自己的名字加到作者名单里&lt;/em>&lt;/p>
&lt;p>DDE v23 beta3 即将随 deepin v23 beta 3 发布（你阅读到这个文章的时候可能已经发布了）。为了方便各个其它发行版的包维护者可以更方便的移植 DDE 到对应的发行版，这里提供一篇简要的移植指南，用以描述常见的移植问题和解决方案。&lt;/p>
&lt;blockquote>
&lt;p>下面对项目名称的称呼均以 GitHub 对应的原始仓库名为准。
&amp;#123;.note}&lt;/p>&lt;/blockquote>
&lt;h2 id="概览">概览&lt;/h2>
&lt;p>DDE 此次 beta2 -&amp;gt; beta3 的更新中，dde-application-manager 进行了大规模重构；dde-launchpad 取代 dde-launcher 成为了新的启动器/开始菜单应用；新的技术预览项目 dde-shell 和 treeland 也随此次发布提供了初步版本，以及为了服务 dde-launcher 和 dde-shell，dtk 也开始提供 Qt 6 版本。&lt;/p>
&lt;p>由于这些项目的版本间互相影响，我们建议移植人员参照 deepin v23 beta3 所使用的包版本进行打包（随后会把完整的版本参照列表贴到这里），下面会对主要的部分进行详细说明。&lt;/p>
&lt;h2 id="主要组件">主要组件&lt;/h2>
&lt;h3 id="dtk-与-dtk6">DTK 与 DTK6&lt;/h3>
&lt;p>作为 DDE 组件与应用的基础依赖，DTK 现开始提供 Qt 6 支持。适用于 beta 3 的版本参照如下：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>package&lt;/th>
&lt;th>version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>dtkcommon&lt;/td>
&lt;td>5.6.21&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkcore&lt;/td>
&lt;td>5.6.22&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkgui&lt;/td>
&lt;td>5.6.22&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkwidget&lt;/td>
&lt;td>5.6.22&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtkdeclarative&lt;/td>
&lt;td>5.6.24&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5integration&lt;/td>
&lt;td>5.6.20&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt5platform-plugins&lt;/td>
&lt;td>5.6.22&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6core&lt;/td>
&lt;td>6.0.4&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6gui&lt;/td>
&lt;td>6.0.5&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6widget&lt;/td>
&lt;td>6.0.4&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>dtk6declarative&lt;/td>
&lt;td>6.0.7&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6integration&lt;/td>
&lt;td>6.0.4&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>qt6platform-plugins&lt;/td>
&lt;td>6.0.4&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>由于 dtkdeclarative 和 dtkgui 存在一些发布前的紧急修复，所以这两个组件存在版本号未对齐的情况。请直接参照上述表格的版本进行打包，以便确保 dtk 自身互相依赖的版本无误。&lt;/p>
&lt;p>关于 qt5platform-plugins，现有的 dwayland 插件可能对非 DDE 环境（例如 KDE）的 wayland 用户存在影响，可参照 &lt;a href="https://github.com/linuxdeepin/developer-center/issues/7217">linuxdeepin/developer-center#7217&lt;/a> 打对应的 patch 规避影响。&lt;/p>
&lt;p>dtk6 曾对 Qt 6.2, Qt 6.4 和 Qt 6.6 均进行过适配，但我们目前的研发与测试主要使用 Qt 6.6 版本，故我们建议使用尽可能新的 Qt 6 版本。&lt;/p>
&lt;p>目前，使用 dtk6 的正式组件有 dde-application-manager 以及 dde-launchpad，技术预览组件有 dde-shell, treeland。&lt;/p>
&lt;h3 id="dde-application-manager">dde-application-manager&lt;/h3>
&lt;p>dde-application-manager 自 1.1.0 版本起进行了完整重构（此次随 beta 3 发布的版本是 1.1.8），接口存在完全不兼容的变动。故原本依赖重构前 dde-application-manager 的应用程序和组件均需要进行更新。比较典型的组件为 dde-dock 与 dde-file-manager。&lt;/p>
&lt;h3 id="dde-launchpad">dde-launchpad&lt;/h3>
&lt;p>dde-launchpad 取代了 dde-launcher，原 dde-launcher 由于大量缺陷以及对旧 dde-application-manager 的强依赖，已不再可用。如果您的发行版在使用 dde-launcher，现在可以废弃 dde-launcher 并使用 dde-launchpad 替代了。&lt;/p>
&lt;p>尽管在 deepin 中，dde-launchpad 使用的是 Qt 6 构建的版本，dde-launchpad 本身仍然提供了选项来控制使用 Qt 5 或是 Qt 6。我们建议在允许的情况下尽可能使用 Qt 6 而不是 Qt 5，以及无论 Qt 5 或是 Qt 6 支持，请都不要忘记安装 qt5/6integration 插件以及 qt5/6platform-plugins，否则 dde-launchpad 可能会缺失圆角与模糊效果。同样，强烈建议配合最新 DTK 版本构建来获得最佳显示效果。&lt;/p>
&lt;p>另外，随 dde-launchpad 一并发布了 dde-application-wizard 项目。此项目是一个守护进程，提供对旧的 dde-application-manager 所提供的卸载服务接口的兼容支持，但可被移植到支持 packagekit 的发行版，故您可以考虑打包移植此组件。如果您的发行版不支持 packagekit，则可考虑暂时 patch dde-launchpad 来隐藏菜单中的卸载按钮，后续 dde-launchpad 会提供 DConfig 来便于发行版定制这些菜单项。&lt;/p>
&lt;h2 id="技术预览组件">技术预览组件&lt;/h2>
&lt;p>技术预览阶段的组件均可酌情决定是否打包。&lt;/p>
&lt;h3 id="dde-shell">dde-shell&lt;/h3>
&lt;p>dde-shell 目前是技术预览阶段的项目。dde-shell 旨在将 DDE 桌面环境插件化与模块化，降低开发难度，使各个组件的替换变得更加容易，并且提供更好的桌面环境集成支持。&lt;/p>
&lt;p>dde-shell 目前包含 shell 本体，以及预制的新 dock 插件和通知中心插件。&lt;/p>
&lt;h3 id="treeland--ddm">treeland / ddm&lt;/h3>
&lt;p>treeland 目前是技术预览阶段的项目，兼顾 Wayland 窗口合成器和显示管理功能。treeland 除需要 dtk6declarative 外，还依赖新项目包括 &lt;a href="https://github.com/vioken/waylib">waylib&lt;/a> 和 &lt;a href="https://github.com/vioken/qwlroots">qwlroots&lt;/a>。需要使用 Qt 版本 &amp;gt;= 6.6.0， wlroots 版本 &amp;gt;= 0.17.0 编译 qwlroots/waylib。&lt;/p>
&lt;p>同时我们放弃了对 deepin-kwin wayland 的支持（更新 dde-session 后会屏蔽入口），DDE 所有 wayland 相关的支持均由 Treeland 提供。&lt;/p>
&lt;h3 id="deepin-im">deepin-im&lt;/h3>
&lt;p>treeland 目前是技术预览阶段的项目，提供了输入法的抽象层。目前，deepin-im 仅期望在 treeland 环境下使用。它会设置 &lt;code>QT_IM_MODULE&lt;/code> 等环境变量的值来影响实际使用的输入法模块。&lt;/p>
&lt;h2 id="获取移植帮助">获取移植帮助&lt;/h2>
&lt;p>如果您希望得到移植相关的帮助，请考虑加入我们 DDE 移植小组的在线交流群（下列房间有桥接，任选其一即可），一起展开相关的交流：&lt;/p>
&lt;ul>
&lt;li>Matrix 群：&lt;code>#dde-port:deepin.org&lt;/code>&lt;/li>
&lt;li>Telegram 群：&lt;a href="https://t.me/ddeport">https://t.me/ddeport&lt;/a>&lt;/li>
&lt;/ul></description><author>DDE 移植兴趣小组</author></item><item><title>dde-nixos 2023 年 1 月成果展示</title><link>https://deepin-community.github.io/sig-dde-porting/posts/dde-nixos-2023-01-31/?utm_source=atom_feed</link><pubDate>Mon, 30 Jan 2023 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/dde-nixos-2023-01-31/</guid><description>&lt;p>本月对 NixOS DDE 做了进一步完善，已经比较适合在实体机上使用了。&lt;/p>
&lt;p>现在 deepin v23 的版本即将发布，github 大部分 DDE 软件已经升级到了 23 版本，由于 20 和 23 版本不完全兼容，dde-nixos 将使用 v20 分支继续维护/测试 v20 版本的 DDE, main 分支尝试 v23 版本。&lt;/p>
&lt;p>目前的主要工作是将 v20 版本的移植工作转移到上游，方便更多用户使用。同时 review 机制也可以找到并处理现有写法的不规范之处。Nixpkgs 合并进程请关注：https://github.com/linuxdeepin/dde-nixos/issues/9&lt;/p>
&lt;p>目前已经有一部分应用可在&lt;a href="https://search.nixos.org/packages?channel=unstable&amp;amp;from=0&amp;amp;size=50&amp;amp;sort=relevance&amp;amp;type=packages&amp;amp;query=deepin">官方仓库下载&lt;/a>。&lt;/p>
&lt;p>此外 @SamLukeYes 构建了 NixOS DDE 的 iso，可以直接使用： &lt;a href="https://github.com/SamLukeYes/nixos-dde-iso/releases/tag/22.11.20230113">https://github.com/SamLukeYes/nixos-dde-iso/releases/tag/22.11.20230113&lt;/a>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>dtk&lt;/p>
&lt;ul>
&lt;li>统一升级到 5.6.3&lt;/li>
&lt;li>qt5integration 使用 5.6.4 ：修复通知中心图标缺失问题/修复 log 中大量 &lt;code>create icon [] engine failed.[theme:]&lt;/code> 问题&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>dde-control-center:&lt;/p>
&lt;ul>
&lt;li>禁用系统版权协议模块（仅适配deepin/uos）&lt;/li>
&lt;li>(dde-api) 修复无法识别本机语言的问题&lt;/li>
&lt;li>修改语言（locale）功能在 NixOS 中无法实现，属于正常现象&lt;/li>
&lt;li>适配系统版权信息（通过 /etc/deepin-installer.conf 配置实现）&lt;/li>
&lt;li>修复明暗主题，图标光标主题缺失（go-lib isDir 判断错误）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>dde-daemon&lt;/p>
&lt;ul>
&lt;li>修复 nix 的 wrapped 应用后无法通过 verifyExe 校验问题&lt;/li>
&lt;li>清理无用的硬编码 patch&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>deepin-kwin 相关软件：&lt;/p>
&lt;ul>
&lt;li>已经切换至 linuxdeepin 仓库的 tag 版本&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>dde-account-faces&lt;/p>
&lt;ul>
&lt;li>/var 路径文件改用 systemd.tmpfiles 模块管理&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>deepin-system-monitor&lt;/p>
&lt;ul>
&lt;li>修复 dock 插件显示问题&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>配置 Garnix CI&lt;/p>
&lt;ul>
&lt;li>通过 Garnix 自动构建 dde-nixos 的软件，支持 x86 和 arm&lt;/li>
&lt;li>使用 Garnix 提供的 binary caches，可以无需编译即可使用 DDE, 用法见项目 readme&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>gio-qt&lt;/p>
&lt;ul>
&lt;li>修复文档编译失败&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>deepin-boot-maker&lt;/p>
&lt;ul>
&lt;li>修复多处硬编码路径，修复无法识别 u 盘的问题&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>dpa-ext-gnomekeyring&lt;/p>
&lt;ul>
&lt;li>修复硬编码&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>deepin-font-manager&lt;/p>
&lt;ul>
&lt;li>修复系统字体识别错误&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>dde-file-manager&lt;/p>
&lt;ul>
&lt;li>修复新版空格预览崩溃&lt;/li>
&lt;li>处理插件硬编码路径&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>dde-app-services&lt;/p>
&lt;ul>
&lt;li>修复 GUI 编辑器无法读取其他应用配置问题&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>dde-network-core&lt;/p>
&lt;ul>
&lt;li>修复控制中心插件翻译&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>其他：&lt;/p>
&lt;ul>
&lt;li>新增 security.pam.services.dde-lock， 代替 dde-lock 所使用的 common-auth&lt;/li>
&lt;li>新增 deepin-orc 支持&lt;/li>
&lt;li>修复对 deepin-anything 的支持&lt;/li>
&lt;li>默认安装 onboard， dock 插件使用&lt;/li>
&lt;li>处理 updateDbusEnvironment 未生效的问题：https://github.com/NixOS/nixpkgs/issues/209847&lt;/li>
&lt;li>清理 dde 模块中非必要启用的 services&lt;/li>
&lt;li>优化 nix 函数结构，dtk 不再使用列表，改用 propagatedBuildInputs&lt;/li>
&lt;/ul>
&lt;h3 id="目前已知的问题">目前已知的问题：&lt;/h3>
&lt;h5 id="qt-应用启动器启动不是deepin主题但通过-deepin-terminal-启动是">qt 应用启动器启动不是deepin主题，但通过 deepin-terminal 启动是：&lt;/h5>
&lt;blockquote>
&lt;p>qt5integration 未全局安装，安装后虽然 qt 主题无问题，但是会导致启动器缺失图标等问题。建议 qt 继续使用 breeze 主题&lt;/p>&lt;/blockquote>
&lt;h5 id="lightdm-不是-deepin-主题">lightdm 不是 deepin 主题&lt;/h5>
&lt;blockquote>
&lt;p>目前 deepin-greeter 无法正常使用，不过使用其他主题或者用 sddm 也是可以启动 DDE 的。&lt;/p>&lt;/blockquote>
&lt;h4 id="窗口模糊特效缺失">窗口模糊特效缺失&lt;/h4>
&lt;blockquote>
&lt;p>deepin-kwin 的问题，等待修复。通过控制中心关闭再打开特效，可以临时解决。&lt;/p>&lt;/blockquote></description><author>DDE 移植兴趣小组</author></item><item><title>Snippet</title><link>https://deepin-community.github.io/sig-deepin-doc-doc-go/posts/snippet/snippet/?utm_source=atom_feed</link><pubDate>Tue, 27 Dec 2022 01:24:05 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-doc-doc-go/posts/snippet/snippet/</guid><description>&lt;h1 id="vscode-snipets-用户代码片段使用指南">vscode snipets 用户代码片段使用指南&lt;/h1>
&lt;h2 id="snipets简介">snipets简介&lt;/h2>
&lt;p>snipets 我愿意称为程序员的ctrl+c ctrl+v最大的竞争对手，sniptes适用于遇到很多重复代码却又没法去提取代码特点抽象成类的情况。比如写注释的时候。&lt;/p>
&lt;p>这是一个极好的工具用于高效率&lt;del>办公&lt;/del>（摸鱼）&lt;/p>
&lt;h2 id="vscode-打开snipets">vscode 打开snipets&lt;/h2>
&lt;p>&lt;img alt="" src="https://deepin-community.github.io/sig-deepin-doc-doc-go/snippets.png" />&lt;/p>
&lt;p>然后你就需要选择对应的语言，这里我以doxygen注释文件为例子：
打开之后会得到这样一个文件：&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-jasonc" data-lang="jasonc">&amp;#123;
// Place your snippets for doxygen here. Each snippet is defined under a snippet name and has a prefix, body and
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and $&amp;#123;1:label}, $&amp;#123;2:another} for placeholders. Placeholders with the
// same ids are connected.
// Example:
// "Print to console": &amp;#123;
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }
}
&lt;/code>&lt;/pre>&lt;p>这个文件都是注释组成，自然也没有任何功能，我们要做的是对其进行修改，以支持我们自己的代码片段&lt;/p>
&lt;h2 id="代码片段的解读">代码片段的解读&lt;/h2>
&lt;p>先以一个比较简单的例子作为内容：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>&amp;#123;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Place your snippets for doxygen here. Each snippet is defined under a snippet name and has a prefix, body and
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// $1, $2 for tab stops, $0 for the final cursor position, and $&amp;#123;1:label}, $&amp;#123;2:another} for placeholders. Placeholders with the
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// same ids are connected.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// Example:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// "Print to console": &amp;#123;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// "prefix": "log",
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// "body": [
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// "console.log('$1');",
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// "$2"
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// ],
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// "description": "Log output to console"
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#f92672">"dox dtkwidget"&lt;/span>: &amp;#123;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"prefix"&lt;/span>: &lt;span style="color:#e6db74">"da"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"body"&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">"@fn void Dtk::Widget::DAlertControl::$&amp;#123;2:function}"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">"@brief $&amp;#123;3:info}"&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们来逐行解释：dox dtkwidget指的是这个代码片段的命名，你可以使用任何你喜欢的命名来代替它&lt;/p>
&lt;p>然后就是一个键值对 &amp;ldquo;prefix&amp;rdquo; 的值一般是一个字符串，这个字符串也是可以自定义的，含义就是你在代码文件里面输入这个字符串（在某一行的开头）后会弹出选项询问你是否使用代码片段：比如这个就是da,你在某一行的开头打出da这俩字符，就会弹窗询问你是否替换&lt;/p>
&lt;p>&lt;img alt="" src="https://deepin-community.github.io/sig-deepin-doc-doc-go/snippets2.png" />&lt;/p>
&lt;p>同时会显示对于代码片段的命名&lt;/p>
&lt;p>body键值对就是最重要的一部分，这个里面就保存的你需要的代码片段，&lt;/p>
&lt;p>请注意，其中每一行都代表的是你之后生成的代码的一行，如上代码片段生成的代码就是&lt;/p>
&lt;pre tabindex="0">&lt;code>@fn void Dtk::Widget::DAlertControl::function
@brief info
&lt;/code>&lt;/pre>&lt;p>如果你现在已经复制了我的代码片段，不妨新建一个dox文件试试。打出da之后会弹出选项，按tab键确认之后就会出现这个代码，不过会让你填写function的值，填写完毕之后就按tab键切换到info需要你填写info的值&lt;/p>
&lt;p>不过这样还不是代码片段的全部实力，它还能更强大点：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">"dox about dtkwidget"&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> &amp;#123;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"prefix"&lt;/span>: &lt;span style="color:#e6db74">"dd"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">"body"&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">"@property $&amp;#123;1|QColor,void,bool|} Dtk::Widget::DAlertControl::$&amp;#123;2:function}"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">"@brief 返回$&amp;#123;3:info}"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">""&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">"@fn void Dtk::Widget::DAlertControl::set$&amp;#123;2/(.*)/$&amp;#123;1:/capitalize}/i}"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">"@brief 设置$&amp;#123;3:info}"&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">"@sa DAboutDialog::$&amp;#123;2:function}"&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>比如这样&lt;/p>
&lt;p>你可以自己试试这个会发生什么&lt;/p>
&lt;p>对的 它还支持正则表达式的替换&lt;/p>
&lt;p>现在看看我正在用的doxygen的代码片段&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-jsonc" data-lang="jsonc">&amp;#123;
"dox about DtkGui": &amp;#123;
"prefix": "dd",
"body": [
"@property Dtk::Gui::DDciIconPalette::$&amp;#123;2:function}",
"@brief $&amp;#123;2}属性",
"@sa READ方法: void Dtk::Widget::DDciIconPalette::$&amp;#123;2:function}",
"@sa WRITE方法: void Dtk::Widget::DDciIconPalette::set$&amp;#123;2/(.*)/$&amp;#123;1:/capitalize}/i}",
"",
"@fn $&amp;#123;1|QColor,void,bool|} Dtk::Widget::DDciIconPalette::$&amp;#123;2:function}",
"@brief 返回$&amp;#123;3:info}",
"@sa DDciIconPalette::$&amp;#123;2:function}",
"",
"@fn void Dtk::Widget::DDciIconPalette::set$&amp;#123;2/(.*)/$&amp;#123;1:/capitalize}/i}",
"@brief 设置$&amp;#123;3:info}",
"@sa DDciIconPalette::$&amp;#123;2:function}"
]
},
"dox dtkwidget": &amp;#123;
"prefix": "da",
"body": [
"@fn void Dtk::Widget::DDciIconPalette::$&amp;#123;2:function}",
"@brief $&amp;#123;3:info}"
]
}
}
&lt;/code>&lt;/pre>&lt;p>我可以通过这个代码片段一键生成属性的get和set方法的doxygen注释&lt;/p></description><author>deepin 文档小组</author></item><item><title>DDE 移植小组成果展示</title><link>https://deepin-community.github.io/sig-dde-porting/posts/dde-nixos-2022-12/?utm_source=atom_feed</link><pubDate>Mon, 12 Dec 2022 00:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/dde-nixos-2022-12/</guid><description>&lt;p>自小组成立以来，已经为 DDE 的移植做出了很多贡献，本文做出了一些总结。如有补充或批改，可向 &lt;a href="https://github.com/deepin-community/sig-dde-porting">sig-dde-porting&lt;/a> 提出 pr。&lt;/p>
&lt;p>NixOS 的移植从今年 3 月开始，到现在已经有了实用的可能，今后会用一篇文章单独介绍。未来会每月更新进展，关注的同学可以订阅本站 rss。&lt;/p>
&lt;h3 id="今年改善可移植性的一些努力">今年改善可移植性的一些努力&lt;/h3>
&lt;ul>
&lt;li>修复依赖特定 kwayland 无法在其他发行版编译的问题（为项目增加禁用wayland的option）：&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>deepin-system-monitor，dde-clipboard，dde-session-shell&amp;hellip;&lt;/p>&lt;/blockquote>
&lt;ul>
&lt;li>修复安装路径硬编码问题&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>29 个相关 pr &lt;a href="https://github.com/linuxdeepin/developer-center/issues/3167">https://github.com/linuxdeepin/developer-center/issues/3167&lt;/a>&lt;/p>&lt;/blockquote>
&lt;ul>
&lt;li>减少代码中的硬编码问题&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>16 个相关 pr &lt;a href="https://github.com/linuxdeepin/developer-center/issues/3374">https://github.com/linuxdeepin/developer-center/issues/3374&lt;/a>&lt;/p>&lt;/blockquote>
&lt;ul>
&lt;li>编译 flag 直接覆盖的问题&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>大约 8 处&lt;/p>&lt;/blockquote>
&lt;ul>
&lt;li>修复 as-need 参数 break 了部分连接器:&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>10 个相关 pr &lt;a href="https://github.com/linuxdeepin/developer-center/issues/3345">https://github.com/linuxdeepin/developer-center/issues/3345&lt;/a>&lt;/p>&lt;/blockquote>
&lt;ul>
&lt;li>完善项目关于 pkg-config/Config.cmake 的问题(如写死路径，版本号错误，引入依赖检查不完善等等)&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>至少 17 个相关 pr，事实上应该远多于 17，存在多个修改顺便提在一个 pr 的情况。&lt;/p>&lt;/blockquote>
&lt;ul>
&lt;li>使用 dpkg-architecture 判断架构其他发行版无法使用问题&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>4 处以上&lt;/p>&lt;/blockquote>
&lt;ul>
&lt;li>避免绝对路径头文件：&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>解决 2 处，但仍有 1 处难以处理（https://github.com/linuxdeepin/dde-session-shell/pull/120）&lt;/p>&lt;/blockquote>
&lt;ul>
&lt;li>dde-kwin 问题&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>&lt;a href="https://github.com/justforlxz">justforlxz&lt;/a> 已经多次修复 dde-kwin 对 kwin 的适配问题，但每次 kwin 每次更新都会重新带来很多问题。正在移植中的 deepin-kwin 将会解决这个麻烦，目前可以使用 Arch（aur）或者 dde-nixos 先行体验。&lt;/p>&lt;/blockquote>
&lt;ul>
&lt;li>处理 deepin-wallpapers 非自由协议问题（拆包）&lt;/li>
&lt;/ul>
&lt;h3 id="dde-nixos-近期进展">dde-nixos 近期进展&lt;/h3>
&lt;ul>
&lt;li>
&lt;p>dtkcore：&lt;/p>
&lt;ul>
&lt;li>其他发行版调用 uosType 直接返回 UosTypeUnknown，避免无意义的报错刷屏&lt;/li>
&lt;li>完善 isDDE 函数判断&lt;/li>
&lt;li>deepin-os-release 判断是其他发行版后不会输出 deepin/uos 特有参数&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>dde-control-center:&lt;/p>
&lt;ul>
&lt;li>提供禁用生物认证模块的 option&lt;/li>
&lt;li>修复无法设置头像问题&lt;/li>
&lt;li>修复密码校验无法通过的问题&lt;/li>
&lt;li>修复系统信息/关于本机的显示问题：
&lt;ul>
&lt;li>启用不应隐藏的“计算机名”，“产品名称”&lt;/li>
&lt;li>隐藏“版本”（社区版/专业版）&lt;/li>
&lt;li>替换为 NixOS 的 logo&lt;/li>
&lt;li>隐藏修改 “计算机名” 的功能&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>隐藏用户体验模块，禁用对应 dbus 调用，修复通用模块卡钝问题&lt;/li>
&lt;li>修复显示内存为 0 的问题&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>deepin-movie:&lt;/p>
&lt;ul>
&lt;li>修复格式支持不完整的问题（修复 libPath 硬编码）&lt;/li>
&lt;li>修复一个崩溃问题&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>dde-grand-search:&lt;/p>
&lt;ul>
&lt;li>修复调用 dde-grand-search-daemon 的权限验证&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>deepin-screen-recorder：&lt;/p>
&lt;ul>
&lt;li>修复2个导致崩溃的问题&lt;/li>
&lt;li>修复命令行启动无法录屏的问题（dock插件仍然有此问题）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>修复 GStreamer 音频播放：&lt;/p>
&lt;ul>
&lt;li>需要设置依赖以及 GST_PLUGIN_SYSTEM_PATH_1_0&lt;/li>
&lt;li>涉及 deepin-music deepin-voice-note deepin-movie 和 dde-introduction&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>deepin-editor：&lt;/p>
&lt;ul>
&lt;li>修复主题路径硬编码造成的界面异常&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>deepin-compressor:&lt;/p>
&lt;ul>
&lt;li>修复无法加载自身插件的问题&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>修复个别图标丢失的问题（部分 dci 图标需要 qtimageformats 支持）&lt;/p>
&lt;/li>
&lt;/ul></description><author>DDE 移植兴趣小组</author></item><item><title>伟大的第一步</title><link>https://deepin-community.github.io/sig-deepin-m1/posts/2022-09-22/?utm_source=atom_feed</link><pubDate>Thu, 22 Sep 2022 07:46:56 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-m1/posts/2022-09-22/</guid><description>&lt;p>这是第一篇 deepin-m1 sig 的博客，我非常的激动，因为 deepin 现在已经可以在 m1 设备上启动了。&lt;/p>
&lt;p>目前在 m1 上启动 Linux 的项目进展最快的是 Asahi Linux，他们是 Linux m1 的项目发起者，同时也是 Asahi Linux 的维护者，目前 Asahi Lina 正在努力开发 m1 GPU 驱动，已经实现了绝大部分底层的功能，正在做渲染队列的工作，相信很快就可以为 Linux 带来开源的 m1 GPU 驱动。&lt;/p>
&lt;p>debian 和 fedora 也基于 Asahi installer 制作了发行版本，我们的工作也是从这里开始的。&lt;/p>
&lt;p>经过分析，发现 Asahi installer 已经预留了更换 rootfs 的接口，这使得我们的工作异常顺利，很快便完成了第一版的安装文件。&lt;/p>
&lt;p>&lt;img alt="bootloader" src="https://deepin-community.github.io/sig-deepin-m1/bootloader.jpg" />&lt;/p>
&lt;p>&lt;img alt="start" src="https://deepin-community.github.io/sig-deepin-m1/start.jpg" />&lt;/p>
&lt;p>目前 m1 启动 Linux 的流程是先启动 Asahi m1n1，由 m1n1 启动 u-boot，再由 u-boot 启动 grub。&lt;/p>
&lt;blockquote>
&lt;p>m1n1 是一个非常不错的项目，可以通过 usb 进行串口调试，方便进行驱动逆向和调试，也可以测试启动 elf。&lt;/p>
&lt;/blockquote>
&lt;p>在 m1 上安装 Linux，需要先进入一次 macOS 的恢复模式，利用恢复模式将文件内容写入，目前这已经是最好的解决方案了，目前小组正在整理相关文档和脚本，预计下个月可以对外提供正式的安装脚本和视频教程。还有很多功能没有准备好，例如一些 x86 软件无法运行，m1 只支持 16k 页内存等，可能会带来很多软件的兼容性问题，我们也欢迎更多的人加入 deepin-m1 sig 组中，提供更多的帮助。&lt;/p>
&lt;p>enjoy live!&lt;/p></description><author>deepin M1 适配小组</author></item><item><title>deepin doc doc go 小组现已建立！</title><link>https://deepin-community.github.io/sig-deepin-doc-doc-go/posts/hello-world/?utm_source=atom_feed</link><pubDate>Fri, 16 Sep 2022 06:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-doc-doc-go/posts/hello-world/</guid><description>&lt;p>这是一个模板文章。各个 SIG 在实际使用此博客模板时可根据实际需求对此模板进行任何调整。&lt;/p>
&lt;p>我们要求 deepin 社区中的小组每季度至少能有一到两次对公众公开的小组成果展示，用以分享成果，使得社区大众均能受益。通过使用此博客模板，SIG 即可直接在自己的小组博客站点中提供小组成果的相应博客介绍，而 SIG 委员会以及其它感兴趣的小组即可直接利用 RSS 订阅来获得相应的内容更新。&lt;/p>
&lt;p>对于成果公示类的文章，您即可通过“成果公示”或类似的标签来标记文章，这样，在此标签内的文档就会被生成到同一标签类别的 RSS 之中，以供订阅了。&lt;/p></description><author>deepin 文档小组</author></item><item><title>XXXX 小组现已建立！</title><link>https://deepin-community.github.io/sig-deepin-virt/posts/hello-world/?utm_source=atom_feed</link><pubDate>Fri, 16 Sep 2022 06:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-virt/posts/hello-world/</guid><description>&lt;p>这是一个模板文章。各个 SIG 在实际使用此博客模板时可根据实际需求对此模板进行任何调整。&lt;/p>
&lt;p>我们要求 deepin 社区中的小组每季度至少能有一到两次对公众公开的小组成果展示，用以分享成果，使得社区大众均能受益。通过使用此博客模板，SIG 即可直接在自己的小组博客站点中提供小组成果的相应博客介绍，而 SIG 委员会以及其它感兴趣的小组即可直接利用 RSS 订阅来获得相应的内容更新。&lt;/p>
&lt;p>对于成果公示类的文章，您即可通过“成果公示”或类似的标签来标记文章，这样，在此标签内的文档就会被生成到同一标签类别的 RSS 之中，以供订阅了。&lt;/p></description><author>Deepin desktop virtualization SIG</author></item><item><title>XXXX 小组现已建立！</title><link>https://deepin-community.github.io/sig-deepin-cicd/posts/hello-world/?utm_source=atom_feed</link><pubDate>Fri, 16 Sep 2022 06:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-deepin-cicd/posts/hello-world/</guid><description>&lt;p>这是一个模板文章。各个 SIG 在实际使用此博客模板时可根据实际需求对此模板进行任何调整。&lt;/p>
&lt;p>我们要求 deepin 社区中的小组每季度至少能有一到两次对公众公开的小组成果展示，用以分享成果，使得社区大众均能受益。通过使用此博客模板，SIG 即可直接在自己的小组博客站点中提供小组成果的相应博客介绍，而 SIG 委员会以及其它感兴趣的小组即可直接利用 RSS 订阅来获得相应的内容更新。&lt;/p>
&lt;p>对于成果公示类的文章，您即可通过“成果公示”或类似的标签来标记文章，这样，在此标签内的文档就会被生成到同一标签类别的 RSS 之中，以供订阅了。&lt;/p></description><author>Deepin system cicd SIG</author></item><item><title>规范导出和使用 Config.cmake 文件</title><link>https://deepin-community.github.io/sig-dde-porting/posts/find_package/?utm_source=atom_feed</link><pubDate>Fri, 16 Sep 2022 06:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/find_package/</guid><description>&lt;h2 id="生成-configcmake-文件">生成 Config.cmake 文件&lt;/h2>
&lt;p>生成 Config.cmake 文件路径要求&lt;a href="https://deepin-community.github.io/sig-dde-porting/(https:/deepin-community.github.io/sig-dde-porting/posts/pkg_config/)">与 pkg-config 一致&lt;/a>&lt;/p>
&lt;h4 id="应该提供-fooconfigcmakein-使用-full-版本的-gnuinstalldirs-变量替换路径">应该提供 FooConfig.cmake.in 使用 FULL 版本的 GNUInstallDirs 变量替换路径&lt;/h4>
&lt;p>相关修改：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/dde-dock/pull/556/commits/6185843e8ed93c9d22f9921aeefcfa0e73f4f351">use configure_file set path in DdeDockConfig.cmake&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="使用-find_dependency-代替-find_package">使用 find_dependency 代替 find_package&lt;/h4>
&lt;p>cmake 官方提供了 CMakeFindDependencyMacro 模块，专门用在 Config.cmake 文件中，find_dependency 和 find_package 用法完全相同，因此可以简单的把原来的 find_package 替换成 find_dependency。&lt;/p>
&lt;p>find_dependency 的优点是如果 A 的 Config.cmake 寻找 B 的，如果失败 find_package 只会提示 B 的错误，而 find_dependency 还在报错中输出调用链，清晰显示是谁在找 B。&lt;/p>
&lt;p>find_dependency 应该在 set 路径之后。&lt;/p>
&lt;p>与 pkg-config 的 Requires 类似，这里只 find 传播的构建依赖。&lt;/p>
&lt;h4 id="使用-configure_package_config_file-代替-configure_file">使用 configure_package_config_file 代替 configure_file&lt;/h4>
&lt;p>使用 FULL 版本的 GNUInstallDirs 变量虽然正确，但还有一个缺陷，生成的是绝对路径，库必须安装到对应目录才可以。而 configure_package_config_file 可以自动计算相对的路径，适用性更广泛。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-cmake" data-lang="cmake">&lt;span style="display:flex;">&lt;span>configure_package_config_file(&lt;span style="color:#e6db74">misc/PkgNameConfig.cmake.in&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">$&amp;#123;&lt;/span>CMAKE_CURRENT_BINARY_DIR&lt;span style="color:#f92672">}&lt;/span>&lt;span style="color:#e6db74">/PkgNameConfig.cmake&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">INSTALL_DESTINATION&lt;/span> &lt;span style="color:#e6db74">"$&amp;#123;CMAKE_INSTALL_LIBDIR}/cmake/PkgName"&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">PATH_VARS&lt;/span> &lt;span style="color:#e6db74">INCLUDE_INSTALL_DIR&lt;/span> &lt;span style="color:#e6db74">TOOL_INSTALL_DIR&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>INSTALL_DESTINATION 是 Config.cmake 的安装路径，但这里并不会安装，只是用来计算相对路径的，所以 install 还是需要写的。&lt;/p>
&lt;p>PATH_VARS 传入 PkgNameConfig.cmake.in 里会用到的路径变量。&lt;/p>
&lt;p>为了使用 configure_package_config_file，Config.cmake.in 也需要做出以下变化：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>在开头写一行 @PACKAGE_INIT@，这会展开生成 CMakePackageConfigHelpers 提供的宏，包括set_and_check，check_required_components。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>使用 set_and_check 替代 set 设置路径，可以在引入时检查对应的路径文件是否存在。（注意如果拆包了其他包的文件就不要 check 了）。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>路径变量前面加 PACKAGE，如 @INCLUDE_INSTALL_DIR@ 改成 @PACKAGE_INCLUDE_INSTALL_DIR@，需要上面 configure_package_config_file 的 PATH_VARS 参数传进来。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>文件后面使用 check_required_components(PkgName) 检查组件，即使没有拆分组件也应该检查，明确告诉 cmake 该包没有组件。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h4 id="使用-targets">使用 Targets&lt;/h4>
&lt;p>对于新增的库，建议使用新式 cmake 写法，使用 Targets，而非手动设置路径：&lt;/p>
&lt;p>建议参考： &lt;a href="https://www.f-ax.de/dev/2020/10/07/cmake-config-package.html">Installing a Config.cmake file&lt;/a>&lt;/p>
&lt;h2 id="使用-find_package-引入库">使用 find_package 引入库&lt;/h2>
&lt;p>find_package 有两个模式，Modules Mode 和 Config Mode，在 Modules Mode下，find_package(PkgName) 会寻找命名为 FindPkgName.cmake 的模块，由此模块负责寻找，这种模块一般是由 cmake 官方提供或者调用者自己编写。Config Mode 就是寻找 PkgNameConfig.cmake 模块了，由库自己提供。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-cmake" data-lang="cmake">&lt;span style="display:flex;">&lt;span>find_package(&lt;span style="color:#e6db74">DtkCore&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>message(&lt;span style="color:#e6db74">"$&amp;#123;DtkCore_VERSION}"&lt;/span>) &lt;span style="color:#75715e"># 6.2 以前没有提供
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>message(&lt;span style="color:#e6db74">"$&amp;#123;DtkCore_LIBRARIES}"&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>message(&lt;span style="color:#e6db74">"$&amp;#123;DtkCore_LIBRARY_DIRS}"&lt;/span>) &lt;span style="color:#75715e"># ！没有提供
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>message(&lt;span style="color:#e6db74">"$&amp;#123;DtkCore_INCLUDE_DIRS}"&lt;/span>) &lt;span style="color:#75715e"># ！没有提供
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>find_package(&lt;span style="color:#e6db74">Dtk&lt;/span> &lt;span style="color:#e6db74">REQUIRED&lt;/span> &lt;span style="color:#e6db74">Core&lt;/span> &lt;span style="color:#e6db74">Gui&lt;/span> &lt;span style="color:#e6db74">Widget&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#75715e"># 与 pkg_check_modules 不同，后面是同一（组）软件提供的不同 components，等价于：
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>find_package(&lt;span style="color:#e6db74">Dtk&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>find_package(&lt;span style="color:#e6db74">DtkCore&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>find_package(&lt;span style="color:#e6db74">DtkGui&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>find_package(&lt;span style="color:#e6db74">DtkWidget&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>find_package 提供的变量请检查 Config.cmake 提供了什么，不像 pkg_check_modules 那样命名统一，在 6.2 以前，提供 DTKCORE_INCLUDE_DIR， 6.2 之后使用 DtkCore_INCLUDE_DIR（旧版变量仍保留）。&lt;/p>
&lt;p>使用 find_package 引入 dtk 的软件，如果用了 DtkCore_INCLUDE_DIRS 请修改，这是 pkg_check_modules 引入才有的变量，另外 DTKCORE_INCLUDE_DIR 升级后 建议换成 DtkCore_INCLUDE_DIR。Gui，Widget 同理。&lt;/p>
&lt;p>参考资料：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://cmake.org/cmake/help/latest/command/find_package.html">https://cmake.org/cmake/help/latest/command/find_package.html&lt;/a>
相关修改：&lt;/li>
&lt;li>&lt;a href="https://github.com/linuxdeepin/deepin-calculator/pull/85">https://github.com/linuxdeepin/deepin-calculator/pull/85&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="编写-findxxxcmake-模块">编写 FindXXX.cmake 模块&lt;/h4>
&lt;p>对于没有提供 pkg-config 也没有提供 Config.cmake 并且 cmake 官方没有提供对应模块的，建议自行写一个 FindXXX.cmake 模块&lt;/p>
&lt;ol>
&lt;li>
&lt;p>通过 find_path，find_library 函数计算出 foo_INCLUDE_PATH, foo_LIBRARY。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>通过 FindPackageHandleStandardArgs 模块提供的 find_package_handle_standard_args 根据 foo_INCLUDE_PATH, foo_LIBRARY 是否有值设置 foo_FOUND，这样是让 find_package 识别此模块所需要的。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>一般会把 foo_INCLUDE_PATH, foo_LIBRARY 标记为高级（mark_as_advanced），不再 cmake gui 显示。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>编写模块自由度很高，还可以根据需求检查 check_function_exists，check_symbol_exists 影响 foo_FOUND 而且只需要编写一次，其他项目可以直接复用。&lt;/p>
&lt;p>修改示例：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/image-editor/pull/28">https://github.com/linuxdeepin/image-editor/pull/28&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="dtkcommon-相关">dtkcommon 相关&lt;/h4>
&lt;p>使用 dconfig_override_files 请不要忘记通过 &lt;code>find_package(Dtk) &lt;/code> 引入 dtkcommon 提供的模块，而不是靠 DtkWidget 的传播依赖（实际上不应该传播 dtkcommon 的）。&lt;/p></description><author>DDE 移植兴趣小组</author></item><item><title>规范导出和使用 pkg-config 文件</title><link>https://deepin-community.github.io/sig-dde-porting/posts/pkg_config/?utm_source=atom_feed</link><pubDate>Fri, 16 Sep 2022 06:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/pkg_config/</guid><description>&lt;h2 id="使用-pkg-config">使用 pkg-config&lt;/h2>
&lt;p>pkg-config 是一个在源代码编译时查询已安装的库的使用接口的计算机工具软件。pkg-config原本是设计用于Linux的，但现在在各个版本的BSD、windows、Mac OS X和Solaris上都有着可用的版本。&lt;/p>
&lt;p>它输出已安装的库的相关信息，包括：&lt;/p>
&lt;ul>
&lt;li>C/C++编译器需要的输入参数&lt;/li>
&lt;li>链接器需要的输入参数&lt;/li>
&lt;li>已安装软件包的版本信息&lt;/li>
&lt;/ul>
&lt;h4 id="使用-cmake-生成-pc-文件">使用 cmake 生成 .pc 文件&lt;/h4>
&lt;p>一个比较规范的 foo.pc.in 如下：&lt;/p>
&lt;pre tabindex="0">&lt;code>prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=$&amp;#123;prefix}
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@/libimageviewer
Name: foo
Description: foo is a demo lib
Version: @PROJECT_VERSION@
Libs: -L$&amp;#123;libdir} -lfoo
Cflags: -I$&amp;#123;includedir}
Requires: Qt5Core Qt5Gui Qt5Widgets
&lt;/code>&lt;/pre>&lt;p>这里的 Name 是显示的名称，文件名才是搜索用，显示名一般不要全部大写。&lt;/p>
&lt;p>Requires：设置 foo 的依赖， 这里有2个条件：&lt;/p>
&lt;ol>
&lt;li>必须是传播的构建依赖，即某软件构建依赖 foo，构建环境必须有 bar 才可以编译&lt;/li>
&lt;li>必须是提供 .pc 文件的依赖&lt;/li>
&lt;/ol>
&lt;p>另外不要忘记加 -L 参数（链接库地址），-I 参数（头文件地址）。&lt;/p>
&lt;p>相关修改： &lt;a href="https://github.com/linuxdeepin/image-editor/pull/27">https://github.com/linuxdeepin/image-editor/pull/27&lt;/a>&lt;/p>
&lt;p>其中路径问题（libdir/includedir）这里再强调一下， 不规范做法：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-txt" data-lang="txt">&lt;span style="display:flex;">&lt;span>prefix=/usr
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>exec_prefix=$&amp;#123;prefix}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>libdir=$&amp;#123;prefix}/lib
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>includedir=$&amp;#123;prefix}/include/foo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这种是硬编码路径，可移植性非常差。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-txt" data-lang="txt">&lt;span style="display:flex;">&lt;span>prefix=@CMAKE_INSTALL_PREFIX@
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>exec_prefix=$&amp;#123;prefix}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>libdir=$&amp;#123;prefix}/lib
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>includedir=$&amp;#123;prefix}/include/foo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>相比上种方法，prefix 使用 cmake 变量，可以应对 prefix 不是 /usr 的情况了。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-txt" data-lang="txt">&lt;span style="display:flex;">&lt;span>prefix=@CMAKE_INSTALL_PREFIX@
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>exec_prefix=$&amp;#123;prefix}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>libdir=$&amp;#123;prefix}/@CMAKE_INSTALL_LIBDIR@
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>includedir=$&amp;#123;prefix}/@CMAKE_INSTALL_INCLUDEDIR@/foo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>相比上种方法，lib/include 目录使用 GNUInstallDirs 变量代替，灵活度更高。&lt;/p>
&lt;p>但是，这种并非最好的，因为（1）GNUInstallDirs 有特殊的拼接规则，如 PREFIX=/，LIBDIR=lib，应该拼接成 /usr/include 而非 /include。（2）CMAKE_INSTALL_LIBDIR 虽然默认是相对路径，但允许被直接设置成绝对路径。&lt;/p>
&lt;p>写在 .pc.in 只能简单拼接，无法判断特殊情况，最好由 cmake 进行拼接。GNUInstallDirs 提供 FULL 版本变量（本文第一个示例）就是智能拼接出来的，建议使用。当然，基于此自行设置变量也可以：
&lt;code>set (INCLUDE_INSTALL_DIR &amp;quot;$&amp;#123;CMAKE_INSTALL_FULL_INCLUDEDIR}/libdtk-$&amp;#123;CMAKE_PROJECT_VERSION}/DCore&amp;quot;)&lt;/code>。&lt;/p>
&lt;h4 id="使用-qmake-生成-pkgcconfig-文件">使用 qmake 生成 pkgcconfig 文件&lt;/h4>
&lt;pre tabindex="0">&lt;code class="language-qmake" data-lang="qmake">CONFIG += create_prl create_pc
# 生成 .pc 文件需要 .prl 中间文件，如果不需要可以使用 no_install_prl
QMAKE_PKGCONFIG_NAME = foo
QMAKE_PKGCONFIG_DESCRIPTION = foo is demo
QMAKE_PKGCONFIG_INCDIR = $$&amp;#123;HEADERDIR}
QMAKE_PKGCONFIG_LIBDIR = $$&amp;#123;LIBDIR}
QMAKE_PKGCONFIG_DESTDIR = pkgconfig
&lt;/code>&lt;/pre>&lt;p>其中的 HEADERDIR，LIBDIR 是需要在 .pro 文件中设置的安装路径。&lt;/p>
&lt;h3 id="cmake-使用-pkg-config">cmake 使用 pkg-config&lt;/h3>
&lt;p>不要混淆 find_package 和 pkg_check_modules&lt;/p>
&lt;p>cmake 导入&lt;/p>
&lt;pre tabindex="0">&lt;code>find_package(PkgConfig REQUIRED) # 导入此模块才可以使用 pkg_check_modules
pkg_check_modules(DtkCore REQUIRED dtkcore)
message("$&amp;#123;DtkCore_VERSION}")
message("$&amp;#123;DtkCore_LIBRARIES}") # 对应 -l 参数
message("$&amp;#123;DtkCore_LIBRARY_DIRS}") # 对应 -L 参数
message("$&amp;#123;DtkCore_INCLUDE_DIRS}") # 对应 -I 参数
pkg_check_modules(Dtk REQUIRED dtkcore dtkgui dtkwidget)
message("$&amp;#123;Dtk_LIBRARIES}")
message("$&amp;#123;Dtk_LIBRARY_DIRS}")
message("$&amp;#123;Dtk_INCLUDE_DIRS}")
&lt;/code>&lt;/pre>&lt;p>参考资料&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://cmake.org/cmake/help/latest/module/FindPkgConfig.html">https://cmake.org/cmake/help/latest/module/FindPkgConfig.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="qmake-使用-pkg-config">qmake 使用 pkg-config&lt;/h3>
&lt;pre tabindex="0">&lt;code>CONFIG += link_pkgconfig
PKGCONFIG += foo
&lt;/code>&lt;/pre></description><author>DDE 移植兴趣小组</author></item><item><title>规范设置编译参数</title><link>https://deepin-community.github.io/sig-dde-porting/posts/compile_flag/?utm_source=atom_feed</link><pubDate>Fri, 16 Sep 2022 06:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/compile_flag/</guid><description>&lt;p>CMake 中有许多关于编译器和链接器的设置。当你需要添加一些特殊的需求，你应该首先检查 CMake 是否支持这个需求，如果支持的话，你就可以不用关心编译器的版本，一切交给 CMake 来做即可。 更棒的是，你可以在 &lt;code>CMakeLists.txt&lt;/code> 表明你的意图，而不是通过开启一系列标志 (flag) 。&lt;/p>
&lt;h2 id="优化参数-o0-o1-o2-o3-ofast-os-和-debug-参数--g">优化参数(-O0/-O1/-O2/-O3/-Ofast/-Os) 和 debug 参数 （-g）&lt;/h2>
&lt;p>CMAKE_BUILD_TYPE 包括 &lt;code>Debug&lt;/code>, &lt;code>Release&lt;/code>, &lt;code>RelWithDebInfo&lt;/code> 和 &lt;code>MinSizeRe&lt;/code> 4 个选项&lt;/p>
&lt;p>它们对应参数是（不同编译平台可能有少许区别）：&lt;/p>
&lt;ol>
&lt;li>Release: &lt;code>-O3 -DNDEBUG&lt;/code>&lt;/li>
&lt;li>Debug: &lt;code>-O0 -g&lt;/code>&lt;/li>
&lt;li>RelWithDebInfo: &lt;code>-O2 -g -DNDEBUG&lt;/code>&lt;/li>
&lt;li>MinSizeRel: &lt;code>-Os -DNDEBUG&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>这些参数可以通过 CMAKE_CXX_FLAGS_DEBUG，CMAKE_CXX_FLAGS_RELEASE&amp;hellip; 查看，并不直接体现在 CMAKE_CXX_FLAGS 中。&lt;/p>
&lt;p>CMAKE_BUILD_TYPE 的默认值不是Release，也不属于上面4种，而是空值，即不附加任何参数。&lt;/p>
&lt;h4 id="全局或非debug模式设置--o3">全局（或非debug模式）设置 -O3：&lt;/h4>
&lt;p>建议改为将 CMAKE_BUILD_TYPE 设置默认 Release&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/ros-infrastructure/bloom/issues/327">https://github.com/ros-infrastructure/bloom/issues/327&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="release-模式加-o3-debug-模式加--g-等等">Release 模式加 O3, Debug 模式加 -g 等等&lt;/h4>
&lt;p>不用加，这些都是无用功&lt;/p>
&lt;h4 id="release-模式加--g">Release 模式加 -g&lt;/h4>
&lt;p>如果软件发行版本需要 debug 符号，请用 RelWithDebInfo 模式&lt;/p>
&lt;h4 id="debug-加--o3">Debug 加 -O3&lt;/h4>
&lt;p>高级别的优化会严重影响 debug 调试， 可以改用 -Og，它允许不影响调试的优化&lt;/p>
&lt;h4 id="同时有--o1--o3">同时有 -O1 -O3&lt;/h4>
&lt;p>-O0/-O1/-O2/-O3/-Ofast 开启的优化参数前依次递增，是完全的子集关系，不是多多益善的。
-Os 是在 -O2 的基础上，尽可能优化程序大小。&lt;/p>
&lt;h4 id="release-模式使用--ofast-或其他优化级别">Release 模式使用 -Ofast 或其他优化级别。&lt;/h4>
&lt;p>最好是 &lt;code>string(REPLACE &amp;quot;-O3&amp;quot; &amp;quot;-Ofast&amp;quot; CMAKE_CXX_FLAGS_RELEASE $&amp;#123;CMAKE_CXX_FLAGS_RELEASE})&lt;/code>，这样就没有原来的&lt;code>-O3&lt;/code>了
如果是是降低优化基本，则必须这样替换。&lt;/p>
&lt;p>参考资料：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/48754619/what-are-cmake-build-type-debug-release-relwithdebinfo-and-minsizerel">https://stackoverflow.com/questions/48754619/what-are-cmake-build-type-debug-release-relwithdebinfo-and-minsizerel&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="de">de&lt;/h4>
&lt;p>参考修改：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/dtkcore/pull/114">https://github.com/linuxdeepin/dtkcore/pull/114&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="c标准版本-比如--stdc11">c++标准版本 (比如 -std=c++11)&lt;/h2>
&lt;p>直接使用 set(CMAKE_CXX_STANDARD 11)&lt;/p>
&lt;p>部分项目设置了 CMAKE_CXX_STANDARD 为 14/17, 又加了参数 -std=c++11，这是冲突的，请检查。
比如： &lt;a href="https://github.com/linuxdeepin/deepin-movie-reborn/blob/0a08e29e78c4f35f787b999d857f696f804f8641/CMakeLists.txt#L18">https://github.com/linuxdeepin/deepin-movie-reborn/blob/0a08e29e78c4f35f787b999d857f696f804f8641/CMakeLists.txt#L18&lt;/a>&lt;/p>
&lt;h2 id="hardening">Hardening&lt;/h2>
&lt;p>DEB_BUILD_HARDENING_FORMAT (gcc/g++ -Wformat -Wformat-security -Werror=format-security)
DEB_BUILD_HARDENING_FORTIFY (gcc/g++ -D_FORTIFY_SOURCE=2)
DEB_BUILD_HARDENING_STACKPROTECTOR (gcc/g++ -fstack-protector-strong)
DEB_BUILD_HARDENING_PIE (gcc/g++ -fPIE -pie)
DEB_BUILD_HARDENING_RELRO (ld -z relro)
DEB_BUILD_HARDENING_BINDNOW (ld -z now)&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://wiki.debian.org/Hardening#Using_Hardening_Options">https://wiki.debian.org/Hardening#Using_Hardening_Options&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://nixos.org/manual/nixpkgs/stable/#sec-hardening-in-nixpkgs">https://nixos.org/manual/nixpkgs/stable/#sec-hardening-in-nixpkgs&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://wiki.archlinux.org/title/Security">https://wiki.archlinux.org/title/Security&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://wiki.ubuntu.com/Security/Features">https://wiki.ubuntu.com/Security/Features&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://wiki.gentoo.org/wiki/Project:Hardened">https://wiki.gentoo.org/wiki/Project:Hardened&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="as-need-参数">as-need 参数&lt;/h2>
&lt;p>已知 as-need 参数 break 了 mold 连接器，可用 as-needed 代替。
相关：https://github.com/linuxdeepin/developer-center/issues/3345&lt;/p>
&lt;h2 id="dl-库">dl 库&lt;/h2>
&lt;p>如果你需要链接到 &lt;code>dl&lt;/code> 库，在 Linux 上可以使用 &lt;code>-ldl&lt;/code> 标志，不过在 CMake 中只需要在 &lt;code>target_link_libraries&lt;/code> 命令中使用内置的 CMake 变量 &lt;a href="https://cmake.org/cmake/help/latest/variable/CMAKE_DL_LIBS.html">&lt;code>$&amp;#123;CMAKE_DL_LIBS}&lt;/code> &lt;/a>。这里不需要模组或者使用 &lt;code>find_package&lt;/code> 来寻找它。（这个命令包含了调用 &lt;code>dlopen&lt;/code> 与 &lt;code>dlclose&lt;/code> 的一切依赖）&lt;/p>
&lt;h2 id="程序间优化interprocedural-optimization">程序间优化(Interprocedural optimization)&lt;/h2>
&lt;p>&lt;a href="https://cmake.org/cmake/help/latest/prop_tgt/INTERPROCEDURAL_OPTIMIZATION.html">&lt;code>INTERPROCEDURAL_OPTIMIZATION&lt;/code>&lt;/a>，最有名的是 &lt;em>链接时间优化&lt;/em> 以及 &lt;code>-flto&lt;/code> 标志，这在最新的 CMake 版本中可用。你可以通过变量 &lt;a href="https://cmake.org/cmake/help/latest//CMAKE_INTERPROCEDURAL_OPTIMIZATION.html">&lt;code>CMAKE_INTERPROCEDURAL_OPTIMIZATION&lt;/code>&lt;/a>（ CMake 3.9+ 可用）或对目标指定 &lt;a href="https://cmake.org/cmake/help/latest/prop_tgt/INTERPROCEDURAL_OPTIMIZATION.html">&lt;code>INTERPROCEDURAL_OPTIMIZATION&lt;/code>&lt;/a> 属性来打开它。在 CMake 3.8 中添加了对 GCC 及 Clang 的支持。如果你设置了 &lt;code>cmake_minimum_required(VERSION 3.9)&lt;/code> 或者更高的版本（参考 &lt;a href="https://cmake.org/cmake/help/latest/policy/CMP0069.html">CMP0069&lt;/a>），当在编译器不支持 &lt;a href="https://cmake.org/cmake/help/latest/prop_tgt/INTERPROCEDURAL_OPTIMIZATION.html">&lt;code>INTERPROCEDURAL_OPTIMIZATION&lt;/code>&lt;/a> 时，通过变量或属性启用该优化会产生报错。你可以使用内置模块 &lt;a href="https://cmake.org/cmake/help/latest/module/CheckIPOSupported.html">CheckIPOSupported&lt;/a> 中的 &lt;code>check_ipo_supported()&lt;/code> 来检查编译器是否支持 IPO 。下面是基于 CMake 3.9 的一个例子：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-cmake" data-lang="cmake">&lt;span style="display:flex;">&lt;span>include(&lt;span style="color:#e6db74">CheckIPOSupported&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>check_ipo_supported(&lt;span style="color:#e6db74">RESULT&lt;/span> &lt;span style="color:#e6db74">result&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>if(&lt;span style="color:#e6db74">result&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span> set_target_properties(&lt;span style="color:#e6db74">foo&lt;/span> &lt;span style="color:#e6db74">PROPERTIES&lt;/span> &lt;span style="color:#e6db74">INTERPROCEDURAL_OPTIMIZATION&lt;/span> &lt;span style="color:#e6db74">TRUE&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>endif()&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="位置无关的代码">位置无关的代码&lt;/h2>
&lt;p>在 g++ 中，对编译生成动态库是 -fPIC/-fpic，对生成可执行文件是 -fPIE/-fpie。&lt;/p>
&lt;p>如果要把静态库链接到动态库，这一选项是必须的。其他情况也建议开启，可以防范一些内存攻击。&lt;/p>
&lt;p>在 cmake 中，对于动态库（SHARED and MODULE library），这一选项是默认开启的。对于静态库，在 cmake 3.14 版本之后（CMP0083 新标准），默认值是 POSITION_INDEPENDENT_CODE，而该值默认为假。&lt;/p>
&lt;p>可以通过 &lt;code>set(POSITION_INDEPENDENT_CODE True)&lt;/code> 让静态库默认增加 “-fpic” 编译。&lt;/p>
&lt;p>最好通过 set_property 设置此选项：&lt;code>set_property(TARGET foo PROPERTY POSITION_INDEPENDENT_CODE TRUE)&lt;/code>，foo 为对应静态库或者可执行程序。&lt;/p>
&lt;p>此外，推荐使用 &lt;a href="https://cmake.org/cmake/help/latest/module/CheckPIESupported.html">check_pie_supported()&lt;/a>，检查一下编译器是否支持。&lt;/p>
&lt;p>参考资料：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://cmake.org/cmake/help/latest/prop_tgt/POSITION_INDEPENDENT_CODE.html">https://cmake.org/cmake/help/latest/prop_tgt/POSITION_INDEPENDENT_CODE.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://cmake.org/cmake/help/latest/policy/CMP0083.html">https://cmake.org/cmake/help/latest/policy/CMP0083.html&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="-lpthread">-lpthread&lt;/h2>
&lt;p>POSIX thread 是基于 C/C++ 的标准线程库。在 cmake 3.1 以上的版本提供了 FindThreads 模块， 如果设置 THREADS_PREFER_PTHREAD_FLAG 变量，会优先使用 pthread。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-cmake" data-lang="cmake">&lt;span style="display:flex;">&lt;span>set(&lt;span style="color:#e6db74">THREADS_PREFER_PTHREAD_FLAG&lt;/span> &lt;span style="color:#e6db74">ON&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>find_package(&lt;span style="color:#e6db74">Threads&lt;/span> &lt;span style="color:#e6db74">REQUIRED&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>target_link_libraries(&lt;span style="color:#e6db74">my_app&lt;/span> &lt;span style="color:#e6db74">PRIVATE&lt;/span> &lt;span style="color:#e6db74">Threads::Threads&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;a href="https://cmake.org/cmake/help/latest/module/FindThreads.html">https://cmake.org/cmake/help/latest/module/FindThreads.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://stackoverflow.com/questions/1620918/cmake-and-libpthread/29871891">https://stackoverflow.com/questions/1620918/cmake-and-libpthread/29871891&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>参考文档：&lt;/p>
&lt;p>&lt;a href="https://modern-cmake-cn.github.io/Modern-CMake-zh_CN/chapters/features/small.html">https://modern-cmake-cn.github.io/Modern-CMake-zh_CN/chapters/features/small.html&lt;/a>&lt;/p>
&lt;h2 id="设置时不要覆盖之前的选项">设置时不要覆盖之前的选项&lt;/h2>
&lt;p>错误示例：set(CMAKE_CXX_FLAGS &amp;ldquo;-Wall&amp;rdquo;)
正确示例：set(CMAKE_CXX_FLAGS &amp;ldquo;$&amp;#123;CMAKE_CXX_FLAGS} -Wall&amp;rdquo;)&lt;/p>
&lt;p>如果确实有覆盖的要求，请在源码注释说明，因为一般出现覆盖 flag 很可能是失误。&lt;/p>
&lt;h2 id="判断架构">判断架构&lt;/h2>
&lt;p>遗留代码有使用 dpkg-architecture 判断的，改用 CMAKE_SYSTEM_PROCESSOR。&lt;/p>
&lt;p>dpkg-architecture 是 debian 系特有的，并不通用。
除了 deb 安装器等不考虑像其他发行版移植的应用，不要在 CMakeList 里使用 dpkg 系列命令。&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/dde-dock/pull/681">https://github.com/linuxdeepin/dde-dock/pull/681&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="cmake_l_flags">CMAKE_L_FLAGS&lt;/h2>
&lt;p>&lt;a href="https://github.com/linuxdeepin/deepin-movie-reborn/blob/cc35eb1b40214f6beb4c4b21768983e4f0d8d99f/tests/deepin-movie/CMakeLists.txt#L101">部分项目&lt;/a> 使用了 CMAKE_L_FLAGS，这不是 cmake 标准的变量，这是想用 CMAKE_EXE_LINKER_FLAGS ？&lt;/p>
&lt;h2 id="glib-compile-schemas">glib-compile-schemas&lt;/h2>
&lt;p>gsettings schemas 安装后需要编译一下（一般目录是 /usr/share/glib-2.0/schemas）才能使用，这个应该是安装后进行的，用对应目录所有 gschema.xml 再编译出一个 gschemas.compiled 文件。&lt;/p>
&lt;p>像下面这样在 CMakeList.txt 编译是不需要的：
&lt;code>install(CODE &amp;quot;execute_process(COMMAND glib-compile-schemas $&amp;#123;CMAKE_INSTALL_PREFIX}/share/glib-2.0/schemas)&amp;quot;)&lt;/code>
&lt;a href="https://github.com/linuxdeepin/dde-session-shell/commit/6faf19b4d73cc35f5cd0f20141077139eccc5846">相关修改&lt;/a>&lt;/p></description><author>DDE 移植兴趣小组</author></item><item><title>尽可能避免使用 hardcode 路径</title><link>https://deepin-community.github.io/sig-dde-porting/posts/hardcode/?utm_source=atom_feed</link><pubDate>Fri, 16 Sep 2022 06:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/hardcode/</guid><description>&lt;h2 id="可执行程序">可执行程序&lt;/h2>
&lt;h4 id="判断某个可执行程序是否存在">判断某个可执行程序是否存在&lt;/h4>
&lt;p>不推荐做法： 根据硬编码路径判断某个文件是否存在。比如判断 &lt;code>QFile().exists(/usr/bin/Foo)&lt;/code>。&lt;/p>
&lt;p>推荐做法：根据 PATH 寻找可执行文件, 一般不需要自行读取 PATH，比如 QT 可以使用：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>Exist &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#f92672">!&lt;/span>QStandardPaths&lt;span style="color:#f92672">::&lt;/span>findExecutable(&lt;span style="color:#e6db74">"Foo"&lt;/span>).isEmpty();
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>比如 glib 可以使用 find_program_in_path。&lt;/p>
&lt;p>修改示例：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/dde-network-core/pull/56">https://github.com/linuxdeepin/dde-network-core/pull/56&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/linuxdeepin/dde-session-shell/pull/127">https://github.com/linuxdeepin/dde-session-shell/pull/127&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="执行某个可执行程序">执行某个可执行程序&lt;/h4>
&lt;p>同样，推荐用 PATH 寻找，尽量不使用绝对路径。&lt;/p>
&lt;p>Qt 的 QProcess 会自动处理 PATH 环境变量，因此 &lt;code>QProcess::execute(&amp;quot;/usr/bin/touch&amp;quot;, QStringList() &amp;lt;&amp;lt; sessionCacheFile)&lt;/code> 可以直接改成
&lt;code>QProcess::execute(&amp;quot;touch&amp;quot;, QStringList() &amp;lt;&amp;lt; sessionCacheFile)&lt;/code>。&lt;/p>
&lt;p>修改示例：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/deepin-downloader/pull/27">https://github.com/linuxdeepin/deepin-downloader/pull/27&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="动态库路径">动态库路径&lt;/h4>
&lt;p>尽量不要硬编码 /urs/lib&lt;/p>
&lt;p>qt 应用可以使用 QLibraryInfo::location(QLibraryInfo::LibrariesPath)&lt;/p>
&lt;h3 id="头文件">头文件&lt;/h3>
&lt;p>绝对不要有 &lt;code>#include &amp;lt;/usr/include/xxx.h&amp;gt;&lt;/code> 这种代码，c/c++ 头文件会自动在 /usr/include 里寻找，直接使用 &lt;code>#include &amp;lt;xxx.h&amp;gt;&lt;/code> 。&lt;/p>
&lt;p>此外，如果库提供了 pkg-config 文件，提供的 -I 参数会指定头文件位置，如果提供 Config.cmake （cmake用）文件，一般会提供 Foo_INCLUDE_DIR 变量，如果提供 .pri （qmake 用）文件，由 QT.foo.includes 提供头文件位置。&lt;code>xxx.h&lt;/code> 是相对提供的路径寻找。&lt;/p>
&lt;p>对于没有提供任何开发文件的库，也可以使用 cmake 提供的 &lt;a href="https://cmake.org/cmake/help/latest/module/CheckIncludeFile.html">CheckIncludeFile&lt;/a> 模块寻找头文件。&lt;/p>
&lt;h3 id="应用数据useshare">应用数据（/use/share）&lt;/h3>
&lt;p>/usr/share，一般保存与架构无关的只读数据。此目录同样不应该硬编码，在 XDG 规范中，这种数据的目录使用环境变量 XDG_DATA_DIRS 设置，如果读不到对应环境变量再去读取 /usr/local/share:/usr/share。&lt;/p>
&lt;p>使用 qt 的应用该类型硬编码路径应该使用 QStandardPaths 或者 libqtxdg 代替。&lt;/p>
&lt;p>参考：&lt;a href="https://wiki.archlinux.org/title/XDG_Base_Directory">XDG Base Directory&lt;/a>&lt;/p>
&lt;p>在此目录寻找某文件，推荐使用 QStandardPaths::standardLocations
参考修改：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/0xd34df00d/leechcraft/commit/32bf44704e156eaf6ada672e18febf64d9fd279f">https://github.com/0xd34df00d/leechcraft/commit/32bf44704e156eaf6ada672e18febf64d9fd279f&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="usrshareapplications">/usr/share/applications&lt;/h4>
&lt;p>直接使用 &lt;code>QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation)&lt;/code>&lt;/p>
&lt;p>相关修改：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/dde-grand-search/pull/30">https://github.com/linuxdeepin/dde-grand-search/pull/30&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="usrshareappname">/usr/share/AppName&lt;/h4>
&lt;p>读取本应用数据，qt 软件可以使用 &lt;code>QStandardPaths::standardLocations(QStandardPaths::AppLocalDataLocation)&lt;/code>&lt;/p>
&lt;p>相关修改：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/communi/communi-sailfish/commit/16b669653e0e86b24aa4dd20e0434faa07519fb6">https://github.com/communi/communi-sailfish/commit/16b669653e0e86b24aa4dd20e0434faa07519fb6&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="导入翻译">导入翻译&lt;/h4>
&lt;p>部分应用导入翻译使用了 &lt;code>/usr/share&lt;/code> &lt;a href="https://github.com/linuxdeepin/dde-control-center/blob/ed696dabf41bee19f28758d2589dac20b866c356/src/reset-password-dialog/main.cpp#L63">示例&lt;/a>, 应该改为使用 &lt;code>QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation)&lt;/code>, 此外使用了 dtk 的应用是直接用 &lt;a href="https://github.com/linuxdeepin/dtkwidget/blob/f80f48076e1821b06461e1fc330f50ceaff2c812/src/widgets/dapplication.cpp#L776">DApplication 的 loadTranslator&lt;/a> 导入翻译。&lt;/p>
&lt;h2 id="shebangs">Shebangs&lt;/h2>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-txt" data-lang="txt">&lt;span style="display:flex;">&lt;span>#!/bin/bash -&amp;gt; #!/usr/bin/env bash
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>#!/usr/bin/python -&amp;gt; #!/usr/bin/env python
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>并非所有系统都有 /bin 目录和 /usr/bin 目录，比如 NixOS 和 GNU Guix 操作系统。
env 可以根据环境变量 &lt;strong>&lt;code>$PATH&lt;/code>&lt;/strong> 寻找可执行程序（如 bash），比硬编码路径更加灵活，可以提高可移植性。&lt;/p>
&lt;p>不过并非所有脚本都适合使用 env, 有 2 种情况：&lt;/p>
&lt;ul>
&lt;li>对安全性要求高的脚本（如以 root 权限运行），硬编码可以避免通过相关 PATH 让 bash 指向恶意程序。&lt;/li>
&lt;li>带有参数的 Shebangs 头，如 #!/usr/bin/perl -w 不能改成 #!/usr/bin/env perl -w&lt;/li>
&lt;/ul>
&lt;p>对除此之外的大部分的 Shebangs 脚本来说，使用 env 是更好的方案。&lt;/p>
&lt;p>参考资料：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://blog.csdn.net/qq_37164975/article/details/106181500">#!/bin/bash 和 #!/usr/bin/env bash 的区别&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://unix.stackexchange.com/questions/29608/why-is-it-better-to-use-usr-bin-env-name-instead-of-path-to-name-as-my">why-is-it-better-to-use-usr-bin-env-name-instead-of-path-to-name&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="相关-issues">相关 issues：&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://github.com/linuxdeepin/developer-center/issues/3374">https://github.com/linuxdeepin/developer-center/issues/3374&lt;/a>&lt;/li>
&lt;/ul></description><author>DDE 移植兴趣小组</author></item><item><title>在 GNU/Linux 中使用 GNUInstallDirs 优化 cmake 安装路径</title><link>https://deepin-community.github.io/sig-dde-porting/posts/gnuinstalldirs/?utm_source=atom_feed</link><pubDate>Fri, 16 Sep 2022 06:00:00 +0000</pubDate><guid isPermaLink="true">https://deepin-community.github.io/sig-dde-porting/posts/gnuinstalldirs/</guid><description>&lt;h2 id="使用-gnuinstalldirscmake模块">使用 GNUInstallDirs.cmake模块&lt;/h2>
&lt;p>在指定安装路径时，应当使用变量而非写死安装目录，以便于在不完全符合 FHS 的系统上安装，提高程序的可移植性。对于使用何种变量， GUN 提出了适用于 unix 系统的 &lt;a href="https://www.gnu.org/prep/standards/html_node/Directory-Variables.html">GNU标准安装目录&lt;/a>，GNU/Linux 上使用的就是这套标准的变体。cmake 官方提供了 GNUInstallDirs 模块，定义了一组标准的变量，用于安装不同类型文件到规范指定的目录中。&lt;/p>
&lt;p>要使用这个模块，在 CMakeLists.txt 添加一行 &lt;code>include(GNUInstallDirs)&lt;/code> 即可导入。如果你发现 &lt;em>CMAKE_INSTALL_XXXX&lt;/em> 的值为空，大概率是缺少这一行。注意导入模块需要放在使用变量之前。&lt;/p>
&lt;h2 id="前缀值-cmake_install_prefix">前缀值 CMAKE_INSTALL_PREFIX&lt;/h2>
&lt;p>CMAKE_INSTALL_PREFIX（&lt;strong>后面简称 PREFIX&lt;/strong>） 是一个非常特殊的变量，在 CMakeLists.txt 中所有的相对路径都会默认把 PREFIX 作为前缀进行拼接，组成绝对路径。这一变量是 cmake 基础变量，不导入 GNUInstallDirs 模块也会存在。&lt;/p>
&lt;p>举一个例子，假如之前要把文件安装进 /usr/share 目录&lt;/p>
&lt;pre tabindex="0">&lt;code>install(FILES $&amp;#123;QM_FILES} DESTINATION /usr/share/deepin-calculator/translations)
&lt;/code>&lt;/pre>&lt;p>如果设置了 PREFIX=/usr，只需要改成相对路径 share，cmake 会自动拼接成 /usr/share&lt;/p>
&lt;pre tabindex="0">&lt;code>install(FILES $&amp;#123;QM_FILES} DESTINATION share/deepin-calculator/translations)
&lt;/code>&lt;/pre>&lt;p>这样写，如果安装前缀改变，比如改成 &lt;code>/usr/local&lt;/code>，只需要修改 CMAKE_INSTALL_PREFIX，而不用改 CMakeLists.txt 源码。&lt;/p>
&lt;p>目前 CMAKE_INSTALL_PREFIX 按 GNU 的标准默认值是 &lt;code>/usr/local&lt;/code>。而在 dde 的项目中，一般期望前缀值是 /usr。我们当然可以通过 &lt;code>cmake -DCMAKE_INSTALL_PREFIX=/usr&lt;/code> 来修改 PREFIX，但最好在项目中添加下面 3 行修改一下 PREFIX 的默认值：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-cmake" data-lang="cmake">&lt;span style="display:flex;">&lt;span>if (&lt;span style="color:#e6db74">CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span> set(&lt;span style="color:#e6db74">CMAKE_INSTALL_PREFIX&lt;/span> &lt;span style="color:#e6db74">/usr&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>endif ()&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里容易犯两种错误：&lt;/p>
&lt;ol>
&lt;li>没有检查 CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT 就直接 set：这会导致用户传入的参数失效，相当于硬编码了路径。&lt;/li>
&lt;li>使用 &lt;code>if (NOT DEFINE CMAKE_INSTALL_PREFIX )&lt;/code> 判断用户是否传参了 PREFIX：实际上 PREFIX 无论什么情况都是有定义的， 只能使用 &lt;code>CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT&lt;/code> 判断是否是默认值。&lt;/li>
&lt;/ol>
&lt;p>对 CMAKE_INSTALL_PREFIX 的配置会对子目录生效，在 cmake 3.14 之前，先应用安装规则再处理 add_subdirectory，而 3.14 及后续版本，会按照声明的顺序运行所有安装规则/add_subdirectory，因此，除非你想给子目录单独配置，上述的设置应写在 add_subdirectory 的前面。 见 &lt;a href="https://cmake.org/cmake/help/latest/policy/CMP0082.html#policy:CMP0082">CMP0082&lt;/a>。&lt;/p>
&lt;h2 id="gnuinstalldirs-变量">GNUInstallDirs 变量&lt;/h2>
&lt;p>在 GNUInstallDirs 中，定义了一些 GNU 标准安装目录的变量，提供给定类型文件的安装路径。这些值可以传递给对应 install() 命令的 DESTINATION 选项。它们通常是相对于安装前缀（PREFIX）的相对路径，以便于以可重定位的方式将其拼接为绝对路径。当然，它们也允许赋值为绝对路径。&lt;/p>
&lt;h3 id="cmake_install_xxxx">CMAKE_INSTALL_XXXX&lt;/h3>
&lt;ul>
&lt;li>
&lt;p>CMAKE_INSTALL_BINDIR
用户可执行程序（ &lt;code>bin&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_SBINDIR
系统管理员可执行程序（ &lt;code>sbin&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_LIBEXECDIR
可执行库文件（ &lt;code>libexec&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_SYSCONFDIR
单机只读数据/read-only single-machine data（ &lt;code>etc&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_SHAREDSTATEDIR
架构无关的可修改数据/modifiable architecture-independent data （ &lt;code>com&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_LOCALSTATEDIR
单机可修改数据/modifiable single-machine data（ &lt;code>var&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_RUNSTATEDIR
3.9版中加入：运行时可修改数据（ &lt;code>LOCALSTATEDIR/run&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_LIBDIR
目标代码库/object code libraries （ &lt;code>lib&lt;/code> 或 &lt;code>lib64&lt;/code> ）
（在 Debian 上当 PREFIX 是 /usr 时，LIBDIR 也可能是 &lt;code>lib/&amp;lt;multiarch-tuple&amp;gt;&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_INCLUDEDIR
C语言头文件（ &lt;code>include&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_OLDINCLUDEDIR
non-gcc 的C语言头文件（ &lt;code>/usr/include&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_DATAROOTDIR
与架构无关的只读数据根目录（ &lt;code>share&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_DATADIR
与架构无关的只读数据（ &lt;code>DATAROOTDIR&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_INFODIR
info 文档（ &lt;code>DATAROOTDIR/info&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_LOCALEDIR
与语言相关的数据/locale-dependent data（ &lt;code>DATAROOTDIR/locale&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_MANDIR
man 文档（ &lt;code>DATAROOTDIR/man&lt;/code> ）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>CMAKE_INSTALL_DOCDIR
文档根目录（ &lt;code>DATAROOTDIR/doc/PROJECT_NAME&lt;/code> ）&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>需要注意的是 DATAROOTDIR 是 DATADIR，LOCALEDIR，MANDIR 和 DOCDIR 共同前缀，不应该在 install 中直接使用 &lt;code>DATAROOTDIR&lt;/code> 作为参数，而是应该使用 &lt;code>DATADIR&lt;/code> 代替 。类似 /usr/share/man 的路径应该用 &lt;code>MANDIR&lt;/code> 代替，而不是 &lt;code>DATADIR/man&lt;/code>。&lt;/p>
&lt;h3 id="cmake_install_full_xxxx">CMAKE_INSTALL_FULL_XXXX&lt;/h3>
&lt;p>CMAKE_INSTALL_BINDIR 推荐用相对路径，但也可以使用绝对路径，有些时候我们需要绝对路径（比如生成 pkgconfig 文件时），直接加 PREFIX 拼接并不好（因为 BINDIR 可能已经是绝对路径了），这时，我们可以直接使用 &lt;code>CMAKE_INSTALL_FULL_BINDIR&lt;/code>，这是由 GNUInstallDirs 模块提供，自动从 BINDIR 计算出的绝对路径。如果 BINDIR 是绝对路径，直接相等。如果 BINDIR 是相对路径，则等于 BINDIR 按照一定规则与 PREFIX 拼接而成的值。&lt;/p>
&lt;p>用例： &lt;a href="https://github.com/linuxdeepin/dde-dock/pull/556">https://github.com/linuxdeepin/dde-dock/pull/556&lt;/a>&lt;/p>
&lt;p>当然，install 是没有必要用 CMAKE_INSTALL_FULL_XXXX 的（当然用也可以），因为它会自动判断，并拼接相对路径。&lt;/p>
&lt;h2 id="前缀拼接的特殊情况">前缀拼接的特殊情况&lt;/h2>
&lt;p>GNUInstallDirs 前缀拼接存在一些特殊情况需要注意：&lt;/p>
&lt;h4 id="1-cmake_install_prefix">1. CMAKE_INSTALL_PREFIX=/&lt;/h4>
&lt;p>除了 &lt;code>SYSCONFDIR&lt;/code>, &lt;code>LOCALSTATEDIR&lt;/code> 和 &lt;code>RUNSTATEDIR&lt;/code> 正常拼接外, 其他值都会增加一个 usr/ 前缀 。比如 &lt;code>INCLUDEDIR&lt;/code> 默认值 include ，变成 usr/include, 最终拼接成 /usr/include。&lt;/p>
&lt;pre tabindex="0">&lt;code>这里自动增加 usr 是符合 GNU 目录标准的，因为这些路径是符号链接。
~ ❯❯❯ readlink /lib
usr/lib
~ ❯❯❯ readlink /bin
usr/bin
&lt;/code>&lt;/pre>&lt;h4 id="2-cmake_install_prefixusr">2. CMAKE_INSTALL_PREFIX=/usr&lt;/h4>
&lt;p>&lt;code>SYSCONFDIR&lt;/code>, &lt;code>LOCALSTATEDIR&lt;/code> 和 &lt;code>RUNSTATEDIR&lt;/code> 拼接时只拼接 “/”。 比如, &lt;code>SYSCONFDIR&lt;/code> 默认值 etc 会拼接成 /etc。&lt;/p>
&lt;h4 id="3-cmake_install_prefixopt">3. CMAKE_INSTALL_PREFIX=/opt/&amp;hellip;&lt;/h4>
&lt;p>&lt;code>SYSCONFDIR&lt;/code>, &lt;code>LOCALSTATEDIR&lt;/code> 和 &lt;code>RUNSTATEDIR&lt;/code> 会向后拼接。 比如， &lt;code>SYSCONFDIR&lt;/code> 会变成 /etc/opt/&amp;hellip;.&lt;/p>
&lt;h2 id="参考文档">参考文档&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html">GNUInstallDirs 文档&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://cmake.org/cmake/help/latest/command/install.html">cmake install 文档&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/Kitware/CMake/blob/master/Modules/GNUInstallDirs.cmake">GNUInstallDirs 源码&lt;/a>&lt;/li>
&lt;/ul></description><author>DDE 移植兴趣小组</author></item><item><title>DDE-Control-Center 自启动管理插件 成果展示</title><link>https://y-aang.github.io/posts/%E6%88%90%E6%9E%9C%E5%85%AC%E7%A4%BAdde-control-center/?utm_source=atom_feed</link><pubDate>Sun, 14 Aug 2022 06:00:00 +0000</pubDate><guid isPermaLink="true">https://y-aang.github.io/posts/%E6%88%90%E6%9E%9C%E5%85%AC%E7%A4%BAdde-control-center/</guid><description>&lt;p>DDE 控制中心自启动管理插件可单独编译、集成至 DDE 控制中心、集中展示并管理&lt;code>Deepin&lt;/code>系统中软件开机自启动设置。&lt;/p>
&lt;h2 id="参赛信息">参赛信息&lt;/h2>
&lt;p>本项目参加2023全国大学生计算机系统能力大赛操作系统设计赛-功能挑战赛，选题为&lt;a href="https://github.com/oscomp/proj223-control-center-startup-management-plugin">proj223-control-center-startup-management-plugin&lt;/a>。&lt;/p>
&lt;p>参赛小组：&lt;/p>
&lt;ul>
&lt;li>复旦大学 朱元依&lt;/li>
&lt;li>复旦大学 沈扬&lt;/li>
&lt;li>复旦大学 朱俊杰&lt;/li>
&lt;/ul>
&lt;p>指导老师：&lt;/p>
&lt;ul>
&lt;li>复旦大学 张亮&lt;/li>
&lt;li>复旦大学 陈辰&lt;/li>
&lt;li>企业导师 王子冲&lt;/li>
&lt;/ul>
&lt;h2 id="完成情况">完成情况&lt;/h2>
&lt;h3 id="1dde-控制中心自启动管理插件">1、DDE 控制中心自启动管理插件&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>任务&lt;/th>
&lt;th>完成情况&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>（必须）完成一个控制中心插件，能够展示当前所有开机启动项的列表&lt;/td>
&lt;td>完成&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>（必须）插件以单独的仓库提供，并能够单独构建，不需要合并入 dde-control-center 项目&lt;/td>
&lt;td>完成&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>（必须）能够在插件中，通过用户界面的交互来管理（添加、删除、启用、禁用）开机启动项&lt;/td>
&lt;td>完成&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>（必须）编写博客，记录开发过程的心得与体会，并将博客投递至 planet.deepin.org&lt;/td>
&lt;td>完成博客编写，待deepin官方审核&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="成果展示">成果展示&lt;/h2>
&lt;p>功能展示视频链接：&lt;/p>
&lt;p>链接：https://pan.baidu.com/s/1YgvhbZ8pypE4CFhuBYw3eg
提取码：rwcc&lt;/p>
&lt;p>插件图标：&lt;/p>
&lt;p>&lt;img alt="前端页面" src="https://y-aang.github.io/img/final/%E5%89%8D%E7%AB%AF%E9%A1%B5%E9%9D%A2.jpg" />&lt;/p>
&lt;p>自启动管理窗口：&lt;/p>
&lt;p>&lt;img alt="自启动管理页面" src="https://y-aang.github.io/img/final/%E8%87%AA%E5%90%AF%E5%8A%A8%E7%AE%A1%E7%90%86%E9%A1%B5%E9%9D%A2.jpg" />&lt;/p>
&lt;p>添加自启动应用选择界面：&lt;/p>
&lt;p>&lt;img alt="应用选择页面" src="https://y-aang.github.io/img/final/%E5%BA%94%E7%94%A8%E9%80%89%E6%8B%A9%E9%A1%B5%E9%9D%A2.jpg" />&lt;/p>
&lt;h2 id="部署方式">部署方式&lt;/h2>
&lt;h3 id="1系统环境">1、系统环境&lt;/h3>
&lt;p>开发环境：Deepin V23Beta版&lt;/p>
&lt;p>系统架构：x86&lt;/p>
&lt;p>镜像下载链接：https://mirrors.ustc.edu.cn/deepin-cd/releases/23-Beta/&lt;/p>
&lt;p>虚拟机平台：WMware Workstation 16Pro&lt;/p>
&lt;p>操作系统环境搭建参考博客：https://blog.csdn.net/qq_44133136/article/details/105887560&lt;/p>
&lt;h3 id="2配置-deepin-插件环境">2、配置 Deepin 插件环境&lt;/h3>
&lt;p>&lt;strong>安装依赖包：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>sudo apt build-dep .
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo apt install -y qt5-default
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo apt-get install dde-control-center-dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="3安装插件">3、安装插件&lt;/h3>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>sudo sh install.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>安装成功后，打开控制中心，会看到以下自启动插件图标，即为安装成功：&lt;/p>
&lt;p>&lt;img alt="控制中心插件图标" src="https://y-aang.github.io/img/final/%E6%8E%A7%E5%88%B6%E4%B8%AD%E5%BF%83%E6%8F%92%E4%BB%B6%E5%9B%BE%E6%A0%87.jpg" />&lt;/p>
&lt;p>此时，如果进入&lt;code>/usr/lib/x86_64-linux-gnu/dde-control-center/modules/&lt;/code>文件夹，看到编译出的&lt;code>.so&lt;/code>文件已经被下载到该文件夹中：&lt;/p>
&lt;p>&lt;img alt="插件安装位置文件夹" src="https://y-aang.github.io/img/final/%E6%8F%92%E4%BB%B6%E5%AE%89%E8%A3%85%E4%BD%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E5%A4%B9.jpg" />&lt;/p>
&lt;h3 id="4卸载插件">4、卸载插件&lt;/h3>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>sudo sh uninstall.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>重启控制中心，可以看到原本的“自启动管理”图标消失，即为卸载成功。&lt;/p>
&lt;h2 id="分工">分工&lt;/h2>
&lt;p>朱元依：插件前端开发&lt;/p>
&lt;p>沈扬：环境配置，编写配置文件&lt;/p>
&lt;p>朱俊杰：后端接口开发（添加、删除、启用、禁用）&lt;/p>
&lt;h2 id="开发计划">开发计划&lt;/h2>
&lt;h3 id="第一步62678">第一步（6/26～7/8）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 调研&lt;code>DDE Control Center&lt;/code>框架等相关内容&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 设计前端界面&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 分工&lt;/li>
&lt;/ul>
&lt;h3 id="第二步79715">第二步（7/9～7/15）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 编译教程中的&lt;code>Hello World&lt;/code>控制中心插件&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 设计插件架构&lt;/li>
&lt;/ul>
&lt;h3 id="第三步716722">第三步（7/16～7/22）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 配置环境，编译V20示例插件&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 设计后端接口&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 修改&lt;code>window&lt;/code>、&lt;code>operation&lt;/code>、&lt;code>category&lt;/code>下的文件&lt;/li>
&lt;/ul>
&lt;h3 id="第四步723729">第四步（7/23～7/29）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 配置环境，编译&lt;code>Default-App&lt;/code>与&lt;code>Self Start-up&lt;/code>插件&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> Debug&lt;/li>
&lt;/ul>
&lt;h3 id="第五步730810">第五步（7/30～8/10）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 修改翻译、文字颜色问题&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 撰写文档&lt;/li>
&lt;/ul></description><author>dde 自启动管理插件开发小组博客</author></item><item><title>REUSE使用指南</title><link>https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/</link><pubDate>Wed, 10 Aug 2022 13:00:00 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/</guid><description>&lt;h1 id="什么是reuse">什么是REUSE
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e4%bb%80%e4%b9%88%e6%98%afreuse">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;p>REUSE是一个工具，准确的来说是一个帮助我们下载开源许可证书和检查开源声明是否合规的脚本：&lt;/p>
&lt;h1 id="使用reuse配置项目">使用REUSE配置项目
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e4%bd%bf%e7%94%a8reuse%e9%85%8d%e7%bd%ae%e9%a1%b9%e7%9b%ae">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;h2 id="安装">安装
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e5%ae%89%e8%a3%85">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>REUSE依赖于本地的python环境，所以需要首先安装python和pip&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">sudo apt install python3
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
sudo python3 get-pip.py
&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后安装REUSE&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">pip3 install --user reuse
&lt;/code>&lt;/pre>&lt;/div>&lt;p>在此之后，确保它&lt;code>~/.local/bin&lt;/code>在您的&lt;code>$PATH&lt;/code>中。在 Windows 上，您的环境所需的路径可能类似于 &lt;code>%USERPROFILE%\AppData\Roaming\Python\Python39\Scripts&lt;/code>，具体取决于您安装的 Python 版本。&lt;/p>
&lt;h2 id="使用">使用
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e4%bd%bf%e7%94%a8">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="初始化项目">初始化项目
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e5%88%9d%e5%a7%8b%e5%8c%96%e9%a1%b9%e7%9b%ae">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>进入一个项目，使用&lt;code>reuse init&lt;/code>命令可以初始化项目，此时在你的命令行菜单上会以交互的方式询问你需要的许可证和版权所有人以及邮箱。注意：许可证的名称需要规范：&lt;a href="https://spdx.org/licenses/" title="许可证名称">许可证名称&lt;/a>&lt;/p>
&lt;p>如果你在这一步遗漏了也没事，可以使用&lt;code>reuse download &amp;lt;许可证名称&amp;gt;&lt;/code>的方式下载所需的许可证，这些许可证会一并放入项目下的License文件夹中&lt;/p>
&lt;h3 id="为代码文件添加版权声明">为代码文件添加版权声明
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e4%b8%ba%e4%bb%a3%e7%a0%81%e6%96%87%e4%bb%b6%e6%b7%bb%e5%8a%a0%e7%89%88%e6%9d%83%e5%a3%b0%e6%98%8e">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;h4 id="使用自动化脚本添加">使用自动化脚本添加
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e4%bd%bf%e7%94%a8%e8%87%aa%e5%8a%a8%e5%8c%96%e8%84%9a%e6%9c%ac%e6%b7%bb%e5%8a%a0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>含有代码逻辑文件添加版权声明方法为：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">reuse addheader --copyright&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">"UnionTech Software Technology Co., Ltd."&lt;/span> --license&lt;span style="color:#f92672">=&lt;/span>GPL-3.0-or-later -r &amp;lt;目录&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>这只是一个简单的例子，本质上resuse还有更多的用法：&lt;/p>
&lt;p>&lt;code>--copyright-style&lt;/code>可以将默认值更改 SPDX-FileCopyrightText为以下样式之一：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-text" data-lang="text">spdx: SPDX-FileCopyrightText: &amp;lt;year&amp;gt; &amp;lt;statement&amp;gt;
spdx-symbol: SPDX-FileCopyrightText: © &amp;lt;year&amp;gt; &amp;lt;statement&amp;gt;
string: Copyright &amp;lt;year&amp;gt; &amp;lt;statement&amp;gt;
string-c: Copyright (C) &amp;lt;year&amp;gt; &amp;lt;statement&amp;gt;
string-symbol: Copyright © &amp;lt;year&amp;gt; &amp;lt;statement&amp;gt;
symbol: © &amp;lt;year&amp;gt; &amp;lt;statement&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>reuse不会限制你使用多少个copyright 也不会限制你使用的license的个数。&lt;/p>
&lt;p>reuse 会猜测你使用的语言，并且添加对应的注释格式，如果不能正确添加或者需要更多的需求可以考虑下面的方法。&lt;/p>
&lt;h4 id="手动进行添加">手动进行添加
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e6%89%8b%e5%8a%a8%e8%bf%9b%e8%a1%8c%e6%b7%bb%e5%8a%a0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>reuse允许你手动进行添加版权声明，只要你的版权声明符合它的规范即可。你所需要的是：充分的耐心，键盘上未曾损坏的&lt;code>Ctrl&lt;/code> &lt;code>C&lt;/code> &lt;code>V&lt;/code> 三个按键即可完成这一切。我个人建议是用上面提供的自动化脚本添加一个版权声明作为模板，然后自己修改下，就可以展现你作为CV工程师的实力了。&lt;/p>
&lt;h4 id="自动进行添加">自动进行添加
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e8%87%aa%e5%8a%a8%e8%bf%9b%e8%a1%8c%e6%b7%bb%e5%8a%a0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>理论上reuse不限制你添加版权声明的方式，你可以选择你喜欢的脚本语言来完成这一切。&lt;/p>
&lt;h3 id="为非代码文件添加版权声明">为非代码文件添加版权声明
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e4%b8%ba%e9%9d%9e%e4%bb%a3%e7%a0%81%e6%96%87%e4%bb%b6%e6%b7%bb%e5%8a%a0%e7%89%88%e6%9d%83%e5%a3%b0%e6%98%8e">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>在项目中是存在非代码文件，比如图片，音频等等。部分也是受到版权保护的，但是我们也没法直接在上面操作（别和我说水印和音频水印，这俩玩意不能无损添加）所以我们要使用一个外置的dep5文件去声明这些非代码文件的版权，dep5文件在执行项目&lt;code>init&lt;/code>之后就已经自动帮你生成好了，所以直接写就是的了。比如：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-text" data-lang="text"># css
Files: *.css
Copyright: None
License: CC0-1.0
&lt;/code>&lt;/pre>&lt;/div>&lt;p>第一行就是注释 ，第二行是适合的文件，第三行是版权信息（如果使用cc0,则填写None） ，第四行是使用的licence。第一行的注释你可以选择不写，但是为了以后的可读性，强烈建议你写上，甚至可以作为分类的标签。&lt;/p>
&lt;p>第二行文件，支持使用通配符进行模糊匹配，但是不支持正则表达式：仅支持，&lt;code>*&lt;/code>匹配任意多个字符 &lt;code>？&lt;/code>匹配一个字符。所以可以利用通配符来制定匹配某一个文件夹下面的所有文件&lt;/p>
&lt;p>例子：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-text" data-lang="text"># assets
Files: styleplugins/dstyleplugin/assets/*
Copyright: UnionTech Software Technology Co., Ltd.
License: GPL-3.0-or-later
&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个例子展示了有版权信息的情况&lt;/p>
&lt;p>例子：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-text" data-lang="text"># png svg
Files: platformthemeplugin/icons/* styleplugins/chameleon/menu_shadow.svg styles/images/woodbackground.png
styles/images/woodbutton.png tests/iconengines/builtinengine/icons/actions/icon_Layout_16px.svg
tests/iconengines/svgiconengine/icon_window_16px.svg
Copyright: None
License: CC0-1.0
&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个例子展示了为多个文件路径及文件配置版权信息&lt;/p>
&lt;p>&lt;strong>强烈推荐你使用路径和具体文件名的方式来制定版权信息，而要避免大范围使用通配符的情况&lt;/strong>&lt;/p>
&lt;p>例子：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-text" data-lang="text">Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: qt5integration
Upstream-Contact: UnionTech Software Technology Co., Ltd. &amp;lt;&amp;gt;
Source: https://github.com/linuxdeepin/qt5integration
# README
Files: README.md CHANGELOG.md
Copyright: None
License: CC0-1.0
# assets
Files: styleplugins/dstyleplugin/assets/*
Copyright: UnionTech Software Technology Co., Ltd.
License: GPL-3.0-or-later
# Project file
Files: *.pro *.prf *.pri
Copyright: None
License: CC0-1.0
# css
Files: *.css
Copyright: None
License: CC0-1.0
# qrc
Files: *.qrc
Copyright: None
License: CC0-1.0
# png svg
Files: platformthemeplugin/icons/* styleplugins/chameleon/menu_shadow.svg styles/images/woodbackground.png
styles/images/woodbutton.png tests/iconengines/builtinengine/icons/actions/icon_Layout_16px.svg
tests/iconengines/svgiconengine/icon_window_16px.svg
Copyright: None
License: CC0-1.0
# sh
Files: tests/test-recoverage-qmake.sh
Copyright: None
License: CC0-1.0
# ignore git
Files: .git*
Copyright: None
License: CC0-1.0
# xml toml json conf yaml
Files: *.xml *.toml *.json *conf *.yaml
Copyright: None
License: CC0-1.0
# rpm
Files: rpm/*
Copyright: None
License: CC0-1.0
# debian
Files: debian/*
Copyright: None
License: CC0-1.0
# Arch
Files: archlinux/*
Copyright: None
License: CC0-1.0
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="无需版权声明文件的处理方案">无需版权声明文件的处理方案
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e6%97%a0%e9%9c%80%e7%89%88%e6%9d%83%e5%a3%b0%e6%98%8e%e6%96%87%e4%bb%b6%e7%9a%84%e5%a4%84%e7%90%86%e6%96%b9%e6%a1%88">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>有部分文件是无需版权声明的，比如说某些资源文件脚本文件或者序列化的文件，这种文件有两种处理方式：&lt;/p>
&lt;ul>
&lt;li>&lt;code>.gitignore&lt;/code> 是用来忽略某些与项目无关的文件和编译过程中产生的文件，同样的如果reuse也会忽略在&lt;code>.gitignore&lt;/code>中标记的文件&lt;/li>
&lt;li>&lt;code>dep5&lt;/code> 在执行项目init之后，会在项目目录下产生&lt;code>.reuse/dep5&lt;/code> 文件，打开后能看到官方给的范例，如果一个文件不需要特殊版权声明则使用&lt;code>CC0-1.0&lt;/code>（会放弃对此文件的所有版权）&lt;/li>
&lt;/ul>
&lt;h2 id="检查项目合规">检查项目合规
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e6%a3%80%e6%9f%a5%e9%a1%b9%e7%9b%ae%e5%90%88%e8%a7%84">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">reuse lint
&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个命令会列出项目文件数，如果项目已经合规，将会以&lt;code>：-）&lt;/code>提示，如果项目有些文件不符合开源许可证规范将会以&lt;code>：-（&lt;/code>提示，此时你就需要按照上面的提示进行相应的修改即可。&lt;/p>
&lt;p>一般情况下，每个文件都需要有与之相关的版权和许可信息。REUSE规范详细说明了几种方法。总的来说，有这些方法：&lt;/p>
&lt;ul>
&lt;li>将标签放在文件的标题中。&lt;/li>
&lt;li>将标签放置在与&lt;code>.license&lt;/code>文件相邻的文件中。&lt;/li>
&lt;li>将信息放入 &lt;code>DEP5&lt;/code> 文件中。&lt;/li>
&lt;/ul>
&lt;p>如果发现一个文件没有与之关联的版权和/或许可信息，则该项目不合规。&lt;/p>
&lt;h2 id="个人建议">个人建议
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e4%b8%aa%e4%ba%ba%e5%bb%ba%e8%ae%ae">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>个人建议如果对现有项目使用reuse配置且需要保留版权的时间信息，不要使用一键配置版权头的方式，而是进行手动替换，这里推荐使用vscode的文件筛选功能，筛选出你需要添加的文件，然后cv下去。在配置dep5文件的时候，一定小心通配符的范围，不宜过大。并且对于第三方版权文件一定得小心规避，同样需要完整保留第三方版权信息。&lt;/p>
&lt;h2 id="统信软件常见dep5书写参考">统信软件常见Dep5书写参考
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e7%bb%9f%e4%bf%a1%e8%bd%af%e4%bb%b6%e5%b8%b8%e8%a7%81dep5%e4%b9%a6%e5%86%99%e5%8f%82%e8%80%83">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>参考详细见：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-text" data-lang="text">Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: dtkgui
Upstream-Contact: UnionTech Software Technology Co., Ltd. &amp;lt;&amp;gt;
Source: https://github.com/linuxdeepin/dtkgui
# ci
Files: .github/* .gitlab-ci.yml
Copyright: None
License: CC0-1.0
# gitignore #
Files: .gitignore
Copyright: None
License: CC0-1.0
# json conf yaml
Files: *.json *conf *.yaml
Copyright: None
License: CC0-1.0
#interface
Files: src/util/D* src/kernel/D* src/filedrag/D*
Copyright: None
License: CC0-1.0
# rpm
Files: rpm/*
Copyright: None
License: CC0-1.0
# debian
Files: debian/*
Copyright: None
License: LGPL-3.0-or-later
# Arch
Files: archlinux/*
Copyright: None
License: CC0-1.0
# README&amp;amp;doc
Files: README.md doc/src/*.qdoc
Copyright: None
License: CC-BY-4.0
# DBus
Files: src/dbus/*.xml
Copyright: None
License: CC0-1.0
# Project file
Files: *.pro *.prf *.pri *.qrc *CMakeLists.txt
Copyright: None
License: CC0-1.0
# svg
Files: src/util/icons/actions/* src/util/icons/icons/* src/util/icons/texts/*
tests/images/logo_icon.svg
Copyright: UnionTech Software Technology Co., Ltd.
License: LGPL-3.0-or-later
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="常见开源协议选择">常见开源协议选择
&lt;span>
&lt;a href="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/#%e5%b8%b8%e8%a7%81%e5%bc%80%e6%ba%90%e5%8d%8f%e8%ae%ae%e9%80%89%e6%8b%a9">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>一般情况下对于文档信息使用&lt;code>CC-BY-4.0&lt;/code> 对于我们的一般开源项目使用&lt;code>LGPL-3.0-or-later&lt;/code>许可证，如果遇到第三方文件，则是需要保留原有许可证，保留原版版权声明。&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>lambda</title><link>https://blog.deepin.org/posts/lambda/</link><pubDate>Sat, 09 Jul 2022 18:23:58 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/lambda/</guid><description>&lt;p>这里整理一些关于&lt;code>c++&lt;/code>中的匿名函数的知识.&lt;/p>
&lt;p>《C++ primer》上有的内容就不在重述了.这里重点讲一些primer上&lt;strong>可能&lt;/strong>没有的东西.&lt;/p>
&lt;h2 id="捕获的时机">捕获的时机
&lt;span>
&lt;a href="https://blog.deepin.org/posts/lambda/#%e6%8d%95%e8%8e%b7%e7%9a%84%e6%97%b6%e6%9c%ba">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>primer 向我们介绍到: 有两种捕获变量的方式, 值捕获和引用捕获. 其中:&lt;/p>
&lt;blockquote>
&lt;p>&amp;hellip;&lt;/p>
&lt;p>与参数不同,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝&lt;/p>
&lt;p>&amp;hellip;&lt;/p>
&lt;p>如果我们采用引用方式捕获一个变量,就必须确保被引用的对象在lambda执行的时候是存
在的.lambda捕获的都是局部变量,这些变量在函数结束后就不复存在了.&lt;/p>
&lt;p>&amp;hellip;&lt;/p>
&lt;/blockquote>
&lt;p>我们看一段代码:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#a6e22e">funtionTimesMod1&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> mod) &amp;#123;
&lt;span style="color:#66d9ef">int&lt;/span> variableA &lt;span style="color:#f92672">=&lt;/span> mod;
&lt;span style="color:#66d9ef">auto&lt;/span> f &lt;span style="color:#f92672">=&lt;/span> [variableA](&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b) &amp;#123; &lt;span style="color:#66d9ef">return&lt;/span> a &lt;span style="color:#f92672">%&lt;/span> variableA &lt;span style="color:#f92672">*&lt;/span> b &lt;span style="color:#f92672">%&lt;/span> variableA; };
&lt;span style="color:#66d9ef">return&lt;/span> f;
}
&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">test1&lt;/span>() &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"---test1---"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">int&lt;/span> a &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>, b &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">20&lt;/span>, c &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">7&lt;/span>;
&lt;span style="color:#66d9ef">auto&lt;/span> times &lt;span style="color:#f92672">=&lt;/span> funtionTimesMod1(c);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> times(a, b) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里&lt;code>funtionTimesMod1(int mod)&lt;/code>返回一个签名为&lt;code>int(int, int)&lt;/code>的函数,这个函数计算
两个参数相乘对&lt;code>mod&lt;/code>取模的结果.&lt;/p>
&lt;p>当然可以正常运行,结果为&lt;code>4&lt;/code>,如果我们将它换成引用捕获:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#a6e22e">funtionTimesMod2&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> mod) &amp;#123;
&lt;span style="color:#66d9ef">int&lt;/span> variableA &lt;span style="color:#f92672">=&lt;/span> mod;
&lt;span style="color:#66d9ef">auto&lt;/span> f &lt;span style="color:#f92672">=&lt;/span> [&lt;span style="color:#f92672">&amp;amp;&lt;/span>variableA](&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b) &amp;#123; &lt;span style="color:#66d9ef">return&lt;/span> a &lt;span style="color:#f92672">%&lt;/span> variableA &lt;span style="color:#f92672">*&lt;/span> b &lt;span style="color:#f92672">%&lt;/span> variableA; };
&lt;span style="color:#66d9ef">return&lt;/span> f;
}
&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">test2&lt;/span>() &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"---test2---"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">int&lt;/span> a &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>, b &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">20&lt;/span>, c &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">7&lt;/span>;
&lt;span style="color:#66d9ef">auto&lt;/span> times &lt;span style="color:#f92672">=&lt;/span> funtionTimesMod2(c);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> times(a, b) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>这段代码就已经不能正常工作了,它的输出是 &lt;code>0&lt;/code>.&lt;/p>
&lt;p>我们来看看为啥:&lt;/p>
&lt;pre>&lt;code>Process 15820 stopped
* thread #1, name = 'tmp', stop reason = step in
frame #0: 0x000000000040096b tmp`funtionTimesMod2(mod=7) at tmp.cpp:17
14 }
15
16 auto funtionTimesMod2(int mod) &amp;#123;
-&amp;gt; 17 int variableA = mod;
18 auto f = [&amp;amp;variableA](int a, int b) &amp;#123; return a % variableA * b % variableA; };
19 return f;
20 }
(lldb) p &amp;amp;variableA
(int *) $1 = 0x00007fffffffe120
Process 15820 stopped
* thread #1, name = 'tmp', stop reason = step over
frame #0: 0x00000000004009dc tmp`test2() at tmp.cpp:25
22 cout &amp;lt;&amp;lt; &amp;quot;---test2---&amp;quot; &amp;lt;&amp;lt; endl;
23 int a = 10, b = 20, c = 7;
24 auto times = funtionTimesMod2(c);
-&amp;gt; 25 cout &amp;lt;&amp;lt; times(a, b) &amp;lt;&amp;lt; endl;
26 }
27 auto funtionTimesMod3(int mod) &amp;#123;
28 int variableA = mod;
(lldb) p times
((anonymous class)) $2 = &amp;#123;
variableA = 0x00007fffffffe120
}
&lt;/code>&lt;/pre>
&lt;p>可以看到&lt;code>times&lt;/code>这个对象中保存下来的&lt;code>variableA&lt;/code>只是一个指针,它指向我们之前创建的
局部变量&lt;code>variableA&lt;/code>,这个地址在栈上,这意味着当我们真的调用&lt;code>times&lt;/code>的时候,局部变
量&lt;code>variableA&lt;/code>所在的那片内存已经被使用过了.所以会返回错误的结果.接下来,我们可以
看到实际上调用的时候&lt;code>variableA&lt;/code>里面是&lt;code>20&lt;/code>,这是因为刚好参数&lt;code>b&lt;/code>被放置在
了&lt;code>variableA&lt;/code>之前的位置上.&lt;/p>
&lt;pre>&lt;code>Process 21271 stopped
* thread #1, name = 'tmp', stop reason = step in
frame #0: 0x0000000000400a32 tmp`funtionTimesMod2(this=0x00007fffffffe158, a=10, b=20)::$_1::operator()(int, int) const at tmp.cpp:18
15
16 auto funtionTimesMod2(int mod) &amp;#123;
17 int variableA = mod;
-&amp;gt; 18 auto f = [&amp;amp;variableA](int a, int b) &amp;#123; return a % variableA * b % variableA; };
19 return f;
20 }
21 void test2() &amp;#123;
(lldb) p variableA
(int) $3 = 20
(lldb) p &amp;amp;b
(int *) $4 = 0x00007fffffffe120
&lt;/code>&lt;/pre>
&lt;h2 id="lambda-的实现">lambda 的实现
&lt;span>
&lt;a href="https://blog.deepin.org/posts/lambda/#lambda-%e7%9a%84%e5%ae%9e%e7%8e%b0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>根据《C++ Primer》在10.3以及14.8.1的介绍,我们知道一个lambda表达式是由编译器负责
翻译成一个&lt;strong>没有类名,重载过调用运算符&lt;/strong>的对象的.&lt;/p>
&lt;p>这解释了为什么我们只能用&lt;code>auto&lt;/code>来定义一个lambda类型的变量,因为这个类是没有名字的,
或者说它的名字是编译器自己生成的,我们并不能知道它叫什么.&lt;/p>
&lt;p>这听起来很美好,我们也确实可以写出代码,用一个自己创建的,重载过调用运算符的对象,来
模拟一个lambda:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">INT&lt;/span> &amp;#123;
&lt;span style="color:#66d9ef">public&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;span style="color:#66d9ef">int&lt;/span> num;
INT(&lt;span style="color:#66d9ef">const&lt;/span> INT &lt;span style="color:#f92672">&amp;amp;&lt;/span>i) &lt;span style="color:#f92672">:&lt;/span> num(i.num) &amp;#123; cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"Copy constructor"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl; }
INT() &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">default&lt;/span>;
};
&lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#a6e22e">funtionTimesMod3&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> mod) &amp;#123;
INT variableA;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----A"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
variableA.num &lt;span style="color:#f92672">=&lt;/span> mod;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----B"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">auto&lt;/span> f &lt;span style="color:#f92672">=&lt;/span> [variableA](&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b) &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----C"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> a &lt;span style="color:#f92672">%&lt;/span> variableA.num &lt;span style="color:#f92672">*&lt;/span> b &lt;span style="color:#f92672">%&lt;/span> variableA.num;
};
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----D"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> f;
}
&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">test3&lt;/span>() &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"---test3---"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">int&lt;/span> a &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>, b &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">20&lt;/span>, c &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">7&lt;/span>;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----E"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">auto&lt;/span> times &lt;span style="color:#f92672">=&lt;/span> funtionTimesMod3(c);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----F"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> times(a, b) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----G"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
}
&lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#a6e22e">funtionTimesMod4&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> mod) &amp;#123;
INT variableA;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----A"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
variableA.num &lt;span style="color:#f92672">=&lt;/span> mod;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----B"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">function&lt;/span> &amp;#123;
&lt;span style="color:#66d9ef">const&lt;/span> INT variableA;
&lt;span style="color:#66d9ef">public&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
function(&lt;span style="color:#66d9ef">const&lt;/span> INT &lt;span style="color:#f92672">&amp;amp;&lt;/span>variableA) &lt;span style="color:#f92672">:&lt;/span> variableA(variableA) &amp;#123;}
&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#66d9ef">operator&lt;/span>()(&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b) &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----C"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> a &lt;span style="color:#f92672">%&lt;/span> variableA.num &lt;span style="color:#f92672">*&lt;/span> b &lt;span style="color:#f92672">%&lt;/span> variableA.num;
}
} f(variableA);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----D"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> f;
}
&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">test4&lt;/span>() &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"---test4---"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">int&lt;/span> a &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>, b &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">20&lt;/span>, c &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">7&lt;/span>;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----E"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">auto&lt;/span> times &lt;span style="color:#f92672">=&lt;/span> funtionTimesMod4(c);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----F"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> times(a, b) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----G"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>为了观察发生的值拷贝的时机,以确定我们自己写的这个类的行为和lambda真的完全一致,我
们可以自定义一个class,并且让他发生拷贝构造的时候打印一些信息.&lt;/p>
&lt;p>运行结果如下:&lt;/p>
&lt;pre>&lt;code>---test3---
----E
----A
----B
Copy constructor
----D
----F
----C
4
----G
---test4---
----E
----A
----B
Copy constructor
----D
----F
----C
4
----G
&lt;/code>&lt;/pre>
&lt;p>这又一次说明了说明捕获引起的值拷贝发生在lambda被初始化的时候.&lt;/p>
&lt;p>看起来这两个东西的行为几乎一致, 但是真的是这样吗?&lt;/p>
&lt;h2 id="编译器的优化">编译器的优化
&lt;span>
&lt;a href="https://blog.deepin.org/posts/lambda/#%e7%bc%96%e8%af%91%e5%99%a8%e7%9a%84%e4%bc%98%e5%8c%96">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>编译器会对我们写的代码做出一些优化,以减少复制对象的次数.如果不了解这点可以看看&lt;a href="https://www.cnblogs.com/kekec/p/11303391.html">
这篇&lt;/a>博客.&lt;/p>
&lt;p>如果我们关闭返回值优化,那么运行的结果是这样的:&lt;/p>
&lt;pre>&lt;code>---test3---
----E
----A
----B
Copy constructor
Copy constructor
----D
Copy constructor
Copy constructor
----F
----C
4
----G
---test4---
----E
----A
----B
Copy constructor
----D
Copy constructor
Copy constructor
----F
----C
4
----G
&lt;/code>&lt;/pre>
&lt;p>可以看到我们自己写的类,少了一次拷贝构造.&lt;/p>
&lt;p>我们先来解释一下lambda为什么会发生这么多次拷贝构造.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#a6e22e">funtionTimesMod3&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> mod) &amp;#123;
INT variableA; &lt;span style="color:#75715e">// 5
&lt;/span>&lt;span style="color:#75715e">&lt;/span> cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----A"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl; &lt;span style="color:#75715e">// 6
&lt;/span>&lt;span style="color:#75715e">&lt;/span> variableA.num &lt;span style="color:#f92672">=&lt;/span> mod; &lt;span style="color:#75715e">// 7
&lt;/span>&lt;span style="color:#75715e">&lt;/span> cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----B"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl; &lt;span style="color:#75715e">// 8
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">auto&lt;/span> f &lt;span style="color:#f92672">=&lt;/span> [variableA](&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b) &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----C"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl; &lt;span style="color:#75715e">// 14
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">return&lt;/span> a &lt;span style="color:#f92672">%&lt;/span> variableA.num &lt;span style="color:#f92672">*&lt;/span> b &lt;span style="color:#f92672">%&lt;/span> variableA.num; &lt;span style="color:#75715e">//15
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }; &lt;span style="color:#75715e">// 9
&lt;/span>&lt;span style="color:#75715e">&lt;/span> cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----D"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl; &lt;span style="color:#75715e">// 10
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">return&lt;/span> f; &lt;span style="color:#75715e">// 11
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">test3&lt;/span>() &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"---test3---"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl; &lt;span style="color:#75715e">// 1
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> a &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>, b &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">20&lt;/span>, c &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">7&lt;/span>; &lt;span style="color:#75715e">// 2
&lt;/span>&lt;span style="color:#75715e">&lt;/span> cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----E"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl; &lt;span style="color:#75715e">// 3
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">auto&lt;/span> times &lt;span style="color:#f92672">=&lt;/span> funtionTimesMod3(c); &lt;span style="color:#75715e">// 4
&lt;/span>&lt;span style="color:#75715e">&lt;/span> cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----F"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl; &lt;span style="color:#75715e">// 12
&lt;/span>&lt;span style="color:#75715e">&lt;/span> cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> times(a, b) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl; &lt;span style="color:#75715e">// 13
&lt;/span>&lt;span style="color:#75715e">&lt;/span> cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----G"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl; &lt;span style="color:#75715e">//16
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>首先我们的程序是按照如上的顺序运行的.&lt;/p>
&lt;p>可以看到前两个拷贝构造发生在9,而第3、4次发生在11.&lt;/p>
&lt;p>如果完全按照语义来看的话,9这句话可以有两种理解方式:&lt;/p>
&lt;ol>
&lt;li>创建一个lambda对象,对象名字叫f,这个对象的内容就是后面那个lambda表达式.&lt;/li>
&lt;li>创建一个lambda表达式,然后将其作为参数,调用同类型对象f的拷贝构造函数.&lt;/li>
&lt;/ol>
&lt;p>如果按照第一种方法来理解,那么第9行发生两次拷贝构造就不是很能理解了.&lt;/p>
&lt;p>所以应该是第二种.&lt;/p>
&lt;p>在10之后也发生了两次拷贝调用.应该是先建立了一个变量用来做返回值,比如说叫r,然后
将f赋值给返回值变量r,然后返回值变量r在被赋值给test3()中的times,这样发生的两次拷
贝构造.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#a6e22e">funtionTimesMod4&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> mod) &amp;#123;
INT variableA;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----A"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
variableA.num &lt;span style="color:#f92672">=&lt;/span> mod;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----B"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">function&lt;/span> &amp;#123;
&lt;span style="color:#66d9ef">const&lt;/span> INT variableA;
&lt;span style="color:#66d9ef">public&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
function(&lt;span style="color:#66d9ef">const&lt;/span> INT &lt;span style="color:#f92672">&amp;amp;&lt;/span>variableA) &lt;span style="color:#f92672">:&lt;/span> variableA(variableA) &amp;#123;}
&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#66d9ef">operator&lt;/span>()(&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b) &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----C"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> a &lt;span style="color:#f92672">%&lt;/span> variableA.num &lt;span style="color:#f92672">*&lt;/span> b &lt;span style="color:#f92672">%&lt;/span> variableA.num;
}
} f(variableA); &lt;span style="color:#75715e">// &amp;lt;- 这里
&lt;/span>&lt;span style="color:#75715e">&lt;/span> cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----D"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> f;
}
&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">test4&lt;/span>() &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"---test4---"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">int&lt;/span> a &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>, b &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">20&lt;/span>, c &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">7&lt;/span>;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----E"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">auto&lt;/span> times &lt;span style="color:#f92672">=&lt;/span> funtionTimesMod4(c);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----F"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> times(a, b) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----G"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>之前自己模拟lambda的这个代码,我们在注释标注的那个位置的实现和lambda中,&amp;ldquo;先建一个
右值,然后拷贝构造出f&amp;quot;的行为不太一样.导致这里少了一次拷贝构造.所以实际上应该是这
样:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#a6e22e">funtionTimesMod5&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> mod) &amp;#123;
INT variableA;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----A"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
variableA.num &lt;span style="color:#f92672">=&lt;/span> mod;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----B"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">function&lt;/span> &amp;#123;
&lt;span style="color:#66d9ef">const&lt;/span> INT variableA;
&lt;span style="color:#66d9ef">public&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
function(&lt;span style="color:#66d9ef">const&lt;/span> INT &lt;span style="color:#f92672">&amp;amp;&lt;/span>variableA) &lt;span style="color:#f92672">:&lt;/span> variableA(variableA) &amp;#123;}
&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#66d9ef">operator&lt;/span>()(&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b) &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----C"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> a &lt;span style="color:#f92672">%&lt;/span> variableA.num &lt;span style="color:#f92672">*&lt;/span> b &lt;span style="color:#f92672">%&lt;/span> variableA.num;
}
};
function f &lt;span style="color:#f92672">=&lt;/span> function(variableA);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----D"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> f;
}
&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">test5&lt;/span>() &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"---test5---"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">int&lt;/span> a &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>, b &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">20&lt;/span>, c &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">7&lt;/span>;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----E"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">auto&lt;/span> times &lt;span style="color:#f92672">=&lt;/span> funtionTimesMod5(c);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----F"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> times(a, b) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----G"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>实际上没必要纠结那么多,正常编译的话,其实是不会去先建一个右值的对象的.&lt;/p>
&lt;h2 id="代码">代码
&lt;span>
&lt;a href="https://blog.deepin.org/posts/lambda/#%e4%bb%a3%e7%a0%81">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;bits/stdc++.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">using&lt;/span> &lt;span style="color:#66d9ef">namespace&lt;/span> std;
&lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#a6e22e">funtionTimesMod1&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> mod) &amp;#123;
&lt;span style="color:#66d9ef">int&lt;/span> variableA &lt;span style="color:#f92672">=&lt;/span> mod;
&lt;span style="color:#66d9ef">auto&lt;/span> f &lt;span style="color:#f92672">=&lt;/span> [variableA](&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b) &amp;#123; &lt;span style="color:#66d9ef">return&lt;/span> a &lt;span style="color:#f92672">%&lt;/span> variableA &lt;span style="color:#f92672">*&lt;/span> b &lt;span style="color:#f92672">%&lt;/span> variableA; };
&lt;span style="color:#66d9ef">return&lt;/span> f;
}
&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">test1&lt;/span>() &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"---test1---"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">int&lt;/span> a &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>, b &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">20&lt;/span>, c &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">7&lt;/span>;
&lt;span style="color:#66d9ef">auto&lt;/span> times &lt;span style="color:#f92672">=&lt;/span> funtionTimesMod1(c);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> times(a, b) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
}
&lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#a6e22e">funtionTimesMod2&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> mod) &amp;#123;
&lt;span style="color:#66d9ef">int&lt;/span> variableA &lt;span style="color:#f92672">=&lt;/span> mod;
&lt;span style="color:#66d9ef">auto&lt;/span> f &lt;span style="color:#f92672">=&lt;/span> [&lt;span style="color:#f92672">&amp;amp;&lt;/span>variableA](&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b) &amp;#123; &lt;span style="color:#66d9ef">return&lt;/span> a &lt;span style="color:#f92672">%&lt;/span> variableA &lt;span style="color:#f92672">*&lt;/span> b &lt;span style="color:#f92672">%&lt;/span> variableA; };
&lt;span style="color:#66d9ef">return&lt;/span> f;
}
&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">test2&lt;/span>() &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"---test2---"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">int&lt;/span> a &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>, b &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">20&lt;/span>, c &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">7&lt;/span>;
&lt;span style="color:#66d9ef">auto&lt;/span> times &lt;span style="color:#f92672">=&lt;/span> funtionTimesMod2(c);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> times(a, b) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
}
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">INT&lt;/span> &amp;#123;
&lt;span style="color:#66d9ef">public&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
&lt;span style="color:#66d9ef">int&lt;/span> num;
INT(&lt;span style="color:#66d9ef">const&lt;/span> INT &lt;span style="color:#f92672">&amp;amp;&lt;/span>i) &lt;span style="color:#f92672">:&lt;/span> num(i.num) &amp;#123; cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"Copy constructor"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl; }
INT() &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">default&lt;/span>;
};
&lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#a6e22e">funtionTimesMod3&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> mod) &amp;#123;
INT variableA;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----A"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
variableA.num &lt;span style="color:#f92672">=&lt;/span> mod;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----B"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">auto&lt;/span> f &lt;span style="color:#f92672">=&lt;/span> [variableA](&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b) &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----C"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> a &lt;span style="color:#f92672">%&lt;/span> variableA.num &lt;span style="color:#f92672">*&lt;/span> b &lt;span style="color:#f92672">%&lt;/span> variableA.num;
};
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----D"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> f;
}
&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">test3&lt;/span>() &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"---test3---"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">int&lt;/span> a &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>, b &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">20&lt;/span>, c &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">7&lt;/span>;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----E"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">auto&lt;/span> times &lt;span style="color:#f92672">=&lt;/span> funtionTimesMod3(c);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----F"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> times(a, b) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----G"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
}
&lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#a6e22e">funtionTimesMod4&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> mod) &amp;#123;
INT variableA;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----A"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
variableA.num &lt;span style="color:#f92672">=&lt;/span> mod;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----B"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">function&lt;/span> &amp;#123;
&lt;span style="color:#66d9ef">const&lt;/span> INT variableA;
&lt;span style="color:#66d9ef">public&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
function(&lt;span style="color:#66d9ef">const&lt;/span> INT &lt;span style="color:#f92672">&amp;amp;&lt;/span>variableA) &lt;span style="color:#f92672">:&lt;/span> variableA(variableA) &amp;#123;}
&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#66d9ef">operator&lt;/span>()(&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b) &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----C"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> a &lt;span style="color:#f92672">%&lt;/span> variableA.num &lt;span style="color:#f92672">*&lt;/span> b &lt;span style="color:#f92672">%&lt;/span> variableA.num;
}
} f(variableA);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----D"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> f;
}
&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">test4&lt;/span>() &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"---test4---"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">int&lt;/span> a &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>, b &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">20&lt;/span>, c &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">7&lt;/span>;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----E"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">auto&lt;/span> times &lt;span style="color:#f92672">=&lt;/span> funtionTimesMod4(c);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----F"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> times(a, b) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----G"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
}
&lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#a6e22e">funtionTimesMod5&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> mod) &amp;#123;
INT variableA;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----A"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
variableA.num &lt;span style="color:#f92672">=&lt;/span> mod;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----B"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">function&lt;/span> &amp;#123;
&lt;span style="color:#66d9ef">const&lt;/span> INT variableA;
&lt;span style="color:#66d9ef">public&lt;/span>&lt;span style="color:#f92672">:&lt;/span>
function(&lt;span style="color:#66d9ef">const&lt;/span> INT &lt;span style="color:#f92672">&amp;amp;&lt;/span>variableA) &lt;span style="color:#f92672">:&lt;/span> variableA(variableA) &amp;#123;}
&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#66d9ef">operator&lt;/span>()(&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b) &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----C"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> a &lt;span style="color:#f92672">%&lt;/span> variableA.num &lt;span style="color:#f92672">*&lt;/span> b &lt;span style="color:#f92672">%&lt;/span> variableA.num;
}
};
function f &lt;span style="color:#f92672">=&lt;/span> function(variableA);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----D"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">return&lt;/span> f;
}
&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">test5&lt;/span>() &amp;#123;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"---test5---"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">int&lt;/span> a &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>, b &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">20&lt;/span>, c &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">7&lt;/span>;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----E"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
&lt;span style="color:#66d9ef">auto&lt;/span> times &lt;span style="color:#f92672">=&lt;/span> funtionTimesMod5(c);
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----F"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> times(a, b) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"----G"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> endl;
}
&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() &amp;#123;
test1();
test2();
test3();
test4();
test5();
}
&lt;/code>&lt;/pre>&lt;/div></description><author>deepin 社区官方博客</author></item><item><title>使用 Bcache 对机械硬盘加速</title><link>https://wrj97.com/posts/bcache-in-deepin/</link><pubDate>Sat, 18 Jun 2022 16:09:58 +0000</pubDate><guid isPermaLink="true">https://wrj97.com/posts/bcache-in-deepin/</guid><description>&lt;p>Bcache 是 Linux 下的一个块缓存内核模块，可用于使用单个固态硬盘为一个或多个机械硬盘加速，也可用于使用本地磁盘为网络磁盘的加速。&lt;/p>
&lt;p>Bcache 有比较灵活的缓存模式，支持安全读写缓存(writethrough)、高性能读写缓存(writeback)、只读缓存(writearound)、停用缓存(none)等模式，并且可在线动态调整。&lt;/p>
&lt;p>Bcache 还会自动识别顺序写入，当发现正在进行顺序写入时跳过缓存层，以减少缓存设备(固态硬盘)的损耗，延长固态硬盘的寿命。&lt;/p></description><author>myml</author></item><item><title>DDE-Dock 自启动管理插件 成果展示</title><link>https://y-aang.github.io/posts/%E6%88%90%E6%9E%9C%E5%85%AC%E7%A4%BAdde-dock/?utm_source=atom_feed</link><pubDate>Mon, 30 May 2022 06:00:00 +0000</pubDate><guid isPermaLink="true">https://y-aang.github.io/posts/%E6%88%90%E6%9E%9C%E5%85%AC%E7%A4%BAdde-dock/</guid><description>&lt;p>DDE 自启动管理插件可用于集中管理&lt;code>Deepin&lt;/code>系统中软件开机自启动设置的插件。&lt;/p>
&lt;p>项目链接：&lt;a href="https://github.com/PinappleUnderTheSea/os223.git">DDE 自启动管理插件 github仓库&lt;/a>&lt;/p>
&lt;h2 id="参赛信息">参赛信息&lt;/h2>
&lt;p>本项目参加2023全国大学生计算机系统能力大赛操作系统设计赛-功能挑战赛，选题为&lt;a href="https://github.com/oscomp/proj223-control-center-startup-management-plugin">proj223-control-center-startup-management-plugin&lt;/a>。&lt;/p>
&lt;p>参赛小组：&lt;/p>
&lt;ul>
&lt;li>复旦大学 朱元依&lt;/li>
&lt;li>复旦大学 沈扬&lt;/li>
&lt;li>复旦大学 朱俊杰&lt;/li>
&lt;/ul>
&lt;p>指导老师：&lt;/p>
&lt;ul>
&lt;li>复旦大学 张亮&lt;/li>
&lt;li>复旦大学 陈辰&lt;/li>
&lt;li>企业导师 王子冲&lt;/li>
&lt;/ul>
&lt;h2 id="完成情况">完成情况&lt;/h2>
&lt;p>项目文档位于根目录的&lt;code>初赛报告.md&lt;/code>。&lt;br />
博客地址：https://y-aang.github.io （题目：DDE 自启动管理插件）&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>任务&lt;/th>
&lt;th>完成情况&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>（必须）完成一个控制中心插件，能够展示当前所有开机启动项的列表&lt;/td>
&lt;td>完成&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>（必须）插件以单独的仓库提供，并能够单独构建，不需要合并入 dde-control-center 项目&lt;/td>
&lt;td>完成&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>（必须）能够在插件中，通过用户界面的交互来管理（添加、删除、启用、禁用）开机启动项&lt;/td>
&lt;td>完成&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>（必须）编写博客，记录开发过程的心得与体会，并将博客投递至 planet.deepin.org&lt;/td>
&lt;td>完成博客编写，待deepin官方审核&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="成果展示">成果展示&lt;/h2>
&lt;p>功能展示视频链接：&lt;/p>
&lt;p>链接：&lt;a href="https://pan.baidu.com/s/1-9B3n-61Sh7Jaq4SKp4WSw?pwd=hoo8">百度网盘链接&lt;/a>&lt;/p>
&lt;p>提取码：hoo8&lt;/p>
&lt;p>插件图标：&lt;/p>
&lt;p>&lt;img alt="图标" src="https://y-aang.github.io/img/%E5%9B%BE%E6%A0%87.png" />&lt;/p>
&lt;p>自启动管理窗口：&lt;/p>
&lt;p>&lt;img alt="前端界面" src="https://y-aang.github.io/img/%E5%89%8D%E7%AB%AF%E7%95%8C%E9%9D%A2.png" />&lt;/p>
&lt;h2 id="部署方式">部署方式&lt;/h2>
&lt;h3 id="1系统环境">1、系统环境&lt;/h3>
&lt;p>开发环境：Deepin 20Beta版&lt;/p>
&lt;p>系统架构：x86&lt;/p>
&lt;p>镜像下载链接：&lt;a href="https://uni.mirrors.163.com/deepin-cd/20/deepin-desktop-community-1003-amd64.iso">Deepin 操作系统下载链接&lt;/a>&lt;/p>
&lt;p>虚拟机平台：WMware Workstation 16Pro&lt;/p>
&lt;p>操作系统环境搭建参考博客：&lt;a href="https://blog.csdn.net/qq_44133136/article/details/105887560">环境搭建博客&lt;/a>&lt;/p>
&lt;h3 id="2安装依赖库">2、安装依赖库&lt;/h3>
&lt;p>安装包 &lt;code>build-essential&lt;/code>、&lt;code>git&lt;/code>、&lt;code>g++&lt;/code>、&lt;code>cmake&lt;/code>、&lt;code>dde&lt;/code>、&lt;code>dtk&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>sudo apt install build-essential git g++ cmake
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo apt install dde-dock-dev libdtkwidget-dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="3安装插件">3、安装插件&lt;/h3>
&lt;p>克隆该仓库，进入文件夹后以&lt;code>sudo&lt;/code>权限运行&lt;code>install.sh&lt;/code>脚本&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;" tabindex="0">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>git clone https://github.com/PinappleUnderTheSea/os223.git
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd os233
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo su
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sh install.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>出现&lt;code>成果展示&lt;/code>章节的图标，即为安装成功。&lt;/p>
&lt;h2 id="提交仓库目录和文件描述">提交仓库目录和文件描述&lt;/h2>
&lt;p>. &lt;br />
├── CMakeLists.txt &lt;br />
├── README.md &lt;br />
├── aboutdialog.cpp    #关于窗口的实现文件 &lt;br />
├── aboutdialog.h     #关于窗口的头文件 &lt;br />
├── aboutdialog.ui     #关于窗口的UI文件 &lt;br />
├── appletwidget.cpp   #自启动管理窗口的实现文件 &lt;br />
├── appletwidget.h    #自启动管理窗口的头文件 &lt;br />
├── images        #图片 &lt;br />
│ ├── QT_IDE.png &lt;br />
│ ├── QT_前端.png &lt;br />
│ ├── deepin自启动修改.png &lt;br />
│ ├── 类图.jpg &lt;br />
│ ├── 右键.png &lt;br />
│ ├── 图标.png &lt;br />
│ ├── 结果.png &lt;br />
│ ├── 中期类图.jpg &lt;br />
│ ├── 中期测试.png &lt;br />
│ └── 前端界面.png &lt;br />
├── install.sh        #插件安装脚本 &lt;br />
├── main_aboutdialog_test.cpp #关于窗口的测试文件 &lt;br />
├── main_test.cpp      #测试文件 &lt;br />
├── mainwidget.cpp     #插件类的实现文件 &lt;br />
├── mainwidget.h      #插件类的头文件 &lt;br />
├── self_startup.json     #插件的元数据文件，指明了当前插件所使用的 dde-dock 的接口版本 &lt;br />
├── self_startup.pro     #辅助 cmake 的配置文件 &lt;br />
├── self_startup.qrc     #用于展示插件图片 &lt;br />
├── selfstartupplugin.cpp   #部件类的实现文件 &lt;br />
├── selfstartupplugin.h    #部件类的头文件 &lt;br />
├── uninstall.sh        #插件卸载脚本 &lt;br />
├── 初赛报告.md &lt;br />
└── 过程文档.md&lt;/p>
&lt;h2 id="分工">分工&lt;/h2>
&lt;p>朱元依：插件类框架开发、部件类前端开发&lt;/p>
&lt;p>沈扬：自启动管理功能逻辑设计、插件类右键功能开发&lt;/p>
&lt;p>朱俊杰：自启动管理窗口后端接口开发（添加、删除、启用、禁用）&lt;/p>
&lt;h2 id="开发计划">开发计划&lt;/h2>
&lt;h3 id="第一步49418">第一步（4/9～4/18）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 调研&lt;code>Deepin&lt;/code>、&lt;code>dde-dock&lt;/code>、&lt;code>QT&lt;/code>框架等相关内容&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 设计项目方案&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 分工&lt;/li>
&lt;/ul>
&lt;h3 id="第二步41952">第二步（4/19～5/2）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 搭建主体插件类的框架&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 设计启动项管理窗口的前端展示页面&lt;/li>
&lt;/ul>
&lt;h3 id="第三步53513">第三步（5/3～5/13）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 开发部件类接口&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 完善插件类功能&lt;/li>
&lt;/ul>
&lt;h3 id="第四步514521">第四步（5/14～5/21）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 插件类右键功能开发&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 完成配置文件&lt;/li>
&lt;/ul>
&lt;h3 id="第五步522531">第五步（5/22～5/31）&lt;/h3>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> Debug&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox" /> 撰写文档&lt;/li>
&lt;/ul></description><author>dde 自启动管理插件开发小组博客</author></item><item><title>使用 if constexpr 实现条件编译</title><link>https://blog.deepin.org/posts/if-constexpr/</link><pubDate>Thu, 10 Mar 2022 13:03:27 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/if-constexpr/</guid><description>&lt;p>在项目开发中，我们通常会使用条件编译对代码进行裁剪，选择性地排除不需要的代码，比如在某个平台下完全不支持某个功能，那么这个功能就不应该被编译。&lt;/p>
&lt;p>一般我们使用宏来判断代码，选择性的挑选需要编译的部分，并在构建系统中开启这样的条件。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#75715e">#ifdef XXXXXXXXXX
&lt;/span>&lt;span style="color:#75715e">&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"hello world!"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;span style="color:#75715e">#else
&lt;/span>&lt;span style="color:#75715e">&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"good bye!"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;span style="color:#75715e">#endif
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 C 语言的项目中，这样的行为是很正常的，甚至在 C++ 项目中，我们也会选择使用宏进行条件判断。&lt;/p>
&lt;p>但是如果定义的宏多了，则很容易导致代码碎片化，并且无法直观地看到工作流程。&lt;/p>
&lt;p>例如这样的代码:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#75715e">#define KWIN_VERSION_CHECK(major, minor, patch, build) ((major&amp;lt;&amp;lt;24)|(minor&amp;lt;&amp;lt;16)|(patch&amp;lt;&amp;lt;8)|build)
&lt;/span>&lt;span style="color:#75715e">#ifdef KWIN_VERSION_STR
&lt;/span>&lt;span style="color:#75715e">#define KWIN_VERSION KWIN_VERSION_CHECK(KWIN_VERSION_MAJ, KWIN_VERSION_MIN, KWIN_VERSION_PAT, KWIN_VERSION_BUI)
&lt;/span>&lt;span style="color:#75715e">#endif
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#75715e">#if defined(KWIN_VERSION) &amp;amp;&amp;amp; KWIN_VERSION &amp;lt; KWIN_VERSION_CHECK(5, 21, 3, 0)
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">typedef&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> TimeArgType;
&lt;span style="color:#75715e">#else
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">typedef&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>chrono&lt;span style="color:#f92672">::&lt;/span>milliseconds TimeArgType;
&lt;span style="color:#75715e">#endif
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#75715e">#if defined(Q_OS_LINUX) &amp;amp;&amp;amp; !defined(QT_NO_DYNAMIC_LIBRARY) &amp;amp;&amp;amp; !defined(QT_NO_LIBRARY)
&lt;/span>&lt;span style="color:#75715e">&lt;/span>QT_BEGIN_NAMESPACE
QFunctionPointer &lt;span style="color:#a6e22e">qt_linux_find_symbol_sys&lt;/span>(&lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span>symbol);
QT_END_NAMESPACE
QFunctionPointer KWinUtils&lt;span style="color:#f92672">::&lt;/span>resolve(&lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span>symbol)
&amp;#123;
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#a6e22e">QT_PREPEND_NAMESPACE&lt;/span>(qt_linux_find_symbol_sys)(symbol);
&lt;span style="color:#75715e">#else
&lt;/span>&lt;span style="color:#75715e">&lt;/span>QFunctionPointer KWinUtils&lt;span style="color:#f92672">::&lt;/span>resolve(&lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span>symbol)
&amp;#123;
&lt;span style="color:#66d9ef">static&lt;/span> QString lib_name &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">"kwin.so."&lt;/span> &lt;span style="color:#f92672">+&lt;/span> qApp&lt;span style="color:#f92672">-&amp;gt;&lt;/span>applicationVersion();
&lt;span style="color:#66d9ef">return&lt;/span> QLibrary&lt;span style="color:#f92672">::&lt;/span>resolve(lib_name, symbol);
&lt;span style="color:#75715e">#endif
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>从上面的例子中可以看到，代码中一旦出现大量重复的判断条件，代码非常不直观，而且被宏分割成了很多部分。&lt;/p>
&lt;p>在一次偶然的机会，我看到了一篇介绍 C++ 17 中的 if constexpr 的用法，可以在编译期进行一些计算，虽然我很早就知道了 constexpr 的用法，但是大家举的例子基本上都是数值计算，让编译器在编译期间将数值进行计算，从而减轻运行时的消耗，我也从来想到其他用法，所以一直没有在项目中使用到。&lt;/p>
&lt;p>constexpr 的作用并不是编译期计算数值，而是编译期进行的代码分析，如果代码较小且非常直观，比如大家经常举的例子，在编译期间计算斐波那契数列，这种例子即使不使用 constexpr 显式要求，编译器也会帮助我们开启优化，直接给出结果。&lt;/p>
&lt;p>但是如果代码非常复杂，编译器就不一定会为我们做这样的优化，就需要我们手动标记可以计算的位置，要求编译器在编译期间进行求值和优化。&lt;/p>
&lt;p>我设想的是，使用 cmake 在构建时，先生成一份文件，将开关的值记录下来，在需要进行判断的地方，就可以直接使用 if constexpr 进行条件判断，在编译期间，编译器会发现有一个分支确定不会被执行（相当于 &lt;code>if(false) &amp;#123;}&lt;/code>），那么这个分支就不会进行编译，直接剔除。&lt;/p>
&lt;p>CMakeLists.txt 中需要做一些工作，将编译参数加入构建系统。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cmake" data-lang="cmake">option (&lt;span style="color:#e6db74">ENABLE_MODULE&lt;/span> &lt;span style="color:#e6db74">"Enable Module"&lt;/span> &lt;span style="color:#e6db74">ON&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>if(&lt;span style="color:#e6db74">ENABLE_MODULE&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span> set(&lt;span style="color:#e6db74">ENABLE_MODULE&lt;/span> &lt;span style="color:#e6db74">"1"&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>else()&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span> set(&lt;span style="color:#e6db74">ENABLE_MODULE&lt;/span> &lt;span style="color:#e6db74">"0"&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>endif(&lt;span style="color:#e6db74">ENABLE_MODULE&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>configure_file (
&lt;span style="color:#e6db74">"$&amp;#123;CMAKE_CURRENT_SOURCE_DIR}/options/options.h.in"&lt;/span>
&lt;span style="color:#e6db74">"$&amp;#123;CMAKE_CURRENT_BINARY_DIR}/options/options.h"&lt;/span>
)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 options/options.h.in 文件里，按照 cmake 的要求将变量导入进文件中，进行内容替换。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#75715e">#pragma once
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#75715e">#cmakedefine01 ENABLE_MODULE
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里我仍然使用的是宏定义，也可以直接写成如下形式:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cmake" data-lang="cmake">option (&lt;span style="color:#e6db74">ENABLE_MODULE&lt;/span> &lt;span style="color:#e6db74">"Enable Module"&lt;/span> &lt;span style="color:#e6db74">ON&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>if(&lt;span style="color:#e6db74">ENABLE_MODULE&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span> set(&lt;span style="color:#e6db74">ENABLE_MODULE&lt;/span> &lt;span style="color:#e6db74">"true"&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>else()&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span> set(&lt;span style="color:#e6db74">ENABLE_MODULE&lt;/span> &lt;span style="color:#e6db74">"false"&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>endif(&lt;span style="color:#e6db74">ENABLE_MODULE&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>configure_file (
&lt;span style="color:#e6db74">"$&amp;#123;CMAKE_CURRENT_SOURCE_DIR}/options/options.h.in"&lt;/span>
&lt;span style="color:#e6db74">"$&amp;#123;CMAKE_CURRENT_BINARY_DIR}/options/options.h"&lt;/span>
)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#75715e">#pragma once
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> ENABLE_MODULE&amp;#123;&lt;span style="color:#960050;background-color:#1e0010">@&lt;/span>ENABLE_MODULE&lt;span style="color:#960050;background-color:#1e0010">@&lt;/span>};
&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 main.cpp 中写一段测试代码:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">"options/options.h"&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() &amp;#123;
&lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#66d9ef">constexpr&lt;/span> (ENABLE_MODULE) &amp;#123;
std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"Now Enable Module"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
}
&lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#66d9ef">constexpr&lt;/span> (&lt;span style="color:#f92672">!&lt;/span>ENABLE_MODULE) &amp;#123;
std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"Now Disable Module"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
}
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>执行结果是符合预期的。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-txt" data-lang="txt"># lxz @ lxz-MacBook-Pro in ~/Develop/constexpr-demo/build on git:master o [13:28:39]
$ cmake ../ -G Ninja -DENABLE_MODULE=ON
-- The CXX compiler identification is AppleClang 13.0.0.13000029
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/lxz/Develop/constexpr-demo/build
# lxz @ lxz-MacBook-Pro in ~/Develop/constexpr-demo/build on git:master o [13:28:45]
$ ninja
[2/2] Linking CXX executable src/constexpr
# lxz @ lxz-MacBook-Pro in ~/Develop/constexpr-demo/build on git:master o [13:28:48]
$ ./src/constexpr
Now Enable Module
# lxz @ lxz-MacBook-Pro in ~/Develop/constexpr-demo/build on git:master o [13:28:52]
$ cmake ../ -G Ninja -DENABLE_MODULE=OFF
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/lxz/Develop/constexpr-demo/build
# lxz @ lxz-MacBook-Pro in ~/Develop/constexpr-demo/build on git:master o [13:28:58]
$ ninja
[2/2] Linking CXX executable src/constexpr
# lxz @ lxz-MacBook-Pro in ~/Develop/constexpr-demo/build on git:master o [13:29:00]
$ ./src/constexpr
Now Disable Module
&lt;/code>&lt;/pre>&lt;/div>&lt;p>虽然结果是符合的，但是我们其实不确定是否真的在编译期间就完成了代码剔除，所以使用命令进行汇编，查看汇编中是否包含了判断指令和两段输出的字符串。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-shell" data-lang="shell">clang -S main.cpp -o main.s -I./
&lt;/code>&lt;/pre>&lt;/div>&lt;p>main.s&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-txt" data-lang="txt"> .text
.file "main.cpp"
.globl main // -- Begin function main
.p2align 2
.type main,@function
main: // @main
.cfi_startproc
// %bb.0:
stp x29, x30, [sp, #-32]! // 16-byte Folded Spill
str x19, [sp, #16] // 8-byte Folded Spill
mov x29, sp
.cfi_def_cfa w29, 32
.cfi_offset w19, -16
.cfi_offset w30, -24
.cfi_offset w29, -32
adrp x19, :got:_ZSt4cout
ldr x19, [x19, :got_lo12:_ZSt4cout]
adrp x1, .L.str
add x1, x1, :lo12:.L.str
mov w2, #18
mov x0, x19
bl _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
ldr x8, [x19]
ldur x8, [x8, #-24]
add x8, x8, x19
ldr x19, [x8, #240]
cbz x19, .LBB0_5
// %bb.1:
ldrb w8, [x19, #56]
cbz w8, .LBB0_3
// %bb.2:
ldrb w1, [x19, #67]
b .LBB0_4
.LBB0_3:
mov x0, x19
bl _ZNKSt5ctypeIcE13_M_widen_initEv
ldr x8, [x19]
mov w1, #10
mov x0, x19
ldr x8, [x8, #48]
blr x8
mov w1, w0
.LBB0_4:
adrp x0, :got:_ZSt4cout
ldr x0, [x0, :got_lo12:_ZSt4cout]
bl _ZNSo3putEc
bl _ZNSo5flushEv
ldr x19, [sp, #16] // 8-byte Folded Reload
mov w0, wzr
ldp x29, x30, [sp], #32 // 16-byte Folded Reload
ret
.LBB0_5:
bl _ZSt16__throw_bad_castv
.Lfunc_end0:
.size main, .Lfunc_end0-main
.cfi_endproc
// -- End function
.section .text.startup,"ax",@progbits
.p2align 2 // -- Begin function _GLOBAL__sub_I_main.cpp
.type _GLOBAL__sub_I_main.cpp,@function
_GLOBAL__sub_I_main.cpp: // @_GLOBAL__sub_I_main.cpp
.cfi_startproc
// %bb.0:
stp x29, x30, [sp, #-32]! // 16-byte Folded Spill
str x19, [sp, #16] // 8-byte Folded Spill
mov x29, sp
.cfi_def_cfa w29, 32
.cfi_offset w19, -16
.cfi_offset w30, -24
.cfi_offset w29, -32
adrp x19, _ZStL8__ioinit
add x19, x19, :lo12:_ZStL8__ioinit
mov x0, x19
bl _ZNSt8ios_base4InitC1Ev
adrp x0, :got:_ZNSt8ios_base4InitD1Ev
ldr x0, [x0, :got_lo12:_ZNSt8ios_base4InitD1Ev]
mov x1, x19
ldr x19, [sp, #16] // 8-byte Folded Reload
adrp x2, __dso_handle
add x2, x2, :lo12:__dso_handle
ldp x29, x30, [sp], #32 // 16-byte Folded Reload
b __cxa_atexit
.Lfunc_end1:
.size _GLOBAL__sub_I_main.cpp, .Lfunc_end1-_GLOBAL__sub_I_main.cpp
.cfi_endproc
// -- End function
.type _ZStL8__ioinit,@object // @_ZStL8__ioinit
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.hidden __dso_handle
.type .L.str,@object // @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "Now Disable Module" // 关键在这里
.size .L.str, 19
.section .init_array,"aw",@init_array
.p2align 3
.xword _GLOBAL__sub_I_main.cpp
.ident "clang version 13.0.1"
.section ".note.GNU-stack","",@progbits
.addrsig
.addrsig_sym _GLOBAL__sub_I_main.cpp
.addrsig_sym _ZStL8__ioinit
.addrsig_sym __dso_handle
.addrsig_sym _ZSt4cout
&lt;/code>&lt;/pre>&lt;/div>&lt;p>查看整个 main.s 汇编，发现只在 .L.str 段中有预期的文本字符串，可以得出结论，代码是在编译期间完成了剔除，符合我们的要求。&lt;/p>
&lt;p>原文链接：&lt;a href="https://blog.justforlxz.com/2022/03/10/if-constexpr/">https://blog.justforlxz.com/2022/03/10/if-constexpr/&lt;/a>&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>QDir 和 std::filesystem 的简单对比</title><link>https://blog.deepin.org/posts/qdir-stdfilesystem/</link><pubDate>Fri, 04 Mar 2022 10:11:45 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/qdir-stdfilesystem/</guid><description>&lt;p>作为一名使用 Qt 的开发人员，Qt 为我提供了大量好用的基础设施，例如广受好评的 QString、QNetwork之类的，这是 Qt 平台为我提供的帮助，我只需要在这个平台上开发就足够了。&lt;/p>
&lt;p>同样作为一名 C++ 开发人员，C++ 标准库也是我需要用的基础设施，但是标准库提供的功能就不如 Qt 了，最令人诟病的就是 C++ 的 std::string，业内充斥着对 std::string 的不屑与谩骂。&lt;/p>
&lt;p>但是这一情况将会在 C++ 20 标准后改善，这里不具体展开，将来我会准备写一份 C++ 20 的功能介绍。&lt;/p>
&lt;p>今天在开发项目的时候，为了节省资源，我仅使用标准库完成开发，缺少了 Qt 平台为我提供的有利帮助，项目开发的难度瞬间增加，在此期间，我发现了 C++ 的目录操作似乎能比的上 Qt 提供的 QDir封装，我打算在本篇文章中介绍一下这两者的不同。&lt;/p>
&lt;h2 id="qdir">QDir
&lt;span>
&lt;a href="https://blog.deepin.org/posts/qdir-stdfilesystem/#qdir">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>Qt 的基本思路是继承大于组合，所以 Qt 为我们提供的都是各种继承的类，在 Qt 中，我们使用 QDir 类进行目录操作。&lt;/p>
&lt;p>QDir 类使用相对或绝对文件路径来指向一个文件或目录。如果总是使用 “/” 作为目录分隔符，Qt 将会把你的路径转化为符合底层的操作系统的。&lt;/p>
&lt;p>QDir 类由于不涉及 IO 的具体操作，所以没有继承自 QObject 或者其他 QIO 的类。&lt;/p>
&lt;p>QDir 提供了非常多的方法，可以方便的获取目录的名称、绝对路径、相对路径、设置目录过滤器等。&lt;/p>
&lt;p>一个基本的用法如下:&lt;/p>
&lt;pre>&lt;code class="language-Qt" data-lang="Qt">QDir dir(&amp;quot;/tmp&amp;quot;);
if (!dir.exists()) &amp;#123;
return
}
for (const QFileInfo&amp;amp; item : dir.entryInfoList()) &amp;#123;
if (item.isDir()) &amp;#123;
// ...
}
}
dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
dir.setSorting(QDir::Size | QDir::Reversed);
const QFileInfoList&amp;amp; list = dir.entryInfoList();
for (const QFileInfo&amp;amp; item : list) &amp;#123;
// ...
}
&lt;/code>&lt;/pre>&lt;p>在上面的示例代码中可以看到，QDir 整体是非常符合面向对象的，我们使用 QDir 对象，对目录执行各种操作，涉及到具体的文件（目录是特殊的文件），我们可以使用 QFileInfo 类获取具体文件的信息。&lt;/p>
&lt;p>除了以上演示涉及到的方法，QDir 还有很多其他方法，使用 &lt;code>count()&lt;/code> 方法统计目录内文件和目录的数量，使用 &lt;code>remove()&lt;/code> 方法删除指定的目录，这里就不一一列举了。&lt;/p>
&lt;p>QDir 支持设置多种过滤，过滤(Filter) 和 排序(Sorting) 都会影响到 entryInfoList 方法返回的内容。&lt;/p>
&lt;p>QDir 接受的过滤器枚举:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>枚举&lt;/th>
&lt;th>枚举值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>QDir::Dirs&lt;/td>
&lt;td>0x001&lt;/td>
&lt;td>列出与过滤器匹配的目录。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::AllDirs&lt;/td>
&lt;td>0x400&lt;/td>
&lt;td>列出所有目录；即不要将过滤器应用于目录名称。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::Files&lt;/td>
&lt;td>0x002&lt;/td>
&lt;td>列出文件。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::Drives&lt;/td>
&lt;td>0x004&lt;/td>
&lt;td>列出磁盘驱动器（在Unix下忽略）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::NoSymLinks&lt;/td>
&lt;td>0x008&lt;/td>
&lt;td>不要列出符号链接（不支持符号链接的操作系统忽略）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::NoDotAndDotDot&lt;/td>
&lt;td>NoDot&lt;/td>
&lt;td>NoDotDot&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::NoDot&lt;/td>
&lt;td>0x2000&lt;/td>
&lt;td>不要列出特殊条目“.”。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::NoDotDot&lt;/td>
&lt;td>0x4000&lt;/td>
&lt;td>不要列出特殊条目“..”。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::AllEntries&lt;/td>
&lt;td>Dirs&lt;/td>
&lt;td>Files&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::Readable&lt;/td>
&lt;td>0x010&lt;/td>
&lt;td>列出应用程序具有读取访问权限的文件。可读值需要与Dirs或Files合并。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::Writable&lt;/td>
&lt;td>0x020&lt;/td>
&lt;td>列出应用程序具有写入访问权限的文件。可写值需要与Dirs或Files合并。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::Executable&lt;/td>
&lt;td>0x040&lt;/td>
&lt;td>列出应用程序具有执行访问权限的文件。可执行值需要与Dirs或Files合并。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::Modified&lt;/td>
&lt;td>0x080&lt;/td>
&lt;td>仅列出已修改的文件（在Unix上忽略）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::Hidden&lt;/td>
&lt;td>0x100&lt;/td>
&lt;td>列出隐藏的文件（在Unix上，以“.”开头的文件）。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::System&lt;/td>
&lt;td>0x200&lt;/td>
&lt;td>列出系统文件（包括Unix、FIFO、套接字和设备文件；在Windows上，包括.lnk文件）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::CaseSensitive&lt;/td>
&lt;td>0x800&lt;/td>
&lt;td>过滤器应该区分大小写。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>QDir 接受的排序枚举:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>枚举&lt;/th>
&lt;th>枚举值&lt;/th>
&lt;th>描述&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>QDir::Name&lt;/td>
&lt;td>0x00&lt;/td>
&lt;td>按名称排序。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::Time&lt;/td>
&lt;td>0x01&lt;/td>
&lt;td>按时间（修改时间）排序。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::Size&lt;/td>
&lt;td>0x02&lt;/td>
&lt;td>按文件大小排序。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::Type&lt;/td>
&lt;td>0x80&lt;/td>
&lt;td>按文件类型（扩展名）排序。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::Unsorted&lt;/td>
&lt;td>0x03&lt;/td>
&lt;td>不要排序。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::NoSort&lt;/td>
&lt;td>-1&lt;/td>
&lt;td>默认情况下未排序。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::DirsFirst&lt;/td>
&lt;td>0x04&lt;/td>
&lt;td>先放目录，然后放文件。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::DirsLast&lt;/td>
&lt;td>0x20&lt;/td>
&lt;td>先放文件，然后放目录。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::Reversed&lt;/td>
&lt;td>0x08&lt;/td>
&lt;td>颠倒排序顺序。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::IgnoreCase&lt;/td>
&lt;td>0x10&lt;/td>
&lt;td>不区分大小写的排序。&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QDir::LocaleAware&lt;/td>
&lt;td>0x40&lt;/td>
&lt;td>使用当前区域设置对项目进行适当排序。&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="stdfilesystem">std::filesystem
&lt;span>
&lt;a href="https://blog.deepin.org/posts/qdir-stdfilesystem/#stdfilesystem">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>上面介绍了 Qt 的设计风格，而标准库的设计风格是组合大于继承，标准库提供各种非常具体的类或者函数，将一个系列的操作拆分为各个子项，最终完成任务。&lt;/p>
&lt;p>这里使用一个小例子来说明一下:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">std&lt;span style="color:#f92672">::&lt;/span>filesystem&lt;span style="color:#f92672">::&lt;/span>path tmp&amp;#123;&lt;span style="color:#e6db74">"/tmp"&lt;/span>};
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#f92672">!&lt;/span>std&lt;span style="color:#f92672">::&lt;/span>filesystem&lt;span style="color:#f92672">::&lt;/span>exists(tmp)) &amp;#123;
std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"目录不存在"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;span style="color:#66d9ef">return&lt;/span>;
}
&lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> result &lt;span style="color:#f92672">=&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>filesystem&lt;span style="color:#f92672">::&lt;/span>create_directories(tmp);
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#f92672">!&lt;/span>result) &amp;#123;
std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">"目录创建失败，也许是已经存在。"&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
}
&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span>&lt;span style="color:#f92672">&amp;amp;&lt;/span> dir_entry : std&lt;span style="color:#f92672">::&lt;/span>filesystem&lt;span style="color:#f92672">::&lt;/span>directory_iterator(tmp)) &amp;#123;
&lt;span style="color:#66d9ef">if&lt;/span> (dir_entry.is_directory()) &amp;#123;
&lt;span style="color:#75715e">// ...
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
&lt;span style="color:#66d9ef">if&lt;/span> (std&lt;span style="color:#f92672">::&lt;/span>filesystem&lt;span style="color:#f92672">::&lt;/span>is_directory(tmp)) &amp;#123;
&lt;span style="color:#75715e">// ...
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>可以看到，C++ 标准库使用多个类共同完成了对目录的检查和遍历，这种基于组合的方式可以带来更多的灵活性，如果需要对某个部分进行修改，只需要继承特定类就可以完成，如果是 Qt 的 QDir，则不是很轻松。&lt;/p>
&lt;p>在使用标准库的时候需要注意的是，标准库通常不会约束使用者，使用当前的例子举例，QDir 提供了过滤器枚举，可以帮助开发者简单的实现文件过滤功能，但是 std::filesystem 则不提供这种接口，标准库提供了机制，但是不提供策略，开发者需要使用标准库提供的各种接口，&lt;strong>组合&lt;/strong> 出自己的业务，所以如果想要使用标准库的时候也能实现过滤和排序，就只能自己提供相应的操作。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">std&lt;span style="color:#f92672">::&lt;/span>filesystem&lt;span style="color:#f92672">::&lt;/span>path tmp&amp;#123; &lt;span style="color:#e6db74">"/tmp"&lt;/span> };
std&lt;span style="color:#f92672">::&lt;/span>vector&lt;span style="color:#f92672">&amp;lt;&lt;/span>std&lt;span style="color:#f92672">::&lt;/span>filesystem&lt;span style="color:#f92672">::&lt;/span>directory_entry&lt;span style="color:#f92672">&amp;gt;&lt;/span> list;
std&lt;span style="color:#f92672">::&lt;/span>vector&lt;span style="color:#f92672">&amp;lt;&lt;/span>std&lt;span style="color:#f92672">::&lt;/span>string&lt;span style="color:#f92672">&amp;gt;&lt;/span> filter&amp;#123; &lt;span style="color:#e6db74">".jpg"&lt;/span>, &lt;span style="color:#e6db74">".txt"&lt;/span> };
std&lt;span style="color:#f92672">::&lt;/span>filesystem&lt;span style="color:#f92672">::&lt;/span>directory_iterator iter&amp;#123; tmp };
std&lt;span style="color:#f92672">::&lt;/span>copy_if(iter.begin(),
iter.end(),
std&lt;span style="color:#f92672">::&lt;/span>back_inserter(list),
[filter](std&lt;span style="color:#f92672">::&lt;/span>filesystem&lt;span style="color:#f92672">::&lt;/span>directory_entry entry) &amp;#123;
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#f92672">!&lt;/span>entry.is_directory() &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span>
std&lt;span style="color:#f92672">::&lt;/span>find_if(filter.begin(),
filter.end(),
[entry](std&lt;span style="color:#f92672">::&lt;/span>string s) &amp;#123;
&lt;span style="color:#66d9ef">return&lt;/span> entry.path().extension() &lt;span style="color:#f92672">==&lt;/span> s;
}
);
}
);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;del>可以看到，代码变得异常丑陋（逃&lt;/del>&lt;/p>
&lt;p>标准库提供了一些比较方便的函数，例如 std::copy_if、 std::back_inserter 和 std::find_if 等，还有一个较为常用的 std::transform。标准库提供了迭代器抽象，这样我们可以使用迭代器对象和迭代器算法，方便的进行各种遍历、复制和转换。&lt;/p>
&lt;p>从上面的例子可以看出，Qt 确实为开发者提供了很好的帮助，这是 Qt 作为一个平台力所能及的工作，当然，即使我们使用 Qt，也还是可以写出上面一样的代码。&lt;/p>
&lt;h2 id="总结">总结
&lt;span>
&lt;a href="https://blog.deepin.org/posts/qdir-stdfilesystem/#%e6%80%bb%e7%bb%93">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>以上就是简单的 QDir 和 std::filesystem 的不同，综合来看，Qt 库和标准库其实各有优缺，他们的目标和面向的开发者是不同的，Qt 最近一直在尝试使用标准库的内容来代替自己的一部分组件，最新的Qt6 就已经升级到了 C++ 17 标准，将 qSort 宏改为了使用 std::sort，也许在不久的将来，我们再也不用为使用 Qt 还是标准库而争论或者站队。&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>引用资料&lt;/strong>
&lt;a href="https://en.cppreference.com/w/cpp/filesystem">std::filesystem https://en.cppreference.com/w/cpp/filesystem&lt;/a>
&lt;a href="https://doc.qt.io/qt-5/qdir.html">QDir https://doc.qt.io/qt-5/qdir.html&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>原文链接：&lt;a href="https://blog.justforlxz.com/2022/03/04/qdir-stdfilesystem/">https://blog.justforlxz.com/2022/03/04/qdir-stdfilesystem/&lt;/a>&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>QDir 和 std::filesystem 的简单对比</title><link>https://blog.mkacg.com/2022/03/04/qdir-stdfilesystem/</link><pubDate>Fri, 04 Mar 2022 02:11:45 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2022/03/04/qdir-stdfilesystem/</guid><description>&lt;p>作为一名使用 Qt 的开发人员，Qt 为我提供了大量好用的基础设施，例如广受好评的 QString、QNetwork之类的，这是 Qt 平台为我提供的帮助，我只需要在这个平台上开发就足够了。&lt;/p>&lt;p>同样作为一名 C++ 开发人员，C++ 标准库也是我需要用的基础设施，但是标准库提供的功能就不如 Qt 了，最令人诟病的就是 C++ 的 std::string，业内充斥着对 std::string 的不屑与谩骂。&lt;/p>&lt;p>但是这一情况将会在 C++ 20 标准后改善，这里不具体展开，将来我会准备写一份 C++ 20 的功能介绍。&lt;/p>&lt;p>今天在开发项目的时候，为了节省资源，我仅使用标准库完成开发，缺少了 Qt 平台为我提供的有利帮助，项目开发的难度瞬间增加，在此期间，我发现了 C++ 的目录操作似乎能比的上 Qt 提供的 QDir封装，我打算在本篇文章中介绍一下这两者的不同。&lt;/p>&lt;h2 id="QDir">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#QDir" title="QDir">&lt;/a>QDir&lt;/h2>&lt;p>Qt 的基本思路是继承大于组合，所以 Qt 为我们提供的都是各种继承的类，在 Qt 中，我们使用 QDir 类进行目录操作。&lt;/p>&lt;p>QDir 类使用相对或绝对文件路径来指向一个文件或目录。如果总是使用 “&amp;#x2f;” 作为目录分隔符，Qt 将会把你的路径转化为符合底层的操作系统的。&lt;/p>&lt;p>QDir 类由于不涉及 IO 的具体操作，所以没有继承自 QObject 或者其他 QIO 的类。&lt;/p>&lt;p>QDir 提供了非常多的方法，可以方便的获取目录的名称、绝对路径、相对路径、设置目录过滤器等。&lt;/p>&lt;p>一个基本的用法如下:&lt;/p>&lt;figure class="highlight qt">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">QDir dir(&lt;span class="string">&amp;quot;/tmp&amp;quot;&lt;/span>);&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">if&lt;/span> (!dir.exists()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">for&lt;/span> (&lt;span class="keyword">const&lt;/span> QFileInfo&amp;amp; &lt;span class="attribute">item&lt;/span> : dir.entryInfoList()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (item.isDir()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// ...&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">dir.setFilter(&lt;span class="attribute">QDir&lt;/span>::Files | &lt;span class="attribute">QDir&lt;/span>::Hidden | &lt;span class="attribute">QDir&lt;/span>::NoSymLinks);&lt;/span>&lt;br />&lt;span class="line">dir.setSorting(&lt;span class="attribute">QDir&lt;/span>::Size | &lt;span class="attribute">QDir&lt;/span>::Reversed);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">const&lt;/span> QFileInfoList&amp;amp; &lt;span class="built_in">list&lt;/span> = dir.entryInfoList();&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">for&lt;/span> (&lt;span class="keyword">const&lt;/span> QFileInfo&amp;amp; &lt;span class="attribute">item&lt;/span> : &lt;span class="built_in">list&lt;/span>) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// ...&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>在上面的示例代码中可以看到，QDir 整体是非常符合面向对象的，我们使用 QDir 对象，对目录执行各种操作，涉及到具体的文件（目录是特殊的文件），我们可以使用 QFileInfo 类获取具体文件的信息。&lt;/p>&lt;p>除了以上演示涉及到的方法，QDir 还有很多其他方法，使用 &lt;code>count()&lt;/code> 方法统计目录内文件和目录的数量，使用 &lt;code>remove()&lt;/code> 方法删除指定的目录，这里就不一一列举了。&lt;/p>&lt;p>QDir 支持设置多种过滤，过滤(Filter) 和 排序(Sorting) 都会影响到 entryInfoList 方法返回的内容。&lt;/p>&lt;p>QDir 接受的过滤器枚举:&lt;/p>&lt;table>&lt;thead>&lt;tr>&lt;th>枚举&lt;/th>&lt;th>枚举值&lt;/th>&lt;th>描述&lt;/th>&lt;/tr>&lt;/thead>&lt;tbody>&lt;tr>&lt;td>QDir::Dirs&lt;/td>&lt;td>0x001&lt;/td>&lt;td>列出与过滤器匹配的目录。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::AllDirs&lt;/td>&lt;td>0x400&lt;/td>&lt;td>列出所有目录；即不要将过滤器应用于目录名称。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::Files&lt;/td>&lt;td>0x002&lt;/td>&lt;td>列出文件。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::Drives&lt;/td>&lt;td>0x004&lt;/td>&lt;td>列出磁盘驱动器（在Unix下忽略）。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::NoSymLinks&lt;/td>&lt;td>0x008&lt;/td>&lt;td>不要列出符号链接（不支持符号链接的操作系统忽略）。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::NoDotAndDotDot&lt;/td>&lt;td>NoDot&lt;/td>&lt;td>NoDotDot&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::NoDot&lt;/td>&lt;td>0x2000&lt;/td>&lt;td>不要列出特殊条目“.”。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::NoDotDot&lt;/td>&lt;td>0x4000&lt;/td>&lt;td>不要列出特殊条目“..”。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::AllEntries&lt;/td>&lt;td>Dirs&lt;/td>&lt;td>Files&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::Readable&lt;/td>&lt;td>0x010&lt;/td>&lt;td>列出应用程序具有读取访问权限的文件。可读值需要与Dirs或Files合并。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::Writable&lt;/td>&lt;td>0x020&lt;/td>&lt;td>列出应用程序具有写入访问权限的文件。可写值需要与Dirs或Files合并。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::Executable&lt;/td>&lt;td>0x040&lt;/td>&lt;td>列出应用程序具有执行访问权限的文件。可执行值需要与Dirs或Files合并。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::Modified&lt;/td>&lt;td>0x080&lt;/td>&lt;td>仅列出已修改的文件（在Unix上忽略）。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::Hidden&lt;/td>&lt;td>0x100&lt;/td>&lt;td>列出隐藏的文件（在Unix上，以“.”开头的文件）。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::System&lt;/td>&lt;td>0x200&lt;/td>&lt;td>列出系统文件（包括Unix、FIFO、套接字和设备文件；在Windows上，包括.lnk文件）&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::CaseSensitive&lt;/td>&lt;td>0x800&lt;/td>&lt;td>过滤器应该区分大小写。&lt;/td>&lt;/tr>&lt;/tbody>&lt;/table>&lt;p>QDir 接受的排序枚举:&lt;/p>&lt;table>&lt;thead>&lt;tr>&lt;th>枚举&lt;/th>&lt;th>枚举值&lt;/th>&lt;th>描述&lt;/th>&lt;/tr>&lt;/thead>&lt;tbody>&lt;tr>&lt;td>QDir::Name&lt;/td>&lt;td>0x00&lt;/td>&lt;td>按名称排序。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::Time&lt;/td>&lt;td>0x01&lt;/td>&lt;td>按时间（修改时间）排序。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::Size&lt;/td>&lt;td>0x02&lt;/td>&lt;td>按文件大小排序。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::Type&lt;/td>&lt;td>0x80&lt;/td>&lt;td>按文件类型（扩展名）排序。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::Unsorted&lt;/td>&lt;td>0x03&lt;/td>&lt;td>不要排序。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::NoSort&lt;/td>&lt;td>-1&lt;/td>&lt;td>默认情况下未排序。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::DirsFirst&lt;/td>&lt;td>0x04&lt;/td>&lt;td>先放目录，然后放文件。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::DirsLast&lt;/td>&lt;td>0x20&lt;/td>&lt;td>先放文件，然后放目录。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::Reversed&lt;/td>&lt;td>0x08&lt;/td>&lt;td>颠倒排序顺序。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::IgnoreCase&lt;/td>&lt;td>0x10&lt;/td>&lt;td>不区分大小写的排序。&lt;/td>&lt;/tr>&lt;tr>&lt;td>QDir::LocaleAware&lt;/td>&lt;td>0x40&lt;/td>&lt;td>使用当前区域设置对项目进行适当排序。&lt;/td>&lt;/tr>&lt;/tbody>&lt;/table>&lt;h2 id="std-filesystem">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#std-filesystem" title="std::filesystem">&lt;/a>std::filesystem&lt;/h2>&lt;p>上面介绍了 Qt 的设计风格，而标准库的设计风格是组合大于继承，标准库提供各种非常具体的类或者函数，将一个系列的操作拆分为各个子项，最终完成任务。&lt;/p>&lt;p>这里使用一个小例子来说明一下:&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">std::filesystem::path tmp&amp;#123;&lt;span class="string">&amp;quot;/tmp&amp;quot;&lt;/span>&amp;#125;;&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">if&lt;/span> (!std::filesystem::&lt;span class="built_in">exists&lt;/span>(tmp)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> std::cout &amp;lt;&amp;lt; &lt;span class="string">&amp;quot;目录不存在&amp;quot;&lt;/span> &amp;lt;&amp;lt; std::endl;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="type">const&lt;/span> &lt;span class="type">bool&lt;/span> result = std::filesystem::&lt;span class="built_in">create_directories&lt;/span>(tmp);&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">if&lt;/span> (!result) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> std::cout &amp;lt;&amp;lt; &lt;span class="string">&amp;quot;目录创建失败，也许是已经存在。&amp;quot;&lt;/span> &amp;lt;&amp;lt; std::endl;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">for&lt;/span> (&lt;span class="keyword">auto&lt;/span> &lt;span class="type">const&lt;/span>&amp;amp; dir_entry : std::filesystem::&lt;span class="built_in">directory_iterator&lt;/span>(tmp)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">if&lt;/span> (dir_entry.&lt;span class="built_in">is_directory&lt;/span>()) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// ...&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">if&lt;/span> (std::filesystem::&lt;span class="built_in">is_directory&lt;/span>(tmp)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// ...&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>可以看到，C++ 标准库使用多个类共同完成了对目录的检查和遍历，这种基于组合的方式可以带来更多的灵活性，如果需要对某个部分进行修改，只需要继承特定类就可以完成，如果是 Qt 的 QDir，则不是很轻松。&lt;/p>&lt;p>在使用标准库的时候需要注意的是，标准库通常不会约束使用者，使用当前的例子举例，QDir 提供了过滤器枚举，可以帮助开发者简单的实现文件过滤功能，但是 std::filesystem 则不提供这种接口，标准库提供了机制，但是不提供策略，开发者需要使用标准库提供的各种接口，&lt;strong>组合&lt;/strong> 出自己的业务，所以如果想要使用标准库的时候也能实现过滤和排序，就只能自己提供相应的操作。&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">std::filesystem::path tmp&amp;#123; &lt;span class="string">&amp;quot;/tmp&amp;quot;&lt;/span> &amp;#125;;&lt;/span>&lt;br />&lt;span class="line">std::vector&amp;lt;std::filesystem::directory_entry&amp;gt; list;&lt;/span>&lt;br />&lt;span class="line">std::vector&amp;lt;std::string&amp;gt; filter&amp;#123; &lt;span class="string">&amp;quot;.jpg&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;.txt&amp;quot;&lt;/span> &amp;#125;;&lt;/span>&lt;br />&lt;span class="line">std::filesystem::directory_iterator iter&amp;#123; tmp &amp;#125;;&lt;/span>&lt;br />&lt;span class="line">std::&lt;span class="built_in">copy_if&lt;/span>(iter.&lt;span class="built_in">begin&lt;/span>(),&lt;/span>&lt;br />&lt;span class="line"> iter.&lt;span class="built_in">end&lt;/span>(),&lt;/span>&lt;br />&lt;span class="line"> std::&lt;span class="built_in">back_inserter&lt;/span>(list),&lt;/span>&lt;br />&lt;span class="line"> [filter](std::filesystem::directory_entry entry) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> !entry.&lt;span class="built_in">is_directory&lt;/span>() &amp;amp;&amp;amp; &lt;/span>&lt;br />&lt;span class="line"> std::&lt;span class="built_in">find_if&lt;/span>(filter.&lt;span class="built_in">begin&lt;/span>(),&lt;/span>&lt;br />&lt;span class="line"> filter.&lt;span class="built_in">end&lt;/span>(),&lt;/span>&lt;br />&lt;span class="line"> [entry](std::string s) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> entry.&lt;span class="built_in">path&lt;/span>().&lt;span class="built_in">extension&lt;/span>() == s;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> );&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">);&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>&lt;del>可以看到，代码变得异常丑陋（逃&lt;/del>&lt;/p>&lt;p>标准库提供了一些比较方便的函数，例如 std::copy_if、 std::back_inserter 和 std::find_if 等，还有一个较为常用的 std::transform。标准库提供了迭代器抽象，这样我们可以使用迭代器对象和迭代器算法，方便的进行各种遍历、复制和转换。&lt;/p>&lt;p>从上面的例子可以看出，Qt 确实为开发者提供了很好的帮助，这是 Qt 作为一个平台力所能及的工作，当然，即使我们使用 Qt，也还是可以写出上面一样的代码。&lt;/p>&lt;h2 id="总结">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#总结" title="总结">&lt;/a>总结&lt;/h2>&lt;p>以上就是简单的 QDir 和 std::filesystem 的不同，综合来看，Qt 库和标准库其实各有优缺，他们的目标和面向的开发者是不同的，Qt 最近一直在尝试使用标准库的内容来代替自己的一部分组件，最新的Qt6 就已经升级到了 C++ 17 标准，将 qSort 宏改为了使用 std::sort，也许在不久的将来，我们再也不用为使用 Qt 还是标准库而争论或者站队。&lt;/p>&lt;blockquote>&lt;p>&lt;strong>引用资料&lt;/strong>&lt;br />&lt;a href="https://en.cppreference.com/w/cpp/filesystem">std::filesystem https://en.cppreference.com/w/cpp/filesystem&lt;/a>&lt;br />&lt;a href="https://doc.qt.io/qt-5/qdir.html">QDir https://doc.qt.io/qt-5/qdir.html&lt;/a>&lt;/p>&lt;/blockquote></description><author>justforlxz</author></item><item><title>翻译: Git from the bottom up | 自底向上聊 Git</title><link>https://blog.deepin.org/posts/git-from-the-bottom-up/</link><pubDate>Fri, 21 Jan 2022 09:40:08 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/git-from-the-bottom-up/</guid><description>&lt;p>这是一个 &lt;a href="https://jwiegley.github.io/git-from-the-bottom-up/">git from the bottom
up&lt;/a> 的中文翻译.&lt;/p>
&lt;p>由于译者水平有限, 保留了英文, 防止误解.&lt;/p>
&lt;p>如果真有人看, 还发现了什么错误, 欢迎提 issue, 欢迎 pr.&lt;/p>
&lt;p>为了保持整个文章在一个文件里面所以我并没有单独保存图片文件, 引用了原文的图片链
接, 只要原文不挂应该问题就不大.&lt;/p>
&lt;p>This text is licensed under Creative Commons BY 4.0. Please see the text of that
license here:&lt;/p>
&lt;p>&lt;a href="https://creativecommons.org/licenses/by/4.0/legalcode">https://creativecommons.org/licenses/by/4.0/legalcode&lt;/a>&lt;/p>
&lt;h2 id="0-introduction--介绍">0 Introduction | 介绍
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#0-introduction--%e4%bb%8b%e7%bb%8d">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;blockquote>
&lt;p>Welcome to the world of Git. I hope this document will help to advance your
understanding of this powerful content tracking system, and reveal a bit of
the simplicity underlying it — however dizzying its array of options may seem
from the outside.&lt;/p>
&lt;/blockquote>
&lt;p>欢迎来到 Git 的世界. 我希望这个文档可以加深读者对这一个内容跟踪系统的认识. 虽然
Git 的操作选项从使用者的角度看起来令人头晕眼花, 但是我希望借这篇文档展示它底层的
简洁性.&lt;/p>
&lt;blockquote>
&lt;p>Before we dive in, there are a few terms which should be mentioned first,
since they’ll appear repeatedly throughout this text:&lt;/p>
&lt;/blockquote>
&lt;p>在我们进一步讨论之前, 有几个名词我们需要先做出一些解释, 之后我们将在这篇文章中不
断地见到他们.&lt;/p>
&lt;h3 id="01-repository">0.1 repository
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#01-repository">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>repository&lt;/strong> — A &lt;strong>repository&lt;/strong> is a collection of &lt;em>commits&lt;/em>, each of
which is an archive of what the project&amp;rsquo;s &lt;em>working tree&lt;/em> looked like at a
past date, whether on your machine or someone else&amp;rsquo;s. It also defines HEAD
(see below), which identifies the branch or commit the current working tree
stemmed from. Lastly, it contains a set of &lt;em>branches&lt;/em> and &lt;em>tags&lt;/em>, to
identify certain commits by name.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>repository&lt;/strong> — &lt;strong>repository&lt;/strong> 是由若干 &lt;em>commit&lt;/em> 组成的集合, 这些 &lt;em>commit&lt;/em>
可以存在你的机器上, 也可以在其他的地方, 它们是 &lt;em>working tree&lt;/em> 曾经状态的存档.
&lt;strong>repository&lt;/strong> 同时定义了 HEAD (下面有), 它标志着当前的 working tree 的来源.
&lt;strong>repository&lt;/strong> 还含有一个由 &lt;em>branch&lt;/em> 和 &lt;em>tag&lt;/em> 组成的集合, &lt;em>branch&lt;/em> 和 &lt;em>tag&lt;/em> 可
以看作是某个 commit 的别名, 用户可以通过这个别名来找到某个 commit.&lt;/li>
&lt;/ul>
&lt;h3 id="02-the-index">0.2 the index
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#02-the-index">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>the index&lt;/strong> — Unlike other, similar tools you may have used, Git does not
commit changes directly from the &lt;em>working tree&lt;/em> into the &lt;em>repository&lt;/em>.
Instead, changes are first registered in something called &lt;strong>the index&lt;/strong>.
Think of it as a way of “confirming” your changes, one by one, before
doing a commit (which records all your approved changes at once). Some find
it helpful to call it instead as the “staging area”, instead of the index.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>the index&lt;/strong> — Git 并不像其他你使用过的类似工具那样直接把 &lt;em>working tree&lt;/em> 中
的更改直接 commit 到 &lt;em>repository&lt;/em> 中, 而是先将 &lt;em>working tree&lt;/em> 中的更改注册到
一个叫 &lt;strong>the index&lt;/strong> 的地方. 你可以把这看作是 Git 要求你在做一个 commit 之前,
对你的每个更改的再次确认, 这个 commit 将会一次性记录所有你再次确认过的更改. 有
人认为与其叫它 the index 不如将其称作 staging area (暂存区).&lt;/li>
&lt;/ul>
&lt;h3 id="03-working-tree">0.3 working tree
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#03-working-tree">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>working tree&lt;/strong> — A &lt;strong>working tree&lt;/strong> is any directory on your filesystem
which has a &lt;em>repository&lt;/em> associated with it (typically indicated by the
presence of a sub-directory within it named &lt;code>.git&lt;/code>.). It includes all the
files and sub-directories in that directory.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>working tree&lt;/strong> — &lt;strong>working tree&lt;/strong> 是在你的文件系统中任何一个与某个
&lt;em>repository&lt;/em> 相关联的文件夹, 通常来说这样的文件夹都会有一个叫 &lt;code>.git&lt;/code> 的子目录.
一个 &lt;strong>working tree&lt;/strong> 包含了这样一个文件夹下所有的文件和子目录.&lt;/li>
&lt;/ul>
&lt;h3 id="04-commit">0.4 commit
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#04-commit">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>commit&lt;/strong> — A &lt;strong>commit&lt;/strong> is a snapshot of your working tree at some point
in time. The state of HEAD (see below) at the time your commit is made
becomes that commit’s parent. This is what creates the notion of a
“revision history”.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>commit&lt;/strong> — &lt;strong>commit&lt;/strong> 是某个时间点上你的 working tree 的一个快照&lt;sup id="fnref:1">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:1" role="doc-noteref">1&lt;/a>&lt;/sup>. 当
你提交一个 commit 的时候, HEAD 指向的 commit 会成为你提交的 commit 的父
commit. 这个机制让我们得以追溯修改的历史.&lt;/li>
&lt;/ul>
&lt;h3 id="05-branch">0.5 branch
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#05-branch">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>branch&lt;/strong> — A &lt;strong>branch&lt;/strong> is just a name for a commit (and much more will
be said about commits in a moment), also called a reference. It’s the
parentage of a commit which defines its history, and thus the typical notion
of a “branch of development”.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>branch&lt;/strong> — &lt;strong>branch&lt;/strong> 只是某个的 commit 的别名&lt;sup id="fnref:2">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:2" role="doc-noteref">2&lt;/a>&lt;/sup>, 也可以认为是一个引用.
它是一个 commit 的来历, 记录着我们是如何到达这个 commit 的. 这很好地体现了分支
开发的理念.&lt;/li>
&lt;/ul>
&lt;h3 id="06-tag">0.6 tag
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#06-tag">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>tag&lt;/strong> — A &lt;strong>tag&lt;/strong> is also a name for a commit, similar to a &lt;em>branch&lt;/em>,
except that it always names the same commit, and can have its own
description text.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>tag&lt;/strong> — &lt;strong>tag&lt;/strong> 是另一种 commit 的别名, 和 &lt;em>branch&lt;/em> 很类似, 除了它永远都是
某个特定 commit 的别名, 以及一个 tag 可以有它自己的说明文字.&lt;/li>
&lt;/ul>
&lt;h3 id="07-master">0.7 master
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#07-master">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>master&lt;/strong> — The mainline of development in most repositories is done on a
branch called “&lt;strong>master&lt;/strong>”. Although this is a typical default, it is in
no way special.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>master&lt;/strong> — 大部分主要的开发都在一个叫 &amp;ldquo;&lt;strong>master&lt;/strong>&amp;rdquo; 的分支上完成, 这只是一个
默认的名称, 并没有什么特殊的.&lt;/li>
&lt;/ul>
&lt;h3 id="08-head">0.8 HEAD
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#08-head">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>HEAD&lt;/strong> — &lt;strong>HEAD&lt;/strong> is used by your repository to define what is currently
checked out:
&lt;ul>
&lt;li>If you checkout a branch, HEAD symbolically refers to that branch,
indicating that the branch name should be updated after the next commit
operation.&lt;/li>
&lt;li>If you checkout a specific commit, HEAD refers to that commit only. This
is referred to as a detached &lt;em>HEAD&lt;/em>, and occurs, for example, if you check
out a tag name.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>HEAD&lt;/strong> — &lt;strong>HEAD&lt;/strong> 是被你的 repository 用来确定什么东西是当前 checkout 的:
&lt;ul>
&lt;li>如果你 checkout 一个 branch, HEAD 就会指向那个 branch, 这意味着这个 branch
在下一次 commit 之后会更新.&lt;/li>
&lt;li>如果你 checkout 某个特定的 commit, 那么 HEAD 只会指向那个 commit. 发生这种事
情的时候, 我们称一个 HEAD 处在脱离的状态. 当你 checkout 一个 tag 的时候也会
发生这样的事情.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>The usual flow of events is this: After creating a repository, your work is
done in the working tree. Once your work reaches a significant point — the
completion of a bug, the end of the working day, a moment when everything
compiles — you add your changes successively to the index. Once the index
contains everything you intend to commit, you record its content in the
repository. Here’s a simple diagram that shows a typical project’s
life-cycle:&lt;/p>
&lt;/blockquote>
&lt;p>通常来说, 你用 Git 工作的流程是这样的: 在创建一个 repository 之后, 你在 working
tree 中完成工作. 每当你完成了一个阶段的工作, 比方说修正了一个 bug, 或者下班了,
还有比如&amp;quot;终于能过编译了!&amp;quot;,你就把所有的更改都一个一个添加到 the index 中. 当
the index 中包含了所有你想 commit 的内容, 你就将这个 commit 的内容加入到
repository 中.这里有一个简单的示意图,展示了一个项目通常的生命周期:&lt;/p>
&lt;p>&lt;img alt="ProjectLifecycle" src="https://jwiegley.github.io/git-from-the-bottom-up/images/lifecycle.png" />&lt;/p>
&lt;blockquote>
&lt;p>With this basic picture in mind, the following sections shall attempt to
describe how each of these different entities is important to the operation of
Git.&lt;/p>
&lt;/blockquote>
&lt;p>请将上述的内容记在心里, 接下来的几节我们将说明这些东西对 Git 的操作的重要性.&lt;/p>
&lt;h2 id="1-repository">1 Repository
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#1-repository">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="11-repository-directory-content-tracking--repository-目录内容跟踪">1.1 Repository: Directory content tracking | Repository: 目录内容跟踪
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#11-repository-directory-content-tracking--repository-%e7%9b%ae%e5%bd%95%e5%86%85%e5%ae%b9%e8%b7%9f%e8%b8%aa">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>As mentioned above, what Git does is quite rudimentary: it maintains snapshots
of a directory’s contents. Much of its internal design can be understood in
terms of this basic task.&lt;/p>
&lt;/blockquote>
&lt;p>就像上面提到的一样, Git 实际上做的事情非常的基础: 只是在维护一个目录里的内容的快
照. Git 的许多内部设计都可以从这个角度来理解.&lt;/p>
&lt;blockquote>
&lt;p>The design of a Git repository in many ways mirrors the structure of a UNIX
filesystem:&lt;/p>
&lt;/blockquote>
&lt;p>Git 的 repository 的设计很多时候都和 UNIX 的文件系统很相似:&lt;/p>
&lt;blockquote>
&lt;p>A &lt;em>filesystem&lt;/em> begins with a root directory, which typically consists of other
directories, most of which have leaf nodes, or &lt;em>files&lt;/em>, that contain data.
Meta-data about these files’ contents is stored both in the directory (the
names), and in the i-nodes that reference the contents of those files (their
size, type, permissions, etc). Each &lt;em>i-node&lt;/em> has a unique number that
identifies the contents of its related file. And while you may have many
directory entries pointing to a particular i-node (i.e., hard-links), it’s
the i-node which “owns” the contents stored on your filesystem.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;em>文件系统&lt;/em> 是从根目录开始的, 通常它会包括其他的所有目录. 大部分的目录会含有叶子
节点或者说是 &lt;em>文件&lt;/em> , 文件中才真正含有数据. 关于文件内容的元数据同时被存放在目
录里, 以及引用这个文件的内容的那个 i-node 里. 在目录里只存了个文件名, 而在
i-node 中存放着文件的大小, 类型, 权限等等. 每个 i-node 有一个唯一的编码, 用于区
分它关联的文件内容, 但是要注意: 是可能会出现多个文件目录指向了同一个 i-node 的
情况的(比如说硬链接). 总的来说,是 i-node 最终管理着你存在文件系统里的数据文件.&lt;/p>
&lt;blockquote>
&lt;p>Internally, Git shares a strikingly similar structure, albeit with one or two
key differences.&lt;/p>
&lt;/blockquote>
&lt;p>从内部逻辑的角度来看, Git 除了一两处关键的不同之处以外, 与这个结构有着惊人的相
似.&lt;/p>
&lt;blockquote>
&lt;p>First, it represents your file’s contents in &lt;em>blobs&lt;/em>, which are also leaf
nodes in something awfully close to a directory, called a &lt;em>tree&lt;/em>. Just as an
i-node is uniquely identified by a system-assigned number, a blob is named by
computing the SHA1 hash id of its size and contents.&lt;/p>
&lt;/blockquote>
&lt;p>首先, Git 将你的文件内容以 &lt;em>blob&lt;/em> 的方式呈现. 而 &lt;em>blob&lt;/em> 也是一个和目录十分类似的
树形结构中的叶子节点, 这里我们直接将这种树形结构称为 &lt;em>tree&lt;/em>. 和 i-node 有一个系
统赋予的, 独一无二的值一样, 一个 blob 也有一个独一无二的标识符, 而这个标识符也
就是这个 blob 的名字是从它的大小和内容运用 SHA1 哈希算法计算得出的.&lt;/p>
&lt;blockquote>
&lt;p>For all intents and purposes this is just an arbitrary number, like an i-node,
except that it has two additional properties:&lt;/p>
&lt;/blockquote>
&lt;p>这个值具体是什么并不重要, 在 Git 中我们基本上就是需要一个值来唯一地确定一个
blob, 这一点上和 i-node 很类似. 但是这个通过 SHA1 计算出来的哈希值却有两个额外的
性质:&lt;/p>
&lt;blockquote>
&lt;p>first, it verifies the blob’s contents will never change; and second, the
same contents shall always be represented by the same blob, no matter where it
appears: across commits, across repositories — even across the whole
Internet. If multiple trees reference the same blob, this is just like
hard-linking: the blob will not disappear from your repository as long as
there is at least one link remaining to it.&lt;/p>
&lt;/blockquote>
&lt;p>首先, 这个值能够保证一个 blob 的内容不会被随意更改; 其次, 只要 blob 的内容一样,
他们总会有同一个标识符, 即使他们在不同的地方也一样: 在不同的 commit 之间相同内
容的 blob 会有同一个名字, 不同仓库间的 blob 也一样, 即使是网络上的其他地方的
blob 也一样. 如果有多个 tree 引用了同一个 blob, 这情况几乎就和硬链接没什么两样:
直到没有 tree 引用这个 blob 了, 它才会从你的 repository 中消失.&lt;/p>
&lt;blockquote>
&lt;p>The difference between a Git blob and a filesystem’s file is that a blob
stores no metadata about its content. All such information is kept in the tree
that holds the blob. One tree may know those contents as a file named “foo”
that was created in August 2004, while another tree may know the same contents
as a file named “bar” that was created five years later.&lt;/p>
&lt;/blockquote>
&lt;p>Git 中的 blob 和文件系统中的文件的一大区别就是 blob 中并没有存储与其内容相关的元
数据. 这些信息是存放在引用这个 blob 的 tree 中的. 某个 tree 也许会觉得这个 blob
中的内容是一个叫 &amp;ldquo;foo&amp;rdquo; 的, 于 2004 年八月创建的文件, 同时另一个 tree 却会认为同
样的一个 blob 对应着名叫 &amp;ldquo;bar&amp;rdquo; 的, 五年后才被创建的文件.&lt;/p>
&lt;blockquote>
&lt;p>In a normal filesystem, two files with the same contents but with such
different metadata would always be represented as two independent files. Why
this difference? Mainly, it’s because a filesystem is designed to support
files that change, whereas Git is not. The fact that data is immutable in the
Git repository is what makes all of this work and so a different design was
needed. And as it turns out, this design allows for much more compact storage,
since all objects having identical content can be shared, no matter where they
are.&lt;/p>
&lt;/blockquote>
&lt;p>通常来说, 在文件系统中, 两个内容一样却有不一样的元数据的文件总是会被当作是两个独
立的文件. 为什么会有这种不同呢? 基本上我们可以认为这是因为文件系统主要是为了会
变化的文件设计的, 而 Git 并不是, 我们反而是希望在 Git 中被存下来的快照不会发生
变化的. 实际上这种设计甚至可以用来压缩存储空间, 因为它允许两个有相同内容的文件
共享空间&lt;sup id="fnref:3">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:3" role="doc-noteref">3&lt;/a>&lt;/sup>.&lt;/p>
&lt;h3 id="12-introducing-the-blob--关于-blob-的二三事">1.2 Introducing the blob | 关于 blob 的二三事
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#12-introducing-the-blob--%e5%85%b3%e4%ba%8e-blob-%e7%9a%84%e4%ba%8c%e4%b8%89%e4%ba%8b">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>Now that the basic picture has been painted, let’s get into some practical
examples. I’m going to start by creating a sample Git repository, and showing
how Git works from the bottom up in that repository. Feel free to follow along
as you read:&lt;/p>
&lt;/blockquote>
&lt;p>我们已经大致了解了 Git 的工作原理, 现在让我们看一些实践中的案例. 我将创建一个
Git repository 作为例子, 用它来自底向上地展示 Git 是如何工作的. 你完全可以在阅读
的同时也自己动手试试:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ mkdir sample; cd sample
$ echo &lt;span style="color:#e6db74">'Hello, world!'&lt;/span> &amp;gt; greeting
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Here I’ve created a new filesystem directory named “sample” which contains
a file whose contents are prosaically predictable. I haven’t even created a
repository yet, but already I can start using some of Git’s commands to
understand what it’s going to do. First of all, I’d like to know which hash
id Git is going to store my greeting text under:&lt;/p>
&lt;/blockquote>
&lt;p>这里我创建了一个叫叫 &amp;ldquo;sample&amp;rdquo; 的文件夹, 它的内容是显而易见的. 目前我还没有创建一
个 repository, 但是我实际上已经可以使用一些 Git 命令来简单解释一下接下来我们要
干些啥. 首先, 我想知道一下这个叫 greeting 的文件被使用 Git 进行版本管理之后, 它
对应的那个 blob 的名字是啥:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git hash-object greeting
af5626b4a114abcb82d63db7c8082c3c4756e51b
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>If you run this command on your system, you’ll get the same hash id. Even
though we’re creating two different repositories (possibly a world apart,
even) our greeting blob in those two repositories will have the same hash id.
I could even pull commits from your repository into mine, and Git would
realize that we’re tracking the same content — and so would only store one
copy of it! Pretty cool.&lt;/p>
&lt;/blockquote>
&lt;p>如果你也在你的机子上跑了这条命令,你一定会和我得到一个同样的哈希值. 虽然我们甚至
可能不在同一个位面&lt;sup id="fnref:4">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:4" role="doc-noteref">4&lt;/a>&lt;/sup>, 在两个不同的 repository 中, 同样的内容还是有同样的哈
希值. 我甚至可以从你的 repository 中 pull 一些 commit 到我这里来, 如果我真的那
么做了, Git 也会认识到我们两个人是在跟踪同样的一个内容 — 所以它也只会存储一份
数据. 这真的很棒!&lt;/p>
&lt;blockquote>
&lt;p>The next step is to initialize a new repository and commit the file into it. I
’m going to do this all in one step right now, but then come back and do it
again in stages so you can see what’s going on underneath:&lt;/p>
&lt;/blockquote>
&lt;p>接下来我们创建一个新的 repository 然后把文件 commit 进去. 我接下来将一次性完成
它, 之后我会再一步步的做一次, 这样有利于你理解到底发生了什么:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git init
$ git add greeting
$ git commit -m &lt;span style="color:#e6db74">"Added my greeting"&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>At this point our blob should be in the system exactly as we expected, using
the hash id determined above. As a convenience, Git requires only as many
digits of the hash id as are necessary to uniquely identify it within the
repository. Usually just six or seven digits is enough:&lt;/p>
&lt;/blockquote>
&lt;p>此时, 我们的那个 blob 应该正如我们预想的那样出现在这个系统中了, 它的哈希值正如上
面所说的那样, 应该是&lt;code>af5626b4a114abcb82d63db7c8082c3c4756e51b&lt;/code>. 方便起见, 只要
你给出足以在这个 repository 中唯一确定这个 blob 的哈希值前缀, Git 就可以找到这
样一个 blob. 通常来说六七个字符就够了:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git cat-file -t af5626b
blob
$ git cat-file blob af5626b
Hello, world!
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>There it is! I haven’t even looked at which commit holds it, or what tree it
’s in, but based solely on the contents I was able to assume it’s there,
and there it is. It will always have this same identifier, no matter how long
the repository lives or where the file within it is stored. These particular
contents are now verifiably preserved, forever.&lt;/p>
&lt;/blockquote>
&lt;p>看, 我们已经找到他了, 我甚至都不需要知道是哪个 commit 引用了这个 blob, 也不用知
道这个 blob 在哪个 tree 上, 我们只需要通过文件的内容就足以确定这个 blob 的存在.
这个文件一直都可以以这种方式来唯一确定, 不管这个repository存在了多久, 也不用管这
个 blob 到底被存储在 repository 中的哪里. 这个内容已经被很好的保护了起来.&lt;/p>
&lt;blockquote>
&lt;p>In this way, a Git blob represents the fundamental data unit in Git. Really,
the whole system is about blob management.&lt;/p>
&lt;/blockquote>
&lt;p>Git 以这种方式在 repository 中呈现数据. 讲真, 整个 Git 都只是在管理 blob 而已.&lt;/p>
&lt;h3 id="13-blobs-are-stored-in-trees--blob-是存在-tree-里的">1.3 Blobs are stored in trees | Blob 是存在 tree 里的
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#13-blobs-are-stored-in-trees--blob-%e6%98%af%e5%ad%98%e5%9c%a8-tree-%e9%87%8c%e7%9a%84">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>The contents of your files are stored in blobs, but those blobs are pretty
featureless. They have no name, no structure — they’re just “blobs”, after
all.&lt;/p>
&lt;/blockquote>
&lt;p>Git 在追踪文件的过程中, 将你文件里的内容存储在 blob 中, 但是 blob 的功能相当的有
限, 它们没有文件名, 没有结构, 它们只是 &amp;ldquo;blob&amp;rdquo;, 仅此而已.&lt;/p>
&lt;blockquote>
&lt;p>In order for Git to represent the structure and naming of your files, it
attaches blobs as leaf nodes within a tree. Now, I can’t discover which
tree(s) a blob lives in just by looking at it, since it may have many, many
owners. But I know it must live somewhere within the tree held by the commit I
just made:&lt;/p>
&lt;/blockquote>
&lt;p>为了能够记录下你文件的名字和结构, Git 将 blob 看作是 tree 的叶子节点来管理. 我们
显然不能通过查看一个 blob 来看出它到底属于哪个 tree, 因为它很可能同时出现在相当
多个 tree 中. 但是我知道它一定存在于哪个我们刚刚创建的 commit 所管理的 tree 中:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git ls-tree HEAD
&lt;span style="color:#ae81ff">100644&lt;/span> blob af5626b4a114abcb82d63db7c8082c3c4756e51b greeting
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>There it is! This first commit added my greeting file to the repository. This
commit contains one Git tree, which has a single leaf: the greeting content’s
blob.&lt;/p>
&lt;/blockquote>
&lt;p>这就是那个 blob 了! 这是我刚提交的那个, 将我的 greeting 文件添加到 repository
中的 commit. 这个 commit 里面有一个 Git tree, 而这个 tree 中只有一个叶子节点 —
那就是存放着 greeting 文件内容的那个 blob.&lt;/p>
&lt;blockquote>
&lt;p>Although I can look at the tree containing my blob by passing HEAD to
&lt;code>ls-tree&lt;/code>, I haven’t yet seen the underlying tree object referenced by that
commit. Here are a few other commands to highlight that difference and thus
discover my tree:&lt;/p>
&lt;/blockquote>
&lt;p>尽管我可以通过向 &lt;code>ls-tree&lt;/code> 这个命令传入 HEAD 来查看那个含有我的 blob 的 tree, 但
是我还没从底层看到过那个被 commit 引用的 tree 对象. 这里有几个命令能说明这两者
有什么不同&lt;sup id="fnref:5">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:5" role="doc-noteref">5&lt;/a>&lt;/sup>, 我们来看看:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git rev-parse HEAD
588483b99a46342501d99e3f10630cfc1219ea32 &lt;span style="color:#75715e"># different on your system&lt;/span>
$ git cat-file -t HEAD
commit
$ git cat-file commit HEAD
tree 0563f77d884e4f79ce95117e2d686d7d6e282887
author John Wiegley &amp;lt;johnw@newartisans.com&amp;gt; &lt;span style="color:#ae81ff">1209512110&lt;/span> -0400
committer John Wiegley &amp;lt;johnw@newartisans.com&amp;gt; &lt;span style="color:#ae81ff">1209512110&lt;/span> -0400
Added my greeting
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>The first command decodes the HEAD alias into the commit it references, the
second verifies its type, while the third command shows the hash id of the
tree held by that commit, as well as the other information stored in the
commit object. The hash id for the commit is unique to my repository —
because it includes my name and the date when I made the commit — but the
hash id for the tree should be common between your example and mine,
containing as it does the same blob under the same name.&lt;/p>
&lt;/blockquote>
&lt;p>第一个命令通过 HEAD 找到它所指向的那个 commit, 第二个可以指明 HEAD 指针的类型,
而第三个命令则展示了那个 commit 所管理的 tree 的哈希值, 以及这个 commit 的一些
相关信息. 这个 commit 的哈希值和你的系统中得到的是不同的, 因为如你所见, 一个
commit 中式包含作者和提交时间,还有说明文字等信息的, 这些东西都会加入哈希值的计
算, 所以一定是不同的. 然而 tree 的哈希值却和你的系统中是一样的. 因为我们两的系
统中, 这个 tree 的内容是完全一致的.&lt;/p>
&lt;blockquote>
&lt;p>Let’s verify that this is indeed the same tree object:&lt;/p>
&lt;/blockquote>
&lt;p>我们来确认一下这个 tree 是真的和你那边相同:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git ls-tree 0563f77
&lt;span style="color:#ae81ff">100644&lt;/span> blob af5626b4a114abcb82d63db7c8082c3c4756e51b greeting
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>There you have it: my repository contains a single commit, which references a
tree that holds a blob — the blob containing the contents I want to record.
There’s one more command I can run to verify that this is indeed the case:&lt;/p>
&lt;/blockquote>
&lt;p>这里你可以看到, 我的 repository 中只包含一个 commit, 这个 commit 它关联到一个
tree, 而这个 tree 管理了一个 blob, 这个 blob 里面是我希望记录下来的内容. 还有一
个命令也能展示这个情况:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ find .git/objects -type f | sort
.git/objects/05/63f77d884e4f79ce95117e2d686d7d6e282887
.git/objects/58/8483b99a46342501d99e3f10630cfc1219ea32
.git/objects/af/5626b4a114abcb82d63db7c8082c3c4756e51b
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>From this output I see that the whole of my repo contains three objects, each
of whose hash id has appeared in the preceding examples. Let’s take one last
look at the types of these objects, just to satisfy curiosity:&lt;/p>
&lt;/blockquote>
&lt;p>在这个命令的输出中可以看到我的整个 repository 中只有三个对象, 每一个的哈希值你都
已经见到过了. 为了满足好奇心, 我们甚至可以看一下他们对应的类型:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git cat-file -t 588483b99a46342501d99e3f10630cfc1219ea32
commit
$ git cat-file -t 0563f77d884e4f79ce95117e2d686d7d6e282887
tree
$ git cat-file -t af5626b4a114abcb82d63db7c8082c3c4756e51b
blob
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>I could have used the show command at this point to view the concise contents
of each of these objects, but I’ll leave that as an exercise to the reader.&lt;/p>
&lt;/blockquote>
&lt;p>我本可以使用 show 命令来查看这些对象的内容, 但是我选择将这个留作读者的练习.&lt;/p>
&lt;h3 id="14-how-trees-are-made--tree-是怎样炼成的">1.4 How trees are made | Tree 是怎样炼成的
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#14-how-trees-are-made--tree-%e6%98%af%e6%80%8e%e6%a0%b7%e7%82%bc%e6%88%90%e7%9a%84">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>Every commit holds a single tree, but how are trees made? We know that blobs
are created by stuffing the contents of your files into blobs — and that
trees own blobs — but we haven’t yet seen how the tree that holds the blob
is made, or how that tree gets linked to its parent commit.&lt;/p>
&lt;/blockquote>
&lt;p>每个 commit 对应一个 tree, 但是 tree 是怎样炼成的呢? 我们已经知道了 blob 是基于
你的文件内容创建的数据块, 以及 Git 是用 tree 来管理 blob 的. 但是我们还并不理解
这些管理 blob 用的 tree 是如何被创建的, 或者说我们并不懂 tree 是如何连接到它的
父 commit 的.&lt;/p>
&lt;blockquote>
&lt;p>Let’s start with a new sample repository again, but this time by doing things
manually, so you can get a feeling for exactly what’s happening under the
hood:&lt;/p>
&lt;/blockquote>
&lt;p>我们来看上一节中提到的那个例子, 不过这次我们一步一步手动地做这些事情, 这样你可以
明白底层到底发生了什么:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ rm -fr greeting .git
$ echo &lt;span style="color:#e6db74">'Hello, world!'&lt;/span> &amp;gt; greeting
$ git init
$ git add greeting
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>It all starts when you first add a file to the index. For now, let’s just say
that the index is what you use to initially create blobs out of files. When I
added the file &lt;code>greeting&lt;/code>, a change occurred in my repository. I can’t see
this change as a commit yet, but here is one way I can tell what happened:&lt;/p>
&lt;/blockquote>
&lt;p>这一切是从你首次将一个文件加入 the index 开始的. 现在让我们先这么认为: the index
是你初次用来将文件内容装填进 blob 时所需要使用的工具.当我添加名为 &lt;code>greeting&lt;/code> 的
文件时, 我的 repository 发生了一次修改, 而我目前还不能够将这个更改看作是
commit, 但是我还是有一个方法能够搞清到底发生了什么:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git log &lt;span style="color:#75715e"># this will fail, there are no commits!&lt;/span>
fatal: bad default revision &lt;span style="color:#e6db74">'HEAD'&lt;/span>
$ git ls-files --stage &lt;span style="color:#75715e"># list blob referenced by the index&lt;/span>
&lt;span style="color:#ae81ff">100644&lt;/span> af5626b4a114abcb82d63db7c8082c3c4756e51b &lt;span style="color:#ae81ff">0&lt;/span> greeting
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>What’s this? I haven’t committed anything to the repository yet, but already
an object has come into being. It has the same hash id I started this whole
business with, so I know it represents the contents of my &lt;code>greeting&lt;/code> file. I
could use &lt;code>cat-file -t&lt;/code> at this point on the hash id, and I’d see that it was
a blob. It is, in fact, the same blob I got the first time I created this
sample repository. The same file will always result in the same blob (just in
case I haven’t stressed that enough).&lt;/p>
&lt;/blockquote>
&lt;p>那么这是啥呢? 我还没有 commit 过任何东西啊? 但是实际上已经有一个东西在上述的操作
中被生成了出来. 它的哈希值和我们一开始对 &lt;code>greeting&lt;/code> 算出来的那个是一样的, 所以
我们可以知道这个东西它里面是文件 &lt;code>greeting&lt;/code> 的数据. 此时, 我可以使用命令
&lt;code>cat-file-t&lt;/code> 来查看那个哈希值对应的文件, 实际上我们也看过了, 这个文件本身是一个
blob. 实际上它和我们一开始创建的那个 repository 中的那个 blob 是完全一样的东西.&lt;/p>
&lt;blockquote>
&lt;p>This blob isn’t referenced by a tree yet, nor are there any commits. At the
moment it is only referenced from a file named &lt;code>.git/index&lt;/code>, which references
the blobs and trees that make up the current index. So now let’s make a tree
in the repo for our blob to hang off of:&lt;/p>
&lt;/blockquote>
&lt;p>这个 blob 目前还没有被某个 tree 引用, 实际上也没有被任何一个 commit 引用. 在这个
时候, 实际上只有一个叫 &lt;code>.git/index&lt;/code>的文件引用了这个 blob, 这文件里面引用了所有
构成了当前的 index 的 blob 和 tree. 那么我们现在用这个 blob 来创建一个 tree:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git write-tree &lt;span style="color:#75715e"># record the contents of the index in a tree&lt;/span>
0563f77d884e4f79ce95117e2d686d7d6e282887
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>This number should look familiar as well: a tree containing the same blobs
(and sub-trees) will always have the same hash id. I don’t have a commit
object yet, but now there is a tree object in that repository which holds the
blob. The purpose of the low-level &lt;code>write-tree&lt;/code> command is to take whatever
the contents of the index are and tuck them into a new tree for the purpose of
creating a commit.&lt;/p>
&lt;/blockquote>
&lt;p>这个值也令人十分熟悉: 两个包含同样的 blob, 或者说 sub-tree 们的 tree 的哈希值总
时一样的. 我的 repository 中到现在还没有一个 commit, 但是已经有一个 tree 了, 这
个 tree 还引用了一个 blob. &lt;code>wirte-tree&lt;/code> 这个底层命令的存在, 使得我们可以将任何
在 the index 中的内容装载到一个新的 tree 中, 然后用来创建一个 commit.&lt;/p>
&lt;blockquote>
&lt;p>I can manually make a new commit object by using this tree directly, which is
just what the &lt;code>commit-tree&lt;/code> command does:&lt;/p>
&lt;/blockquote>
&lt;p>只需要用 &lt;code>commit-tree&lt;/code> 命令, 我现在就可以用上面创建的 tree 来手动地创建一个新的
commit 对象, 就像这样:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ echo &lt;span style="color:#e6db74">"Initial commit"&lt;/span> | git commit-tree 0563f77
5f1bc85745dcccce6121494fdd37658cb4ad441f
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>The raw &lt;code>commit-tree&lt;/code> command takes a tree’s hash id and makes a commit
object to hold it. If I had wanted the commit to have a parent, I would have
had to specify the parent commit’s hash id explicitly using the &lt;code>-p&lt;/code> option.
Also, note here that the hash id differs from what will appear on your system:
This is because my commit object refers to both my name, and the date at which
I created the commit, and these two details will always be different from
yours.&lt;/p>
&lt;/blockquote>
&lt;p>这个 &lt;code>commit-tree&lt;/code> 命令需要知道那个 tree 的哈希值, 然后创建一个 commit 来管理它.
如果我想要这个 commit 有一个父 commit, 那我需要使用 &lt;code>-p&lt;/code> 参数指定父 commit 的哈
希值. 以及我们可以注意到, 这个 tree 的哈希值和你的系统上的是不同的, 这是因为我
的 commit 中实际上包含了我的名字信息, 以及我创建这个 commit 的日期, 这两个值是
和你的仓库中的情况不同的.&lt;/p>
&lt;blockquote>
&lt;p>Our work is not done yet, though, since I haven’t registered the commit as
the new head of a branch:&lt;/p>
&lt;/blockquote>
&lt;p>活还没完, 我们还没更新这个 commit 对应的分支:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ echo 5f1bc85745dcccce6121494fdd37658cb4ad441f &amp;gt; .git/refs/heads/master
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>This command tells Git that the branch name “master” should now refer to our
recent commit. Another, much safer way to do this is by using the command
&lt;code>update-ref&lt;/code>:&lt;/p>
&lt;/blockquote>
&lt;p>这个命令告诉 Git 该用 &amp;ldquo;master&amp;rdquo; 这个名字指代我们新提交的那个 commit 了. 还有一点
要说的就是, 完成上面这件事情还有一个更安全的方法, 那就是使用 &lt;code>update-ref&lt;/code> 命令:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git update-ref refs/heads/master 5f1bc857
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>After creating &lt;code>master&lt;/code>, we must associate our working tree with it. Normally
this happens for you whenever you check out a branch:&lt;/p>
&lt;/blockquote>
&lt;p>在创建&lt;sup id="fnref:6">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:6" role="doc-noteref">6&lt;/a>&lt;/sup>完 master 之后,我们必须将我们的 working tree 调整成和 master 相匹配
的样子. 一般来说, 这个操作是用于 check out 一个分支的:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git symbolic-ref HEAD refs/heads/master
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>This command associates HEAD symbolically with the master branch. This is
significant because any future commits from the working tree will now
automatically update the value of &lt;code>refs/heads/master&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;p>这个命令会将 HEAD 绑定到 master 分支上. 这一点是相当自然的, 因为我们接下来再从
working tree 中做出更改之后提交的 commit 应该要更新这个名叫 master 的分支, 也就
是文件 &lt;code>refs/heads/master&lt;/code> 的值.&lt;/p>
&lt;blockquote>
&lt;p>It’s hard to believe it’s this simple, but yes, I can now use log to view my
newly minted commit:&lt;/p>
&lt;/blockquote>
&lt;p>虽然很难相信, 但是 Git 的工作就是如此简单, 我还可以使用 log 命令来查看我刚一步步
手动添加的这个 commit:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git log
commit 5f1bc85745dcccce6121494fdd37658cb4ad441f
Author: John Wiegley &amp;lt;johnw@newartisans.com&amp;gt;
Date: Mon Apr &lt;span style="color:#ae81ff">14&lt;/span> 11:14:58 &lt;span style="color:#ae81ff">2008&lt;/span> -0400
Initial commit
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>A side note: if I hadn’t set &lt;code>refs/heads/master&lt;/code> to point to the new commit,
it would have been considered “unreachable”, since nothing currently refers
to it nor is it the parent of a reachable commit. When this is the case, the
commit object will at some point be removed from the repository, along with
its tree and all its blobs. (This happens automatically by a command called
&lt;code>gc&lt;/code>, which you rarely need to use manually). By linking the commit to a name
within &lt;code>refs/heads&lt;/code>, as we did above, it becomes a reachable commit, which
ensures that it’s kept around from now on.&lt;/p>
&lt;/blockquote>
&lt;p>这里有个值得注意的地方: 如果我不曾将文件 &lt;code>refs/heads/master&lt;/code> 指向那个新的
commit, 这个 commit 会被看作是 &amp;ldquo;不可达的&amp;rdquo;, 因为没有任何其他的东西和他有关联, 它
也不是任何一个可达的 commit 的祖先. 当发生这种情况的时候, 这个 commit 将会被
Git 在某个时候从 repository 中移除, 它对应的 tree 和 blob 也一样, (这个事情会在
运行一个叫 &lt;code>gc&lt;/code> 的命令的时候自动地发生, 当然这个命令基本不会被用户手动运行). 如
果我们像上面做的那样, 在目录 &lt;code>.git/heads&lt;/code> 下用某个名字关联到这个 commit, 这个
commit 就是可达的了, 这可以保证它不会被删掉.&lt;/p>
&lt;h3 id="15-the-beauty-of-commits--commit-之美">1.5 The beauty of commits | Commit 之美
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#15-the-beauty-of-commits--commit-%e4%b9%8b%e7%be%8e">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>Some version control systems make “branchCes” into magical things, often
distinguishing them from the “main line” or “trunk”, while others discuss
the concept as though it were very different from commits. But in Git there
are no branches as separate entities: there are only blobs, trees and commits.
Since a commit can have one or more parents, and those commits can have
parents, this is what allows a single commit to be treated like a branch:
because it knows the whole history that led up to it.&lt;/p>
&lt;/blockquote>
&lt;p>一些版本控制系统将 branch 弄得很晦涩难懂, 经常将 &amp;ldquo;主线&amp;rdquo; (或者说 &amp;ldquo;主干&amp;rdquo; ), 和 &amp;ldquo;分
支&amp;rdquo; 区分开来, 而另一些版本控制系统则认为分支这个概念和 commit 有很大区别. 但是
在 Git 中并没有 branch 这个实体: Git 里只有 blob, tree 以及 commit 这三个概念.
因为一个 commit 可以有多个父 commit 而这些父 commit 也有父亲, 所以我们实际上可
以把单个 commit 当作 branch 来看待: 因为我们实际上可以从这个 commit 开始, 回溯
出文件内容是如何一步步地被修改, 更迭到当前这个 commit 的这整个历史.&lt;/p>
&lt;blockquote>
&lt;p>You can examine all the top-level, referenced commits at any time using the
&lt;code>branch&lt;/code> command:&lt;/p>
&lt;/blockquote>
&lt;p>你随时都可以通过 &lt;code>branch&lt;/code> 命令查看所有顶层的, 被引用的 commit :&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git branch -v
* master 5f1bc85 Initial commit
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Say it with me: A branch is nothing more than a named reference to a commit.
In this way, branches and tags are identical, with the sole exception that
tags can have their own descriptions, just like the commits they reference.
Branches are just names, but tags are descriptive, well, “tags”.&lt;/p>
&lt;/blockquote>
&lt;p>来跟着我一起念: &amp;ldquo;一个 branch 仅仅只是一个 commit 的引用, 只是这个引用本身还有一
个名字罢了.&amp;rdquo; 从这种角度来看, branch 和 tags 几乎没有什么区别, 只有一个例外: tag
就像它指向的那个 commit 一样还可以拥有自己的说明性文字. branch 只是 commit 的名
字, 但是 tag 是描述性的, 毕竟人家是 &amp;ldquo;标签&amp;rdquo; 嘛.&lt;/p>
&lt;blockquote>
&lt;p>But the fact is, we don’t really need to use aliases at all. For example, if
I wanted to, I could reference everything in the repository using only the
hash ids of its commits. Here’s me being straight up loco and resetting the
head of my working tree to a particular commit:&lt;/p>
&lt;/blockquote>
&lt;p>但是实际上我们也可以完全就不用这些别名. 比如说, 如果我想的话, 我实际上可以用
repository 中任何一个 commit 的哈希值来唯一地确定它. 下面这个是如果我想直接把我
的 working tree 对应的 HEAD 指向某个特定的 commit 需要用的命令:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git reset --hard 5f1bc85
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>The &amp;ndash;hard option says to erase all changes currently in my working tree,
whether they’ve been registered for a checkin or not (more will be said about
this command later). A safer way to do the same thing is by using &lt;code>checkout&lt;/code>:&lt;/p>
&lt;/blockquote>
&lt;p>这个 &lt;code>--hard&lt;/code> 选项的意思就是说, 让 Git 不管我当前的 working tree 中相对于给定的
commit 发生的所有更改是否有被记录下来过, 都把它们清除掉. 我们以后还会聊聊
&lt;code>reset&lt;/code> 这个命令. 完成这件事情还有一个更安全的方式, 那就是使用 checkout 命令:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git checkout 5f1bc85
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>The difference here is that changed files in my working tree are preserved. If
I pass the &lt;code>-f&lt;/code> option to &lt;code>checkout&lt;/code>, it acts the same in this case to &lt;code>reset --hard&lt;/code>, except that checkout only ever changes the working tree, whereas
&lt;code>reset --hard&lt;/code> changes the current branch&amp;rsquo;s HEAD to reference the specified
version of the tree.&lt;/p>
&lt;/blockquote>
&lt;p>这两者的区别在于, 后者对于我的 working tree 中的文件修改操作是有一定保护的, 对于
没记录过的数据会做一次是否清除的询问. 如果我们将参数 &lt;code>-f&lt;/code> 传递给 &lt;code>checkout&lt;/code>命
令, Git 的行为就会几乎和执行 &lt;code>reset --hard&lt;/code> 时一模一样. 它们两之间的区别在于
&lt;code>checkout&lt;/code> 命令只是清除 working tree 中的文件变化, 而 &lt;code>reset --hard&lt;/code> 会将当前的
HEAD 指向的那个 branch 一起移动到某个版本的 tree 上.&lt;/p>
&lt;blockquote>
&lt;p>Another joy of the commit-based system is that you can rephrase even the most
complicated version control terminology using a single vocabulary. For
example, if a commit has multiple parents, it’s a “merge commit” — since
it merged multiple commits into one. Or, if a commit has multiple children, it
represents the ancestor of a “branch”, etc. But really there is no
difference between these things to Git: to it, the world is simply a
collection of commit objects, each of which holds a tree that references other
trees and blobs, which store your data. Anything more complicated than this is
simply a device of nomenclature.&lt;/p>
&lt;/blockquote>
&lt;p>另一个使用基于 commit 的版本控制系统带来的乐趣是你可以用很简单的话语来描述版本控
制系统中那些晦涩难懂的术语. 比方说, 如果一个 commit 拥有两个父 commit, 那么我们
称这个 commit 是一个 &amp;ldquo;merge commit&amp;rdquo; — 因为它确实将多个 commit 合并了起来嘛.还
有,如果一个 commit 拥有多个子 commit 这代表着这里是分支的开始, 诸如此类. 但是实
际上这些术语所描述的事情对于 Git 来说并没有什么区别: 对于 Git 来说, 整个世界就
是由 commit 对象组成的, 它们管理着一个 tree 而这个 tree 中又引用了 其他的 tree
以及 blob, 在 blob 中实际存放着你的数据. 其他比这个复杂的东西都只是其他人对某个
行为取的名字罢了.&lt;/p>
&lt;blockquote>
&lt;p>Here is a picture of how all these pieces fit together:&lt;/p>
&lt;/blockquote>
&lt;p>这里有一张关于上面提到的种种对象之间的关系的图,也许可以帮助你的理解:&lt;/p>
&lt;p>&lt;img alt="Commits" src="https://jwiegley.github.io/git-from-the-bottom-up/images/commits.png" />&lt;/p>
&lt;h3 id="16-a-commit-by-any-other-name--commit-的名字">1.6 A commit by any other name&amp;hellip; | Commit 的名字
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#16-a-commit-by-any-other-name--commit-%e7%9a%84%e5%90%8d%e5%ad%97">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>Understanding commits is the key to grokking Git. You’ll know you have
reached the Zen plateau of branching wisdom when your mind contains only
commit topologies, leaving behind the confusion of branches, tags, local and
remote repositories, etc. Hopefully such understanding will not require
lopping off your arm — although I can appreciate if you’ve considered it by
now.&lt;/p>
&lt;/blockquote>
&lt;p>对 commit 的理解对于弄懂 Git 来说非常关键. 当你抛弃了那些令人困惑的什么 branch,
tag, 本地的以及远程 repository 之后, 当你的头脑中只剩下了 commit 相关的拓扑结构
的时候, 你将会对 &amp;ldquo;分支&amp;rdquo; 的智慧有崭新的理解. 想过去这种对 commit 的理解对于你来
不会是什么很困难的事情, 如果你现在就能开始试图理解 commit 我会很开心.&lt;/p>
&lt;blockquote>
&lt;p>If commits are the key, how you name commits is the doorway to mastery. There
are many, many ways to name commits, ranges of commits, and even some of the
objects held by commits, which are accepted by most of the Git commands. Here
’s a summary of some of the more basic usages:&lt;/p>
&lt;/blockquote>
&lt;p>如果说 commit 是关键, 那么如何给 commit 命名就是理解 Git 的门道所在. 实际上有很
多很多给 commit 命名的方式, 甚至连一次性给某个范围内的 commit 命名, 乃至于给一
些由 commit 管理的对象命名, 都是可以办到的. 大部分的 Git 命令都支持这种操作. 这
里有一些你需要知道的用法总结:&lt;/p>
&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>branchname&lt;/strong> — As has been said before, the name of any branch is simply
an alias for the most recent commit on that “branch”. This is the same as
using the word HEAD whenever that branch is checked out.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>branchname&lt;/strong> — 就像之前说的那样, branch 的名字只是那个 &amp;ldquo;分支&amp;rdquo; 上最新的一个
commit 的别名. 这和你使用单词 HEAD 来表示当前 check out 的那个 branch 是一样
的.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>tagname&lt;/strong> — A tag-name alias is identical to a branch alias in terms of
naming a commit. The major difference between the two is that tag aliases
never change, whereas branch aliases change each time a new commit is
checked in to that branch.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>tagname&lt;/strong> — 从给 commit 命名的角度来看, 一个 tag 几乎和一个 branch 是一模一
样的. 主要的区别在于 branch 指代的 commit 是可能会变化的, 它会在新的 commit
提交到这个 branch 的时候被更新成新的那个, 而 tag 不会.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>HEAD&lt;/strong> — The currently checked out commit is always called HEAD. If you
check out a specific commit — instead of a branch name — then HEAD refers
to that commit only and not to any branch. Note that this case is somewhat
special, and is called “using a detached HEAD” (I’m sure there’s a joke
to be told here&amp;hellip;).&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>HEAD&lt;/strong> — 当前 check out 的那个 commit 被称作 HEAD. 如果你 check out 了一个
特定的 commit — 而不是一个 branch 的话 — 那么 HEAD 就只是指向了那个 commit
而已, 并没有指向任何一个 branch. 需要注意的是这是一种特殊的情况, 我们称这种情
况为 HEAD 指针的脱离.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>c82a22c39cbc32&amp;hellip;&lt;/strong> — A commit may always be referenced using its
full, 40-character SHA1 hash id. Usually this happens during
cut-and-pasting, since there are typically other, more convenient ways to
refer to the same commit.&lt;/li>
&lt;li>&lt;strong>c82a22c&lt;/strong> — You only need use as many digits of a hash id as are needed
for a unique reference within the repository. Most of the time, six or seven
digits is enough.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>c82a22c39cbc32&amp;hellip;&lt;/strong> — 我们总是可以通过一个 commit 的那个由四十个字符组成的
SHA1 哈希值来找到它. 通常这种事情发生在你需要复制粘贴的时候, 因为在其他情况下,
几乎都有更方便的方式来找到它.&lt;/li>
&lt;li>&lt;strong>c82a22c&lt;/strong> — 其实你只需要给出足够在当前的 repository 中唯一确定一个 commit
的哈希值的前缀就足以找到那个 commit 了, 一般来说是 6 到 7 位左右.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>name^&lt;/strong> — The parent of any commit is referenced using the caret symbol.
If a commit has more than one parent, the first is used.&lt;/li>
&lt;li>&lt;strong>name^^&lt;/strong> — Carets may be applied successively. This alias refers to “the
parent of the parent” of the given commit name.&lt;/li>
&lt;li>&lt;strong>name^2&lt;/strong> — If a commit has multiple parents (such as a merge commit), you
can refer to the &lt;em>nth&lt;/em> parent using &lt;code>name^n&lt;/code>.&lt;/li>
&lt;li>&lt;strong>name~10&lt;/strong> — A commit’s &lt;em>nth&lt;/em> generation ancestor may be referenced
using a tilde (~) followed by the ordinal number. This type of usage is
common with &lt;code>rebase -i&lt;/code>, for example, to mean “show me a bunch of recent
commits”. This is the same as name^^^^^^^^^^.&lt;/li>
&lt;li>&lt;strong>name:path&lt;/strong> — To reference a certain file within a commit’s content
tree, specify that file’s name after a colon. This is helpful with show, or
to show the difference between two versions of a committed file:&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>接下来的几条中的 &amp;ldquo;name&amp;rdquo; 可以使用上文提到的所有可以找到 commit 的字符串替换, 以下
是一些相对某个 commit 查询另一个 commit 的方法:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>name^&lt;/strong> — &lt;code>^&lt;/code> 这个符号可以找到一个 commit 的父 commit, 如果这个 commit 有多
个父 commit 那么查询的结果是第一个&lt;sup id="fnref:7">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:7" role="doc-noteref">7&lt;/a>&lt;/sup>.&lt;/li>
&lt;/ul>
&lt;!-- end list -->
&lt;ul>
&lt;li>&lt;strong>name^^&lt;/strong> — &lt;code>^&lt;/code> 这个符号是可以被连续的调用的&lt;sup id="fnref:8">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:8" role="doc-noteref">8&lt;/a>&lt;/sup>, 这意味着你寻找的是当前
commit 的爷爷.&lt;/li>
&lt;/ul>
&lt;!-- end list -->
&lt;ul>
&lt;li>&lt;strong>name^2&lt;/strong> — 如果一个 commit 有多个父 commit — 比方说一个 merge commit — 那
么如果你想找其中的第 n 个父 commit, 可以使用 &lt;code>name^n&lt;/code>.&lt;/li>
&lt;li>&lt;strong>name~10&lt;/strong> — 这意味着一个 commit 十代之前的那个祖宗, 第 n 代祖先,可以通过一
个 &lt;code>~&lt;/code> 符号后面跟着一个数字来找到. 这一般是用于执行 &lt;code>rebase -i&lt;/code> 命令的时候用
的. 效果和 name^^^^^^^^^^ 是完全一致的.&lt;/li>
&lt;li>&lt;strong>name:path&lt;/strong> — 你可以用这种方式来从一个 commit 中根据路径来找到 tree 中的某
个特定文件. 一般来说这个功能在你想比较两个 commit 之间某个文件的差异的时候比
较有用, 比如像下面这样:&lt;/li>
&lt;/ul>
&lt;!-- end list -->
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash"> $ git diff HEAD^1:Makefile HEAD^2:Makefile
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>name^&amp;#123;tree}&lt;/strong> — You can reference just the tree held by a commit, rather
than the commit itself.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>name^&amp;#123;tree}&lt;/strong> — 上面说的都是找到某个 commit 的方法, 而你可以通过这个方式来
指定一个 commit 管理的那个 tree.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>name1..name2&lt;/strong> — This and the following aliases indicate &lt;em>commit ranges&lt;/em>,
which are supremely useful with commands like log for seeing what’s
happened during a particular span of time. The syntax to the left refers to
all the commits reachable from &lt;strong>name2&lt;/strong> back to, but not including,
&lt;strong>name1&lt;/strong>. If either &lt;strong>name1&lt;/strong> or &lt;strong>name2&lt;/strong> is omitted, HEAD is used in its
place.&lt;/li>
&lt;li>&lt;strong>name1&amp;hellip;name2&lt;/strong> — A “triple-dot” range is quite different from the
two-dot version above. For commands like log, it refers to all the commits
referenced by &lt;strong>name1&lt;/strong> or &lt;strong>name2&lt;/strong>, but not by both. The result is then a
list of all the unique commits in both branches. For commands like &lt;code>diff&lt;/code>,
the range expressed is between &lt;strong>name2&lt;/strong> and the common ancestor of
&lt;strong>name1&lt;/strong> and &lt;strong>name2&lt;/strong>. This differs from the &lt;code>log&lt;/code> case in that changes
introduced by &lt;strong>name1&lt;/strong> are not shown.&lt;/li>
&lt;li>&lt;strong>master..&lt;/strong> — This usage is equivalent to “&lt;code>master..HEAD&lt;/code>”. I’m adding
it here, even though it’s been implied above, because I use this kind of
alias constantly when reviewing changes made to the current branch.&lt;/li>
&lt;li>&lt;strong>..master&lt;/strong> — This, too, is especially useful after you’ve done a &lt;code>fetch&lt;/code>
and you want to see what changes have occurred since your last &lt;code>rebase&lt;/code> or
&lt;code>merge&lt;/code>.&lt;/li>
&lt;li>&lt;strong>&amp;ndash;since=&amp;ldquo;2 weeks ago&amp;rdquo;&lt;/strong> — Refers to all commits since a certain date.&lt;/li>
&lt;li>&lt;strong>&amp;ndash;until=”1 week ago”&lt;/strong> — Refers to all commits before a certain date.&lt;/li>
&lt;li>&lt;strong>&amp;ndash;grep=pattern&lt;/strong> — Refers to all commits whose commit message matches the
regular expression pattern.&lt;/li>
&lt;li>&lt;strong>&amp;ndash;committer=pattern&lt;/strong> — Refers to all commits whose committer matches the
pattern.&lt;/li>
&lt;li>&lt;strong>&amp;ndash;author=pattern&lt;/strong> — Refers to all commits whose author matches the
pattern. The author of a commit is the one who created the changes it
represents. For local development this is always the same as the committer,
but when patches are being sent by e-mail, the author and the committer
usually differ.&lt;/li>
&lt;li>&lt;strong>&amp;ndash;no-merges&lt;/strong> — Refers to all commits in the range that have only one
parent — that is, it ignores all merge commits.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>以下都是一个范围内的 commit 的别名, 通常来说, 在调用 log 命令来查看过去的某段时
间内, 代码到底发生了什么变化的时候非常的有用:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>name1..name2&lt;/strong> — 这意味着你选中了在树上遍历时, 从 name2 出发, 在遍历到
name1 之前所有可以遍历到的 commit, 当然是不包括 name1 的.如果你在 name1 或者
name2 的位置什么都没写, 那么意味着你希望在相应位置使用 HEAD 来代替
name1/name2.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>master..&lt;/strong> — 这样可以选中 master 到 HEAD 的所有 commit, 一般来说, 在查看
dev 比 master 快了多少的时候比较有用.&lt;/li>
&lt;li>&lt;strong>..master&lt;/strong> — 这个通常用于: 在你完成了一次 fetch 之后, 想查看自己本地的
master 在上一次 rebase 或者是 merge 之后在远程仓库中被做了什么样的修改.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>name1&amp;hellip;name2&lt;/strong> — 中间有三个点的范围和上面说的只有两个点的有很大的差异. 对
于 &lt;code>log&lt;/code> 这类的命令, 三个点的范围选中了所有被 name1 或者 name2 其中之一直接或
者间接引用的所有 commit, 注意: 并不包括两者同时引用的 commit. 结果上来说, 这
意味着你找到了一系这两个分支并不共同拥有的 commit. 而对于像 &lt;code>diff&lt;/code> 这样的指令
而言, 这样会选中从 name2 到 name1 和 name2 的最近公共祖先之间的所有 commit.
&lt;code>differ&lt;/code> 和 &lt;code>log&lt;/code> 的区别实际上在于, 从 LCA 到 name1 这条路径上的 commit 并不会
被选中.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>&amp;ndash;since=&amp;ldquo;2 weeks ago&amp;rdquo;&lt;/strong> — 这样可以选中某个特定日期之后的所有 commit.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>&amp;ndash;until=”1 week ago”&lt;/strong> — 与上一条类似, 选中某个特定日期之前的所有 commit.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>&amp;ndash;grep=pattern&lt;/strong> — 选中所有在 commit message 中有和 pattern 匹配的内容的
commit.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>&amp;ndash;committer=pattern&lt;/strong> — 选中所有提交者名字和 pattern 相匹配的 commit.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>&amp;ndash;author=pattern&lt;/strong> — 选中所有作者名字和 pattern 相匹配的 commit. 作者和提交
者是有区别的, 作者是指实际上对目录中的内容做出更改的那个人, 而提交者是将这个
更改加入到 repository 中的人. 对于本地仓库来说, 作者和提交者总是一样的, 但是
比如说某些更改是通过邮件发送的, 那么作者和提交者往往是不同的.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>&amp;ndash;no-merges&lt;/strong> — 顾名思义, 是指选中所有只有一个父 commit 的 commit, 即将所有
merge commit 排除在外.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>Most of these options can be mixed-and-matched. Here is an example which shows
the following log entries: changes made to the current branch (branched from
master), by myself, within the last month, which contain the text “foo”:&lt;/p>
&lt;/blockquote>
&lt;p>这些选项通常都可以混合使用. 下面这个例子是我选中在当前分支 (这是一个比 master 更
快的分支, 比如 dev 分支) 中由我在过去一个月内写的, 并且还在 commit message 中含
有 &amp;ldquo;foo&amp;rdquo; 的所有 commit:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git log --grep&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">'foo'&lt;/span> --author&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">'johnw'&lt;/span> --since&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">"1 month ago"&lt;/span> master..
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="17-branching-and-the-power-of-rebase--分支-以及-rebase-的力量">1.7 Branching and the power of rebase | 分支, 以及 rebase 的力量
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#17-branching-and-the-power-of-rebase--%e5%88%86%e6%94%af-%e4%bb%a5%e5%8f%8a-rebase-%e7%9a%84%e5%8a%9b%e9%87%8f">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>One of Git’s most capable commands for manipulating commits is the
innocently-named rebase command. Basically, every branch you work from has one
or more “base commits”: the commits that branch was born from. Take the
following typical scenario, for example. Note that the arrows point back in
time because each commit references its parent(s), but not its children.
Therefore, the D and Z commits represent the heads of their respective
branches:&lt;/p>
&lt;/blockquote>
&lt;p>Git 最好用的命令之一就是操作 commit 的 rebase 命令, 顾名思义, rebase 是用来更改
commit 的 base 的. 通常来说, 你的每个分支都会有一个或者是更多个的 &amp;ldquo;base
commits&amp;rdquo;: 指你的分支是从哪个 commit 开始创建的. 以下面这张图描述的这种典型情况为
例, 我们可以注意到箭头是指向父 commit 的, 而不是指向子 commit, 因为实际上是子
commit 中含有对父 commit 的引用. 我们通常将 D 和 Z 视作它们所在分支的 &lt;code>HEAD&lt;/code>:&lt;/p>
&lt;p>&lt;img alt="Branching" src="https://jwiegley.github.io/git-from-the-bottom-up/images/branching.png" />&lt;/p>
&lt;blockquote>
&lt;p>In this case, running branch would show two “heads”: &lt;code>D&lt;/code> and &lt;code>Z&lt;/code>, with the
common parent of both branches being A. The output of show-branch shows us
just this information:&lt;/p>
&lt;/blockquote>
&lt;p>在如上图所示的这个情况中, 我们可以看到这些分支一共有两个 &amp;ldquo;&lt;code>HEAD&lt;/code>&amp;quot;: &lt;code>D&lt;/code> 和 &lt;code>Z&lt;/code>,
它们的公共祖先是 &lt;code>A&lt;/code>. &lt;code>show-branch&lt;/code> 指令的输出结果向我们展示了这一点:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git branch
Z
* D
$ git show-branch
! &lt;span style="color:#f92672">[&lt;/span>Z&lt;span style="color:#f92672">]&lt;/span> Z
* &lt;span style="color:#f92672">[&lt;/span>D&lt;span style="color:#f92672">]&lt;/span> D
--
* &lt;span style="color:#f92672">[&lt;/span>D&lt;span style="color:#f92672">]&lt;/span> D
* &lt;span style="color:#f92672">[&lt;/span>D^&lt;span style="color:#f92672">]&lt;/span> C
* &lt;span style="color:#f92672">[&lt;/span>D~2&lt;span style="color:#f92672">]&lt;/span> B
+ &lt;span style="color:#f92672">[&lt;/span>Z&lt;span style="color:#f92672">]&lt;/span>Z
+ &lt;span style="color:#f92672">[&lt;/span>Z^&lt;span style="color:#f92672">]&lt;/span>Y
+ &lt;span style="color:#f92672">[&lt;/span>Z~2&lt;span style="color:#f92672">]&lt;/span> X
+ &lt;span style="color:#f92672">[&lt;/span>Z~3&lt;span style="color:#f92672">]&lt;/span> W
+* &lt;span style="color:#f92672">[&lt;/span>D~3&lt;span style="color:#f92672">]&lt;/span> A
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Reading this output takes a little getting used to, but essentially it’s no
different from the diagram above. Here’s what it tells us:&lt;/p>
&lt;/blockquote>
&lt;p>阅读这个输出需要我们花一点时间来习惯它, 但是它实际上给出的信息和上面那张示意图没
有什么区别. 以下是这个输出的阅读方法&lt;sup id="fnref:9">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:9" role="doc-noteref">9&lt;/a>&lt;/sup>:&lt;/p>
&lt;blockquote>
&lt;ul>
&lt;li>The branch we’re on experienced its first divergence at commit &lt;code>A&lt;/code> (also
known as commit &lt;code>D~3&lt;/code>, and even &lt;code>Z~4&lt;/code> if you feel so inclined). The syntax
&lt;code>commit^&lt;/code> is used to refer to the parent of a commit, while &lt;code>commit~3&lt;/code>
refers to its third parent, or great-grandparent.&lt;/li>
&lt;li>Reading from bottom to top, the first column (the plus signs) shows a
divergent branch named &lt;code>Z&lt;/code> with four commits: &lt;code>W&lt;/code>, &lt;code>X&lt;/code>, &lt;code>Y&lt;/code> and &lt;code>Z&lt;/code>.&lt;/li>
&lt;li>The second column (the asterisks) show the commits which happened on the
current branch, namely three commits: &lt;code>B&lt;/code>, &lt;code>C&lt;/code> and &lt;code>D&lt;/code>.&lt;/li>
&lt;li>The top of the output, separated from the bottom by a dividing line,
identifies the branches displayed, which column their commits are labelled
by, and the character used for the labeling.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>我们目前在 repository 中拥有的两个分支是从 &lt;code>A&lt;/code> 开始分支的.&lt;/li>
&lt;li>从下往上读,第一列字符 (一列 &lt;code>+&lt;/code>) 告诉我们, 分支 &lt;code>Z&lt;/code> 在分叉后拥有的 commit 依次
是: &lt;code>W&lt;/code>, &lt;code>X&lt;/code>, &lt;code>Y&lt;/code> 还有 &lt;code>Z&lt;/code>.&lt;/li>
&lt;li>同样, 第二列字符 (一列 &lt;code>*&lt;/code>) 告诉我们, 分支 &lt;code>D&lt;/code> 在分叉后拥有的 commit 依次是:
&lt;code>B&lt;/code>, &lt;code>C&lt;/code> 还有 &lt;code>D&lt;/code>.&lt;/li>
&lt;li>在整个输出的最上面有一些被 &lt;code>--&lt;/code> 分开的部分, 这里是在告诉我们分支的 &lt;code>HEAD&lt;/code> 是
谁, 以及下面的一系列输出中, 开头的第几列是和这个分支对应的, 使用 &lt;code>*&lt;/code> 标注的分
支是当前 checkout 的, 其他的分支 &lt;code>HEAD&lt;/code> 使用 &lt;code>!&lt;/code>, 而在接下来的部分中使用 &lt;code>+&lt;/code>.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>The action we’d like to perform is to bring the working branch &lt;code>Z&lt;/code> back up to
speed with the main branch, &lt;code>D&lt;/code>. In other words, we want to incorporate the
work from &lt;code>B&lt;/code>, &lt;code>C&lt;/code>, and &lt;code>D&lt;/code> into &lt;code>Z&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;p>我们接下来想做的事情是把分支 &lt;code>Z&lt;/code> 并入主分支 &lt;code>D&lt;/code>. 换句话说, 我们希望把&lt;code>B&lt;/code>, &lt;code>C&lt;/code> 以
及 &lt;code>D&lt;/code> 做出的更改也写进 &lt;code>Z&lt;/code>.&lt;/p>
&lt;blockquote>
&lt;p>In other version control systems this sort of thing can only be done using a
“branch merge”. In fact, a branch merge can still be done in Git, using
&lt;code>merge&lt;/code>, and remains needful in the case where &lt;code>Z&lt;/code> is a published branch and
we don’t want to alter its commit history. Here are the commands to run:&lt;/p>
&lt;/blockquote>
&lt;p>在其他的版本控制系统中, 这是一件只能通过 &amp;ldquo;分支合并&amp;rdquo; 操作完成的事情. 而实际上所谓
的分支合并在 Git 中也是可以被完成的, 只要使用 &lt;code>merge&lt;/code> 命令就可以了, 如果我们不
想更改 &lt;code>Z&lt;/code> 的 commit 历史, 那么我们可以运行这些命令:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git checkout Z &lt;span style="color:#75715e"># switch to the Z branch&lt;/span>
$ git merge D &lt;span style="color:#75715e"># merge commits B, C and D into Z&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>This is what the repository looks like afterward:&lt;/p>
&lt;/blockquote>
&lt;p>运行完了以后你的 repository 看起来会像是下面图中的这样:&lt;/p>
&lt;p>&lt;img alt="BranchMerge" src="https://jwiegley.github.io/git-from-the-bottom-up/images/branch-merge.png" />&lt;/p>
&lt;blockquote>
&lt;p>If we checked out the &lt;code>Z&lt;/code> branch now, it would contain the contents of the
previous &lt;code>Z&lt;/code> (now referenceable as &lt;code>Z^&lt;/code>), merged with the contents of &lt;code>D&lt;/code>.
(Though note: a real merge operation would have required resolving any
conflicts between the states of &lt;code>D&lt;/code> and &lt;code>Z&lt;/code>).&lt;/p>
&lt;/blockquote>
&lt;p>如果我现在检出分支 &lt;code>Z&lt;/code>, 注意 &amp;ldquo;分支 &lt;code>Z&lt;/code>&amp;rdquo; 只是一个 commit 的别名, 它曾经指 commit
Z, 而现在它是图中的 &lt;code>Z'&lt;/code>, 分支 &lt;code>Z&lt;/code> (现在指代 commit &lt;code>Z&lt;/code>) 现在会包含 commit &lt;code>Z&lt;/code>
和 commit &lt;code>D&lt;/code> 合并过后的内容. 当然, 要进行一个 &lt;code>merge&lt;/code> 操作需要先处理所有 &lt;code>D&lt;/code>
和 &lt;code>Z&lt;/code> 之间的冲突.&lt;/p>
&lt;blockquote>
&lt;p>Although the new &lt;code>Z&lt;/code> now contains the changes from &lt;code>D&lt;/code>, it also includes a new
commit to represent the merging of &lt;code>Z&lt;/code> with &lt;code>D&lt;/code>: the commit now shown as &lt;code>Z ’&lt;/code>. This commit doesn’t add anything new, but represents the work done to
bring &lt;code>D&lt;/code> and &lt;code>Z&lt;/code> together. In a sense it’s a “meta-commit”, because its
contents are related to work done solely in the repository, and not to new
work done in the working tree.&lt;/p>
&lt;/blockquote>
&lt;p>在新的 &lt;code>Z&lt;/code> 现在包含了 &lt;code>D&lt;/code> 中做出的更改的同时, 在新 &lt;code>Z&lt;/code> 中同样存在着一个新的
commit, 这个 commit 是用来合并 &lt;code>Z&lt;/code> 和 &lt;code>D &lt;/code>的: 就是上图中的 &lt;code>Z'&lt;/code>. 这个 commit
很可能没有添加任何新的更改, 只是意味着做了一些工作将 &lt;code>D&lt;/code> 和 &lt;code>Z&lt;/code> 合并到了一起.
某种意义上来说, 这算是一种 &amp;ldquo;meta-commit&amp;rdquo;, 因为它的内容只是对 repository 的更改,
而不是对 working tree 的更改.&lt;/p>
&lt;blockquote>
&lt;p>There is a way, however, to transplant the &lt;code>Z&lt;/code> branch straight onto &lt;code>D&lt;/code>,
effectively moving it forward in time: by using the powerful rebase command.
Here’s the graph we’re aiming for:&lt;/p>
&lt;/blockquote>
&lt;p>实际上还有一种办法能直接将分支 &lt;code>Z&lt;/code> 移植到 &lt;code>D&lt;/code> 上, 通过 &lt;code>rebase&lt;/code> 命令, 可以直接把
&lt;code>D&lt;/code> 快进. 如下图所示:&lt;/p>
&lt;p>&lt;img alt="Rebase" src="https://jwiegley.github.io/git-from-the-bottom-up/images/rebase.png" />&lt;/p>
&lt;blockquote>
&lt;p>This state of affairs most directly represents what we’d like done: for our
local, development branch &lt;code>Z&lt;/code> to be based on the latest work in the main
branch &lt;code>D&lt;/code>. That’s why the command is called “rebase”, because it changes
the base commit of the branch it’s run from. If you run it repeatedly, you
can carry forward a set of patches indefinitely, always staying up-to-date
with the main branch, but without adding unnecessary merge commits to your
development branch. Here are the commands to run, compared to the merge
operation performed above:&lt;/p>
&lt;/blockquote>
&lt;p>我们可以这样来描述我们实际上想干啥: 我们直接把我们在本地进行开发的分支 &lt;code>Z&lt;/code> 的
&amp;ldquo;base commit&amp;rdquo; 改成了 &lt;code>D&lt;/code>. 这正是上问提到的 &lt;code>rebase&lt;/code> 命令为什么会被称作 &amp;ldquo;rebase&amp;rdquo;.
如果你不停地运行这个命令, 就可以在没有额外的 merge commit 的情况下更新主分支的数
据. 以下是运行 &lt;code>rebase&lt;/code> 需要的指令:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git checkout Z &lt;span style="color:#75715e"># switch to the Z branch&lt;/span>
$ git rebase D &lt;span style="color:#75715e"># change Z’s base commit to point to D&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Why is this only for local branches? Because every time you rebase, you’re
potentially changing every commit in the branch. Earlier, when &lt;code>W&lt;/code> was based
on &lt;code> A&lt;/code>, it contained only the changes needed to transform &lt;code>A&lt;/code> into &lt;code>W&lt;/code>.
After running rebase, however, &lt;code>W&lt;/code> will be rewritten to contain the changes
necessary to transform &lt;code>D&lt;/code> into &lt;code>W’&lt;/code>. Even the transformation from &lt;code>W&lt;/code> to &lt;code>X&lt;/code>
is changed, because &lt;code>A+W+X&lt;/code> is now &lt;code>D+W’+X’&lt;/code> — and so on. If this were a
branch whose changes are seen by other people, and any of your downstream
consumers had created their own local branches off of &lt;code>Z&lt;/code>, their branches
would now point to the old &lt;code>Z&lt;/code>, not the new &lt;code>Z’&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;p>为什么我们说这种操作只能对本地分支进行呢? 这是因为每当运行 &lt;code>rebase&lt;/code> 命令的时候,
我们实际上将分支中的每一个 commit 都进行了更改. 之前的 &lt;code>W&lt;/code> 是基于 &lt;code>A&lt;/code> 做出的修
改, 如果我们认为一个 commit 中只包含更改的信息的话&lt;sup id="fnref:10">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:10" role="doc-noteref">10&lt;/a>&lt;/sup>, 那么 &lt;code>W&lt;/code> 实际上只含
有 &amp;ldquo;从 &lt;code>A&lt;/code> 变成 &lt;code>W&lt;/code>&amp;rdquo; 做出的变化. 但是运行完 &lt;code>rebase&lt;/code> 之后, &lt;code>W&lt;/code> 实际上就包含了从
&lt;code>D&lt;/code> 变成 &lt;code>W'&lt;/code> 产生的变化. 以及 &lt;code>W&lt;/code> 和 &lt;code>X&lt;/code> 之间的更改都产生了改变, 因为原来的
&lt;code>A+W+X&lt;/code> 现在是 &lt;code>D+W'+X'&lt;/code> — 其他的 commit 也有类似的变化. 如果这样的一个分支中的
更改对于其他人来说是可见的, 以及从你的 repository 中获得源代码的下游人员中有人
用原来的 &lt;code>Z&lt;/code> 创建了新的分支, 那么他们的 &lt;code>Z&lt;/code> 并不会指向 &lt;code>rebase&lt;/code> 后的那个新 &lt;code>Z&lt;/code>.&lt;/p>
&lt;blockquote>
&lt;p>Generally, the following rule of thumb can be used: Use rebase if you have a
local branch with no other branches that have branched off from it, and use
merge for all other cases. merge is also useful when you’re ready to pull
your local branch’s changes back into the main branch.&lt;/p>
&lt;/blockquote>
&lt;p>通常来说, 以下的经验法则是很有用的: 只要一个本地分支上没有再分出其他的分支, 那么
我们就使用 &lt;code>rebase&lt;/code> 命令来合并分支, 其他的情况一律使用 &lt;code>merge&lt;/code>. &lt;code>merge&lt;/code> 这个命
令在你想把你的本地分支合并进主分支的时候很有用.&lt;/p>
&lt;h3 id="18-interactive-rebasing--交互式的-rebase-命令">1.8 Interactive rebasing | 交互式的 rebase 命令
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#18-interactive-rebasing--%e4%ba%a4%e4%ba%92%e5%bc%8f%e7%9a%84-rebase-%e5%91%bd%e4%bb%a4">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>When rebase was run above, it automatically rewrote all the commits from &lt;code>W&lt;/code>
to &lt;code>Z&lt;/code> in order to rebase the &lt;code>Z&lt;/code> branch onto the &lt;code>D&lt;/code> commit (i.e., the head
commit of the &lt;code>D&lt;/code> branch). You can, however, take complete control over how
this rewriting is done. If you supply the &lt;code>-i&lt;/code> option to &lt;code>rebase&lt;/code>, it will pop
you into an editing buffer where you can choose what should be done for every
commit in the local &lt;code>Z&lt;/code> branch:&lt;/p>
&lt;/blockquote>
&lt;p>就像上面说的那样, 当你运行一个 rebase 命令的时候, 它将为了将 &lt;code>Z&lt;/code> 分支的 base 更
改到分支 &lt;code>D&lt;/code> 的 &lt;code>HEAD&lt;/code> 上, 而自动的重写从 &lt;code>W&lt;/code> 到 &lt;code>Z&lt;/code> 的所有 commit. 其实你是完
全可以控制这个重写的过程的. 给 &lt;code>rebase&lt;/code> 传递一个 &lt;code>-i&lt;/code> 参数, 那么你将进入一个可
编辑的缓冲区, 在这个界面里, 你可以选择到底应该对 &lt;code>Z&lt;/code> 分支上的每个 commit 做些什
么:&lt;/p>
&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>pick&lt;/strong> — This is the default behavior chosen for every commit in the
branch if you don’t use interactive mode. It means that the commit in
question should be applied to its (now rewritten) parent commit. For every
commit that involves conflicts, the &lt;code>rebase&lt;/code> command gives you an
opportunity to resolve them.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>pick&lt;/strong> — 这是如果你不使用交互模式的时候会执行的默认操作, 它意味着你选中了这
个分支中的每一个 commit, 也就是说: 每一个分支上的 commit 都将更改自己的父
commit 到它被重写过的父 commit 上. 对于每个可能产生冲突的 commit, 你会有机会解
决它们.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>squash&lt;/strong> — A squashed commit will have its contents “folded” into the
contents of the commit preceding it. This can be done any number of times.
If you took the example branch above and squashed all of its commits (except
the first, which must be a &lt;strong>pick&lt;/strong> in order to &lt;strong>squash&lt;/strong>), you would end
up with a new &lt;code>Z&lt;/code> branch containing only one commit on top of &lt;code>D&lt;/code>. Useful if
you have changes spread over multiple commits, but you’d like the history
rewritten to show them all as a single commit.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>squash&lt;/strong> — 一个被 &amp;ldquo;压扁&amp;rdquo; 的 commit 将会将它之中的更改 &amp;ldquo;折叠&amp;rdquo; 进它的新父
commit 里. 这个操作可以不限次数地进行. 如果你对上面说的那个例子, 将除了第一个
commit 以外 (为了进行 &lt;strong>squash&lt;/strong> 需要先进行一个 &lt;strong>pick&lt;/strong>) 的所有 commit &amp;ldquo;压
扁&amp;rdquo;, 那么你将会将分支 &lt;code>D&lt;/code> 中的所有更改折叠成一个 commit, 然后将这个 commit 应
用在分支 &lt;code>Z&lt;/code> 上. 如果你不想保留更改历史, 只想留下一个 commit 的话, 这个模式还
挺好用的.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>&lt;strong>edit&lt;/strong> — If you mark a commit as &lt;strong>edit&lt;/strong>, the rebasing process will stop
at that commit and leave you at the shell with the current working tree set to
reflect that commit. The index will have all the commit’s changes registered
for inclusion when you run &lt;code>commit&lt;/code>. You can thus make whatever changes you
like: amend a change, undo a change, etc.; and after committing, and running
&lt;code>rebase --continue&lt;/code>, the commit will be rewritten as if those changes had been
made originally.&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>edit&lt;/strong> — 如果你将一个 commit 标记为了 &lt;strong>edit&lt;/strong> 模式, 那么这个 rebase 命令将
在这个 commit 这里停下来, 然后让你在你的 working tree 中编辑文件, 你可以重做
一次这个 commit 做过的修改. 随后你运行 &lt;code>commit&lt;/code> 命令的时候, the index 中将会
含有所有已经提交的更改. 你可能会在类似以下的情况下使用这个模式: 添加一个更改
到这个 commit 中, 或者说撤销一个更改. 在你提交 commit 之后, 运行 &lt;code>rebase --continue&lt;/code>, 那个被标记为 edit 的 commit 将会被你新编辑的这个 commit 代替.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;ul>
&lt;li>&lt;strong>(drop)&lt;/strong> — If you remove a commit from the interactive rebase file, or if
you comment it out, the commit will simply disappear as if it had never been
checked in. Note that this can cause merge conflicts if any of the later
commits in the branch depended on those changes.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;strong>(drop)&lt;/strong> — 如果你打算删除一个 commit. 这个 commit 将会像是从来没有被记录过
一样不复存在. 注意:如果后续的其他 commit 是基于这个 commit 的更改的, 那么这个
操作很可能引起合并冲突.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>The power of this command is hard to appreciate at first, but it grants you
virtually unlimited control over the shape of any branch. You can use it to:&lt;/p>
&lt;/blockquote>
&lt;p>交互式的 rebase 命令的用处很难一下子说清, 但是这个命令让你有了几乎不受限制的能
力, 来对任何分支做出任何形式的调整. 你实际上可以用这个命令来:&lt;/p>
&lt;blockquote>
&lt;ul>
&lt;li>Collapse multiple commits into single ones.&lt;/li>
&lt;li>Re-order commits.&lt;/li>
&lt;li>Remove incorrect changes you now regret.&lt;/li>
&lt;li>Move the base of your branch &lt;em>onto any other commit in the repository&lt;/em>.&lt;/li>
&lt;li>Modify a single commit, to amend a change long after the fact.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;ul>
&lt;li>将多个 commit 折叠成一个.&lt;/li>
&lt;li>重新调整 commit 的顺序.&lt;/li>
&lt;li>将不正确的更改删除.&lt;/li>
&lt;li>将你的分支的 base 移动到你的 repository 中的任何一个 commit 上.&lt;/li>
&lt;li>更改一个单独的 commit, 比如说在实际上这个 commit 已经被提交很久之后, 往里面添
加更改.&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>I recommend reading the man page for &lt;code>rebase&lt;/code> at this point, as it contains
several good examples how the true power of this beast may be unleashed. To
give you one last taste of how potent a tool this is, consider the following
scenario and what you’d do if one day you wanted to migrate the secondary
branch &lt;code>L&lt;/code> to become the new head of &lt;code>Z&lt;/code>:&lt;/p>
&lt;/blockquote>
&lt;p>我在这里十分建议读者阅读一下 &lt;code>rebase&lt;/code> 的帮助页面. 其中有几个可以帮助你理解这个强
大命令的能力到底应该被如果使用的优秀例子. 为了让读者至少对这个工具到底有多屌有
一点点的认识, 我们来看一下下面这个例子:&lt;/p>
&lt;p>&lt;img alt="Rebasing Multiple BranchesPart 1" src="https://jwiegley.github.io/git-from-the-bottom-up/images/rebasing-branches-1.png" />&lt;/p>
&lt;p>如果你打算将分支 &lt;code>L&lt;/code> 迁移到分支 &lt;code>Z&lt;/code> 上, 并且使得 commit &lt;code>L&lt;/code> 是分支 &lt;code>Z&lt;/code> 的新
&lt;code>HEAD&lt;/code>, 你会怎么做呢?&lt;/p>
&lt;blockquote>
&lt;p>The picture reads: we have our main-line of development, &lt;code>D&lt;/code>, which three
commits ago was branched to begin speculative development on &lt;code>Z&lt;/code>. At some
point in the middle of all this, back when &lt;code>C&lt;/code> and &lt;code>X&lt;/code> were the heads of their
respective branches, we decided to begin another speculation which finally
produced &lt;code>L&lt;/code>. Now we’ve found that &lt;code>L&lt;/code>’s code is good, but not quite good
enough to merge back over to the main-line, so we decide to move those changes
over to the development branch &lt;code>Z&lt;/code>, making it look as though we’d done them
all on one branch after all. Oh, and while we’re at it, we want to edit &lt;code>J&lt;/code>
real quick to change the copyright date, since we forgot it was 2008 when we
made the change! Here are the commands needed to untangle this knot:&lt;/p>
&lt;/blockquote>
&lt;p>这个图实际上在说: 我们主要的开发发生在分支 &lt;code>D&lt;/code> 上, 而这个分支 &lt;code>D&lt;/code> 在三个 commit
之前, 分支出了一个 &lt;code>Z&lt;/code> 来进行试探性质的开发. 在 &lt;code>C&lt;/code> 和 &lt;code>X&lt;/code> 还是分支的 &lt;code>HEAD&lt;/code> 的
时候, 有人尝试合并了两个分支, 进行了新一轮的试探性开发, 最终产出了分支 &lt;code>L&lt;/code>. 那
么现在我们知道了 commit &lt;code>L&lt;/code> 的代码是很优秀的, 但是还没有优秀到足以合并回主分支
的程度, 我们为了进一步改进这个代码, 希望把 &lt;code>L&lt;/code> 上的更改移动到分支 &lt;code>Z&lt;/code> 上, 然后
调整得足够好了以后, 再合并回主分支. 顺便, 我们在做这一切的同时, 还希望编辑一下
commit &lt;code>J&lt;/code> 来简单的变更一下版权信息的日期, 因为当初提交 commit &lt;code>J&lt;/code> 的时候忘了改
它. 那么以下是解决以上这些麻烦的问题所需要的命令:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git checkout L
$ git rebase -i Z
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>After resolving whatever conflicts emerge, I now have this repository:&lt;/p>
&lt;/blockquote>
&lt;p>处理完冲突以后, 我的 repository 看起来像下图这样:&lt;/p>
&lt;p>&lt;img alt="Rebasing Multiple BranchesPart 2" src="https://jwiegley.github.io/git-from-the-bottom-up/images/rebasing-branches-2.png" />&lt;/p>
&lt;blockquote>
&lt;p>As you can see, when it comes to local development, rebasing gives you
unlimited control over how your commits appear in the repository.&lt;/p>
&lt;/blockquote>
&lt;p>就像你看到的那样, 如果只是本地开发, rebase 让你几乎有了对 commit 完全的控制能力,
你想让他们是什么样子就可以是什么样子.&lt;/p>
&lt;h2 id="2-the-index">2 The Index
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#2-the-index">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="21-the-index-meet-the-middle-man--the-index--中间人">2.1 The Index: Meet the middle man | The Index : 中间人
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#21-the-index-meet-the-middle-man--the-index--%e4%b8%ad%e9%97%b4%e4%ba%ba">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>Between your data files, which are stored on the filesystem, and your Git
blobs, which are stored in the repository, there stands a somewhat strange
entity: the Git index. Part of what makes this beast hard to understand is
that it’s got a rather unfortunate name. It’s an index in the sense that it
refers to the set of newly created trees and blobs which you created by
running add. These new objects will soon get bound into a new tree for the
purpose of committing to your repository — but until then, they are only
referenced by the index. That means that if you unregister a change from the
index with reset, you’ll end up with an orphaned blob that will get deleted
at some point at the future.&lt;/p>
&lt;/blockquote>
&lt;p>在你存储在文件系统中的数据文件, 与 Git 中存储的 blob 之间还存在着一个奇怪的媒介,
我们称之为 the Git index. 这个令人困惑的名字使得它很难被人理解. 从某种意义上来
讲, 它确实是一种索引: 它引用一个由新添加的 tree&lt;sup id="fnref:11">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:11" role="doc-noteref">11&lt;/a>&lt;/sup> 和 blob 组成的集合, 这个
集合是用户通过运行 add 命令创建的. 在这些对象真正被添加到一个 tree, 并且最终成
为一个 commit 加入到你的 repository 中之前, 这些新的对象, 它们仅仅只被 the
index 引用而已. 这意味着如果你通过 reset 命令将一个被记录到 the index 中的更改撤
销掉的话, 那么你原本新创建的 blob 会成为没有人引用的孤儿, 这种 blob 在未来的某
个时间点会被删除.&lt;/p>
&lt;blockquote>
&lt;p>The index is really just a staging area for your next commit, and there’s a
good reason why it exists: it supports a model of development that may be
foreign to users of CVS or Subversion, but which is all too familiar to Darcs
users: the ability to build up your next commit in stages.&lt;/p>
&lt;/blockquote>
&lt;p>The index 实际上起到的作用只是一个为你的下一个 commit 而设立的暂存区罢了. 它使得
你有了在一个暂存区里搭建你的下一个 commit 的能力, 这种开发方式对于 Darcs 的用户
来说相对熟悉, 而对于 CVS 或者是 Subversion 的用户来说这可能会很陌生.&lt;/p>
&lt;p>&lt;img alt="TheIndex" src="https://jwiegley.github.io/git-from-the-bottom-up/images/the-index.png" />&lt;/p>
&lt;blockquote>
&lt;p>First, let me say that there is a way to ignore the index almost entirely: by
passing the &lt;code>-a&lt;/code> flag to commit. Look at the way Subversion works, for
example. When you type &lt;code>svn status&lt;/code>, what you’ll see is a list of actions to
be applied to your repository on the next call to &lt;code>svn commit&lt;/code>. In a way, this
“list of next actions” is a kind of informal index, determined by comparing
the state of your working tree with the state of HEAD. If the file &lt;code>foo.c&lt;/code> has
been changed, on your next commit those changes will be saved. If an unknown
file has a question mark next to it, it will be ignored; but a new file which
has been added with &lt;code>svn add&lt;/code> will get added to the repository.&lt;/p>
&lt;/blockquote>
&lt;p>首先, 实际上是有一种办法能几乎忽略掉 the index 的方法的: 只要在使用 commit 命令
的时候传递 &lt;code>-a&lt;/code> 这个参数, 就可以了. 我们以 Subversion 的工作方式为例. 我们输入
&lt;code>svn status&lt;/code>, 这个命令的输出是一个关于你的下一个 &lt;code>svn commit&lt;/code> 命令将对于你的
repository 做出怎样的更改的列表. 在这个例子中, 那个列表可以看作是 Git 中的 the
index, 毕竟这个通过比较你当前 working tree 和 HEAD 之间的状态差异而得到的列表,
从某种程度上来说是一种信息的索引. 比方说如果文件 &lt;code>foo.c&lt;/code> 被更改了, 那么就会出现
在这个列表中, 这意味着你的下一个 commit 会将这个更改写入你的 repository 中. 如
果一个文件的后面有一个 &lt;code>?&lt;/code>, 那么意味着这个文件将被版本管理系统忽略; 但是如果通
过 &lt;code>snv add&lt;/code> 将文件添加进管理, 那么这个文件将被加入到 repository 中.&lt;/p>
&lt;blockquote>
&lt;p>This is no different from what happens if you use &lt;code>commit -a&lt;/code>: new, unknown
files are ignored, but new files which have been added with add are added to
the repository, as are any changes to existing files. This interaction is
nearly identical with the Subversion way of doing things.&lt;/p>
&lt;/blockquote>
&lt;p>实际上当你在 Git 中使用 &lt;code>commit -a&lt;/code> 命令的时候, 发生的事情几乎和上面描述的一致:
新建的文件需要使用 add 手动添加, 否则就像被忽略的文件一样, 并不会被添加进
repository. 而以前添加过的文件中的更改则会被加入 repository. 这个交互方式几乎和
Subversion 没有什么区别.&lt;/p>
&lt;blockquote>
&lt;p>The real difference is that in the Subversion case, your “list of next
actions” is always determined by looking at the current working tree. In Git,
the “list of next actions” &lt;em>is&lt;/em> the contents of the index, which represents
what will become the next state of HEAD, and that you can manipulate directly
before executing &lt;code>commit&lt;/code>. This gives you an extra layer of control over what
’s going to happen, by allowing you to stage those changes in advance.&lt;/p>
&lt;/blockquote>
&lt;p>这两者之间的区别实际上在于, 在 Subversion 中, 那个列表总是通过检查你的当前工作区
来列出的. 而在 Git 中, 这个列表的功能, 由 the index 中记录的内容来承担. 这个
the index 中指示着 HEAD 这个 commit 接下来会是什么样的状态, 而你可以在 commit 之
前直接对 the index 做出调整. 这实际上让你对更改的控制能力变得更强了.&lt;/p>
&lt;blockquote>
&lt;p>If this isn’t clear yet, consider the following example: you have a trusty
source file, &lt;code>foo.c&lt;/code>, and you’ve made two sets of unrelated changes to it.
What you’d like to do is to tease apart these changes into two different
commits, each with its own description. Here’s how you’d do this in
Subversion:&lt;/p>
&lt;/blockquote>
&lt;p>如果上面这种说法还不够明确. 那么我们可以考虑一下下面这个例子: 你有一个可以信任的
源文件, 它是 &lt;code>foo.c&lt;/code>, 你还有对他做出了两组没什么关联的更改. 你现在想做的事情是
将这两个更改分成两个不同的 commit 进行提交, 每个 commit 都有它自己的说明. 那么
你在 Subversion 中应该这么做:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ svn diff foo.c &amp;gt; foo.patch
$ vi foo.patch
&amp;lt;edit foo.patch, keeping the changes I want to commit later&amp;gt;
$ patch -p1 -R &amp;lt; foo.patch &lt;span style="color:#75715e"># remove the second set of changes&lt;/span>
$ svn commit -m &lt;span style="color:#e6db74">"First commit message"&lt;/span>
$ patch -p1 &amp;lt; foo.patch &lt;span style="color:#75715e"># re-apply the remaining changes&lt;/span>
$ svn commit -m &lt;span style="color:#e6db74">"Second commit message"&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Sounds like fun? Now repeat that many times over for a complex, dynamic set of
changes. Here’s the Git version, making use of the index:&lt;/p>
&lt;/blockquote>
&lt;p>听起来不错? 但是如果你如果用对付的是更复杂的情况呢? 下面是 Git 中的做法, 让我们
利用 the index:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git add --patch foo.c
&amp;lt;&lt;span style="color:#66d9ef">select&lt;/span> the hunks I want to commit first&amp;gt;
$ git commit -m &lt;span style="color:#e6db74">"First commit message"&lt;/span>
$ git add foo.c &lt;span style="color:#75715e"># add the remaining changes&lt;/span>
$ git commit -m &lt;span style="color:#e6db74">"Second commit message"&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>What’s more, it gets even easier! If you like Emacs, the superlative tool
&lt;code>gitsum.el&lt;/code>, by Christian Neukirchan, puts a beautiful face on this
potentially tedious process. I recently used it to tease apart 11 separate
commits from a set of conflated changes. Thank&lt;/p>
&lt;/blockquote>
&lt;p>这是不是简单多了? 如果你喜欢 Emacs, 那么还有个工具叫 &lt;code>gitsum.el&lt;/code>, 这个工具是由
Christian Neukirchan 开发的, 给这个可能看起来单调乏味的过程添加了一个漂亮的界面.
我最近实际上用它将一个更改拆分成 11 个独立的 commit 过. 真是谢谢 TA 了.&lt;/p>
&lt;h3 id="22-taking-the-index-further--进一步了解-the-index">2.2 Taking the index further | 进一步了解 the index
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#22-taking-the-index-further--%e8%bf%9b%e4%b8%80%e6%ad%a5%e4%ba%86%e8%a7%a3-the-index">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>Let’s see, the index&amp;hellip; With it you can pre-stage a set of changes, thus
iteratively building up a patch before committing it to the repository. Now,
where have I heard that concept before&amp;hellip;&lt;/p>
&lt;/blockquote>
&lt;p>根据上面所说的,我们可以如此来描述 the index 的功能: 通过它, 我们可以将一系列更改
暂存下来, 还可以在更改提交到 repository 之前, 将一次对分拣的更改分成若干的
patch. 我好像曾经在什么地方听到过这个&amp;hellip;&lt;/p>
&lt;blockquote>
&lt;p>If you’re thinking “Quilt!”, you’re exactly right. In fact, the index is
little different from Quilt, it just adds the restriction of allowing only one
patch to be constructed at a time.&lt;/p>
&lt;/blockquote>
&lt;p>你很可能会想到一个叫 &amp;ldquo;&lt;a href="https://en.wikipedia.org/wiki/Quilt_(software)">Quilt&lt;/a>&amp;rdquo;
的软件. 事实上, the index 和 Quilt 有一点点不同, Quilt 实际上只是添加了一个 &amp;ldquo;一
次只允许对代码做出一个 patch 的更改&amp;rdquo; 的限定而已&lt;sup id="fnref:12">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:12" role="doc-noteref">12&lt;/a>&lt;/sup>.&lt;/p>
&lt;blockquote>
&lt;p>But what if, instead of two sets of changes within &lt;code>foo.c&lt;/code>, I had four? With
plain Git, I’d have to tease each one out, commit it, and then tease out the
next. This is made much easier using the index, but what if I wanted to test
those changes in various combinations with each other before checking them in?
That is, if I labelled the patches A, B, C and D, what if I wanted to test A +
B, then A + C, then A + D, etc., before deciding if any of the changes were
truly complete?&lt;/p>
&lt;/blockquote>
&lt;p>但是如果我在文件 &lt;code>foo.c&lt;/code> 中实际上一共有四组的更改呢? 在原生的 Git 中, 我实际上需
要一个一个的将属于其中一个 patch 的更改挑选出来, commit, 然后再回去选下一组的.
The index 实际上已经大大简化了这个过程, 但是如果我想在提交这些更改前, 对这些更改
的多种组合分别做测试呢? 比方说我有四个 patch, 它们分别是 A, B, C, D. 如果我想以
这种顺序进行测试: A + B, 然后 A + C, 随后 A + D, 以此类推. 那么我应该怎么做呢?&lt;/p>
&lt;blockquote>
&lt;p>There is no mechanism in Git itself that allows you to mix and match parallel
sets of changes on the fly. Sure, multiple branches can let you do parallel
development, and the index lets you stage multiple changes into a series of
commits, but you can’t do both at once: staging a series of patches while at
the same time selectively enabling and disabling some of them, to verify the
integrity of the patches in concert before finally committing them. What you
’d need in order to do something like this would be an index which allows
for greater depth than one commit at a time. This is exactly what Stacked Git
provides.&lt;/p>
&lt;p>Here’s how I’d commit two different patches into my working tree using plain
Git:&lt;/p>
&lt;/blockquote>
&lt;p>在 Git 中并没有这样的功能. 多分支确实可以让你完成平行开发, the index 也确实可以
让你将更改拆成若干个 commit, 但是实际上你并不能同时做这两个事情: 你不能暂存下一
系列 commit 中的更改, 然后去选择其中一些出于启用/关闭的状态, 以此来在提交前验证
更改的正确性.&lt;/p>
&lt;p>你实际上想做的事情是希望你的 the index 中, 可以存下多个 commit&lt;sup id="fnref:13">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:13" role="doc-noteref">13&lt;/a>&lt;/sup>, 这个功能
实际上可以由 Stacked Git 来提供的.&lt;/p>
&lt;p>以下是我 commit 两个不同的更改组到我的 working tree 的过程:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git add -i &lt;span style="color:#75715e"># select first set of changes&lt;/span>
$ git commit -m &lt;span style="color:#e6db74">"First commit message"&lt;/span>
$ git add -i &lt;span style="color:#75715e"># select second set of changes&lt;/span>
$ git commit -m &lt;span style="color:#e6db74">"Second commit message"&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>This works great, but I can’t selectively disable the first commit in order
to test the second one alone. To do that, I’d have to do the following:&lt;/p>
&lt;/blockquote>
&lt;p>这样实际上还不错, 但是我无法在做完这一切之后, 再将第一个 commit 暂时禁用, 来单独
测试第二个 commit. 如果我想做这个事情, 那么我接下来还得做这么一个事情:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git log &lt;span style="color:#75715e"># find the hash id of the first commit&lt;/span>
$ git checkout -b work &amp;lt;first commit’s hash id&amp;gt;
$ git cherry-pick &amp;lt;second commit’s hash id&amp;gt;
&amp;lt;... run tests ...&amp;gt;
$ git checkout master &lt;span style="color:#75715e"># go back to the master "branch"&lt;/span>
$ git branch -D work &lt;span style="color:#75715e"># remove my temporary branch&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Surely there has to be a better way! With &lt;code>stg&lt;/code> I can queue up both patches
and then re-apply them in whatever order I like, for independent or combined
testing, etc. Here’s how I’d queue the same two patches from the previous
example, using &lt;code>stg&lt;/code>:&lt;/p>
&lt;/blockquote>
&lt;p>但是应该会有更好的方法. 使用 &lt;code>stg&lt;/code> 命令, 我可以对两个 patch 进行任意顺序的排序,
然后按这个顺序来应用它. 这样就可以进行独立测试/组合测试了. 以下是一个例子:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ stg new patch1
$ git add -i &lt;span style="color:#75715e"># select first set of changes&lt;/span>
$ stg refresh --index
$ stg new patch2
$ git add -i &lt;span style="color:#75715e"># select second set of changes&lt;/span>
$ stg refresh --index
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Now if I want to selectively disable the first patch to test only the second,
it’s very straightforward:&lt;/p>
&lt;/blockquote>
&lt;p>那么完成上面说的事情就非常简单了:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ stg applied
patch1
patch2
&amp;lt;... &lt;span style="color:#66d9ef">do&lt;/span> tests using both patches ...&amp;gt;
$ stg pop patch1
&amp;lt;... &lt;span style="color:#66d9ef">do&lt;/span> tests using only patch2 ...&amp;gt;
$ stg pop patch2
$ stg push patch1
&amp;lt;... &lt;span style="color:#66d9ef">do&lt;/span> tests using only patch1 ...&amp;gt;
$ stg push -a
$ stg commit -a &lt;span style="color:#75715e"># commit all the patches&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>This is definitely easier than creating temporary branches and using
&lt;code>cherry-pick&lt;/code> to apply specific commit ids, followed by deleting the temporary
branch.&lt;/p>
&lt;/blockquote>
&lt;p>这比上面提到的建立临时分支并使用 &lt;code>cherry-pick&lt;/code> 来应用特别的 commit 哈希值, 后面
再把他删掉的方式舒服多了.&lt;/p>
&lt;h2 id="3-reset">3 Reset
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#3-reset">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="31-to-reset-or-not-to-reset--要不要-reset-这是一个问题">3.1 To reset, or not to reset | 要不要 reset, 这是一个问题
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#31-to-reset-or-not-to-reset--%e8%a6%81%e4%b8%8d%e8%a6%81-reset-%e8%bf%99%e6%98%af%e4%b8%80%e4%b8%aa%e9%97%ae%e9%a2%98">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>One of the more difficult commands to master in Git is &lt;code>reset&lt;/code>, which seems to
bite people more often than other commands. Which is understandable, giving
that it has the potential to change both your working tree and your current
HEAD reference. So I thought a quick review of this command would be useful.&lt;/p>
&lt;/blockquote>
&lt;p>在 Git 中比较麻烦的命令之一就是 &lt;code>reset&lt;/code>, 大家似乎都更容易在这个命令上遇到问题.
看起来这个命令由于同时更改了你的 working tree 和你当前的 HEAD 指针, 所以会显得
难以理解. 所以我这里需要快速提一下这个命令的作用.&lt;/p>
&lt;blockquote>
&lt;p>Basically, &lt;code>reset&lt;/code> is a reference editor, an index editor, and a working tree
editor. This is partly what makes it so confusing, because it’s capable of
doing so many jobs. Let’s examine the difference between these three modes,
and how they fit into the Git commit model.&lt;/p>
&lt;/blockquote>
&lt;p>简单来说, &lt;code>reset&lt;/code> 是一个能编辑 HEAD / index / working tree 的工具. 这可以部分解
释它为什么看起来这么难懂, 因为它实际上确实一下子完成了很多很多工作. 我们会在下
面慢慢解释上面提到的三种模式之间的区别, 以及它们在我们前文建立的, 从 commit 的
角度理解 Git 的模型中, 到底是如何工作的.&lt;/p>
&lt;h3 id="32-doing-a-mixed-reset--应用一个-mixed-reset">3.2 Doing a mixed reset | 应用一个 mixed reset
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#32-doing-a-mixed-reset--%e5%ba%94%e7%94%a8%e4%b8%80%e4%b8%aa-mixed-reset">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>If you use the &lt;code>--mixed&lt;/code> option (or no option at all, as this is the default),
reset will revert parts of your index along with your HEAD reference to match
the given commit. The main difference from &lt;code>--soft&lt;/code> is that &lt;code>--soft&lt;/code> only
changes the meaning of HEAD and doesn’t touch the index.&lt;/p>
&lt;/blockquote>
&lt;p>如果你使用了 &lt;code>--mixed&lt;/code> 这个选项 (如果你不带参数调用, 那么默认就是它), &lt;code>reset&lt;/code> 命
令会在将你的 HEAD 指针指向给定的那个 commit, 同时调整 index, 使其与 HEAD 匹配.
&lt;code>--soft&lt;/code> 和这个选项的区别在于, &lt;code>--soft&lt;/code> 并没有更改 index.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git add foo.c &lt;span style="color:#75715e"># add changes to the index as a new blob&lt;/span>
$ git reset HEAD &lt;span style="color:#75715e"># delete any changes staged in the index&lt;/span>
$ git add foo.c &lt;span style="color:#75715e"># made a mistake, add it back&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="33-doing-a-soft-reset--应用一个-soft-reset">3.3 Doing a soft reset | 应用一个 soft reset
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#33-doing-a-soft-reset--%e5%ba%94%e7%94%a8%e4%b8%80%e4%b8%aa-soft-reset">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>If you use the &lt;code>--soft&lt;/code> option to &lt;code>reset&lt;/code>, this is the same as simply changing
your HEAD reference to a different commit. Your working tree changes are left
untouched. This means the following two commands are equivalent:&lt;/p>
&lt;/blockquote>
&lt;p>如果你加上了 &lt;code>--soft&lt;/code> 选项, 那么这个 &lt;code>reset&lt;/code> 命令就非常的简单易懂了: 实际上和你
直接把你的 &lt;code>HEAD&lt;/code> 指针指向了另一个不同的 commit 没什么两样. 你的 working tree
中的所有更改都会在执行完 reset 之后原样保留. 这意味着下面写的两条命令是完全等价
的:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git reset --soft HEAD^ &lt;span style="color:#75715e"># backup HEAD to its parent,&lt;/span>
&lt;span style="color:#75715e"># effectively ignoring the last commit&lt;/span>
$ git update-ref HEAD HEAD^ &lt;span style="color:#75715e"># does the same thing, albeit manually&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>In both cases, your working tree now sits on top of an older HEAD, so you
should see more changes if you run &lt;code>status&lt;/code>. It’s not that your files have
been changed, simply that they are now being compared against an older
version. It can give you a chance to create a new commit in place of the old
one. In fact, if the commit you want to change is the most recent one checked
in, you can use &lt;code>commit --amend&lt;/code> to add your latest changes to the last commit
as if you’d done them together.&lt;/p>
&lt;/blockquote>
&lt;p>在上述的两种情况里, 运行完 &lt;code>reset&lt;/code> 之后, 你的 working tree 中的文件内容是领先于
你的 HEAD 的, 所以如果你运行 &lt;code>status&lt;/code> 命令来查看变更的情况, 你会看到的实际上是
你的 working tree 和一个更老的 HEAD 的差别. 这使得你可以在老的 HEAD 上重新建立
一个新的 commit, 事实上 &lt;code>commit --amend&lt;/code> 这个命令做的事情就是类似于这样的.&lt;/p>
&lt;blockquote>
&lt;p>But please note: if you have downstream consumers, and they’ve done work on
top of your previous head — the one you threw away — changing HEAD like this
will force a merge to happen automatically after their next pull. Below is
what your tree would look like after a soft reset and a new commit:&lt;/p>
&lt;/blockquote>
&lt;p>但是请注意一下: 如果你有下游用户, 而他们使用了被你 &lt;code>reset&lt;/code> + &lt;code>commit&lt;/code> 之后丢弃掉
的那个 &lt;code>commit&lt;/code> 的话, 那么像这样去更改你的 HEAD, 将会在他们的下一个 pull 时导致
一个自动出现的强制 merge. 下图是你的 repository 在执行完 soft reset 之后又提交
了一个新的 commit 之后的样子:&lt;/p>
&lt;p>&lt;img alt="Soft ResetCommit" src="https://jwiegley.github.io/git-from-the-bottom-up/images/soft-reset-commit.png" />&lt;/p>
&lt;blockquote>
&lt;p>And here’s what your consumer’s HEAD would look like after they pulled
again, with colors to show how the various commits match up:&lt;/p>
&lt;/blockquote>
&lt;p>而下图是你的下游用户下一次 pull 之后的情况:&lt;/p>
&lt;p>&lt;img alt="Soft ResetPull" src="https://jwiegley.github.io/git-from-the-bottom-up/images/soft-reset-pull.png" />&lt;/p>
&lt;p>可以从其中给 commit 上的颜色看出来这些 commit 是如何对应的.&lt;/p>
&lt;h3 id="34-doing-a-hard-reset--应用一个-hard-reset">3.4 Doing a hard reset | 应用一个 hard reset
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#34-doing-a-hard-reset--%e5%ba%94%e7%94%a8%e4%b8%80%e4%b8%aa-hard-reset">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;blockquote>
&lt;p>A hard reset (the &lt;code>--hard&lt;/code> option) has the potential of being very dangerous,
as it’s able to do two different things at once:&lt;/p>
&lt;/blockquote>
&lt;p>hard reset 操作起来有一定的风险, 它能同时完成两件不同的事情:&lt;/p>
&lt;blockquote>
&lt;p>First, if you do a hard reset against your current HEAD, it will erase all
changes in your working tree, so that your current files match the contents of
HEAD. There is also another command, &lt;code>checkout&lt;/code>, which operates just like
&lt;code>reset --hard&lt;/code> if the index is empty. Otherwise, it forces your working tree
to match the index.&lt;/p>
&lt;/blockquote>
&lt;p>首先, 如果你对你当前的 HEAD 做 hard reset, 这将从你的 working tree 中抹去所有的
更改, 这样一来你的 working tree 就和你的 HEAD 指向的那个 commit 完全一致了. 如
果你的 index 是空的的话, 其实还有一个命令叫 &lt;code>checkout&lt;/code> 同样能做到这个事情. 如果
你的 index 不空, 那么对某个文件进行 checkout 命令只会使得 working tree 中的文件
和 index 中一致.&lt;/p>
&lt;blockquote>
&lt;p>Now, if you do a hard reset against an earlier commit, it’s the same as first
doing a soft reset and then using reset &lt;code>--hard&lt;/code> to reset your working tree.
Thus, the following commands are equivalent:&lt;/p>
&lt;/blockquote>
&lt;p>如果你对一个之前的 commit 调用 reset 命令, 那么这个事情和先使用 soft reset 之后
再使用 hard reset 来重置你的工作区是完全一致的. 所以说下面的两组命令是等价的:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git reset --hard HEAD~3 &lt;span style="color:#75715e"># Go back in time, throwing away changes&lt;/span>
$ git reset --soft HEAD~3 &lt;span style="color:#75715e"># Set HEAD to point to an earlier commit&lt;/span>
$ git reset --hard &lt;span style="color:#75715e"># Wipe out differences in the working tree&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>As you can see, doing a hard reset can be very destructive. Fortunately, there
is a safer way to achieve the same effect, using the Git stash (see the next
section):&lt;/p>
&lt;/blockquote>
&lt;p>读过以上的内容, 我们可以发现: 一个 hard reset 很可能是具有破环性的. 幸运的是, 我
们实际上有一种更安全的方法来完成同样的事情, 只要使用下一节中提到的 Git stash 命
令就可以了:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git stash
$ git checkout -b new-branch HEAD~3 &lt;span style="color:#75715e"># head back in time!&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>This approach has two distinct advantages if you’re not sure whether you
really want to modify the current branch just now:&lt;/p>
&lt;/blockquote>
&lt;p>当你并不确定你是否要对当前的分支进行修改的时候, 这种方法有两个明显的优点:&lt;/p>
&lt;blockquote>
&lt;ol>
&lt;li>It saves your work in the stash, which you can come back to at any time.
Note that the stash is not branch specific, so you could potentially stash
the state of your tree while on one branch, and later apply the differences
to another.&lt;/li>
&lt;li>It reverts your working tree back to a past state, but on a new branch, so
if you decide to commit your changes against the past state, you won’t
have altered your original branch.&lt;/li>
&lt;/ol>
&lt;/blockquote>
&lt;ol>
&lt;li>这样可以将你的工作保存在 stash 中, 你随时可以再回到你原来的工作状态. 需要注意
的是这个 stash 和分支无关, 你完全可以在某个分支上将你当前的 working tree 存
入, 而随后在另一个分支上取出它.&lt;/li>
&lt;li>这样操作的确将代码回退到了过去的状态, 但是是在一个新的分支上, 如果你决定在过
去的状态上修改, 这并不会影响到你原先那个更快的分支.&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>If you do make changes to &lt;code>new-branch&lt;/code> and then decide you want it to become
your new master branch, run the following commands:&lt;/p>
&lt;/blockquote>
&lt;p>如果你对 new-branch 做出了更改, 然后想让它成为你新的主分支, 可以运行以下的命令来
完成这件事情:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git branch -D master &lt;span style="color:#75715e"># goodbye old master (still in reflog)&lt;/span>
$ git branch -m new-branch master &lt;span style="color:#75715e"># the new-branch is now my master&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>The moral of this story is: although you can do major surgery on your current
branch using &lt;code>reset --soft&lt;/code> and &lt;code>reset --hard&lt;/code> (which changes the working tree
too), why would you want to? Git makes working with branches so easy and
cheap, it’s almost always worth it to do your destructive modifications on a
branch, and then move that branch over to take the place of your old master.
It has an almost Sith-like appeal to it&amp;hellip;&lt;/p>
&lt;/blockquote>
&lt;p>以上的事情是在告诉你: 尽管你是可以用 reset &amp;ndash;soft 和 reset &amp;ndash;hard 命令来操作你当
前的分支,但是你几乎没有理由必须在你当前的分支上这么做. Git 让分支相关的操作变得
简洁而容易, 所以我们往往都将可能有破坏性的操作放在另一个分支上操作, 完成以后再
移动回来, 这种操作方式几乎在任何情况下都比直接在原分支上修改更为优秀.&lt;sup id="fnref:14">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:14" role="doc-noteref">14&lt;/a>&lt;/sup>&lt;/p>
&lt;blockquote>
&lt;p>And what if you do accidentally run &lt;code>reset --hard&lt;/code>, losing not only your
current changes but also removing commits from your master branch? Well,
unless you’ve gotten into the habit of using stash to take snapshots (see
next section), there’s nothing you can do to recover your lost working tree.
But you can restore your branch to its previous state by again using &lt;code>reset --hard&lt;/code> with the reflog (this will also be explained in the next section):&lt;/p>
&lt;/blockquote>
&lt;p>那么如果你不小心运行了 hard reset, 然后同时丢失了你当前的更改和之前的一部分
commit 有办法弥补么? 除非你已经养成了使用 stash 来建立快照, 否则几乎没有办法来挽
回这种情况对于 working tree 带来的损失, 但是你可以将你的分支回溯到它之前的那个
状态. 只要像下面这样使用 &lt;code>reset --hard&lt;/code> 这个命令就可以了:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git reset --hard HEAD@&lt;span style="color:#f92672">&amp;#123;&lt;/span>1&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#75715e"># restore from reflog before the change&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>下一节我们来解释上面这条命令的意思.&lt;/p>
&lt;blockquote>
&lt;p>To be on the safe side, never use &lt;code>reset --hard&lt;/code> without first running
&lt;code>stash&lt;/code>. It will save you many white hairs later on. If you did run stash, you
can now use it to recover your working tree changes as well:&lt;/p>
&lt;/blockquote>
&lt;p>为了安全起见, 我们不应该在运行 &lt;code>stash&lt;/code> 之前使用 &lt;code>reset --hard&lt;/code>. 这个习惯将让你少
掉几根头发. 如果你跑过 &lt;code>stash&lt;/code>, 你实际上就可以使用它来还原你的 working tree:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git stash &lt;span style="color:#75715e"># because it's always a good thing to do&lt;/span>
$ git reset --hard HEAD~3 &lt;span style="color:#75715e"># go back in time&lt;/span>
$ git reset --hard HEAD@&lt;span style="color:#f92672">&amp;#123;&lt;/span>1&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#75715e"># oops, that was a mistake, undo it!&lt;/span>
$ git stash apply &lt;span style="color:#75715e"># and bring back my working tree changes&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="4-stashing-and-the-reflog--stash-和-reflog">4 Stashing and the reflog | Stash 和 reflog
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#4-stashing-and-the-reflog--stash-%e5%92%8c-reflog">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;blockquote>
&lt;p>Until now we’ve described two ways in which blobs find their way into Git:
first they’re created in your index, both without a parent tree and without
an owning commit; and then they’re committed into the repository, where they
live as leaves hanging off of the tree held by that commit. But there are two
other ways a blob can dwell in your repository.&lt;/p>
&lt;/blockquote>
&lt;p>直到现在我们描述过两种在 Git 中找到某个 blob 的方法:&lt;/p>
&lt;ol>
&lt;li>当 blob 在你的 index 中被创建时, 此时还没有一个 tree 在引用它们, 它们也没有被
某个 commit 所拥有, 这时我们可以通过 index 来找到它;&lt;/li>
&lt;li>随后它们被提交给你的 repository, 之后 blob 就成为了 被某个 commit 管理的 tree
上的叶节点.&lt;/li>
&lt;/ol>
&lt;p>但是实际上在 Git 中还有两种 blob 存在的形式.&lt;/p>
&lt;blockquote>
&lt;p>The first of these is the Git &lt;code>reflog&lt;/code>, a kind of meta-repository that records
— in the form of commits — every change you make to your repository. This
means that when you create a tree from your index and store it under a commit
(all of which is done by &lt;code>commit&lt;/code>), you are also inadvertently adding that
commit to the reflog, which can be viewed using the following command:&lt;/p>
&lt;/blockquote>
&lt;p>第一种就是 Git &lt;code>reflog&lt;/code>, 这是一种以 commit 的方式记录了所有你对你的 repository
做出的更改的 meta-repository. 这意味着当你从一个 index 建立一个 tree, 并且将它
存在一个 commit 中的时候, 你实际上无意之中也将这个 commit 添加到了 the reflog
中. 你可以通过以下的命令查看 the reflog:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git reflog
5f1bc85... HEAD@&lt;span style="color:#f92672">&amp;#123;&lt;/span>0&lt;span style="color:#f92672">}&lt;/span>: commit &lt;span style="color:#f92672">(&lt;/span>initial&lt;span style="color:#f92672">)&lt;/span>: Initial commit
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>The beauty of the reflog is that it persists independently of other changes in
your repository. This means I could unlink the above commit from my repository
(using &lt;code>reset&lt;/code>), yet it would still be referenced by the reflog for another 30
days, protecting it from garbage collection. This gives me a month’s chance
to recover the commit should I discover I really need it.&lt;/p>
&lt;/blockquote>
&lt;p>the reflog 的妙处就在于它与其他你对你的 repository 做出的更改是独立的. 这意味着
当我将上面的命令展示的那个 commit 从我的 repository 中解除链接的时候 (比方说我
跑了一个 &lt;code>reset&lt;/code>), 这个 commit 仍然被这个 meta-repository, 也就是 the reflog 所
引用, 直到 30 天以后, 这个 commit 才会进入垃圾回收系统的考虑范围. 这使得你有一
个月的时间来找回你需要的 commit.&lt;/p>
&lt;blockquote>
&lt;p>The other place blobs can exist, albeit indirectly, is in your working tree
itself. What I mean is, say you’ve changed a file &lt;code>foo.c&lt;/code> but you haven’t
added those changes to the index yet. Git may not have created a blob for you,
but those changes do exist, meaning the content exists — it just lives in
your filesystem instead of Git’s repository. The file even has its own SHA1
hash id, despite the fact no real blob exists. You can view it with this
command:&lt;/p>
&lt;/blockquote>
&lt;p>尽管很显然, 但是实际上还有一个地方是 blob 可以呆的, 那就是你的 working tree. 我
的意思是说, 如果你更改了一个文件, 比如说 &lt;code>foo.c&lt;/code> 但是还没有将这个更改添加到 the
index 中, 那么由于我们说 blob 的本质是数据本身, 所以即使 Git 并没有建立对应的
blob, 数据本身也仍然是存在的, 只不过是存在于你的文件系统中罢了. 这个文件本身实际
上是有 SHA1 值的, 尽管其实并没有实际上的 blob 存在. 我们可以通过下面这条命令来
查看它:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git hash-object foo.c
&amp;lt;some hash id&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>What does this do for you? Well, if you find yourself hacking away on your
working tree and you reach the end of a long day, a good habit to get into is
to stash away your changes:&lt;/p>
&lt;/blockquote>
&lt;p>这意味着啥呢? 如果你在你的 working tree 中到处乱改, 然后经过了漫长的一整天工作,
将所有的更改都用 stash 存起来会是一个好习惯:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git stash
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>This takes all your directory’s contents — including both your working tree,
and the state of the index — and creates blobs for them in the git
repository, a tree to hold those blobs, and a pair of stash commits to hold
the working tree and index and record the time when you did the stash.&lt;/p>
&lt;/blockquote>
&lt;p>这个命令将你的那个目录中的所有内容 — 包括你的 working tree, 甚至是 the index 的
状态 — 都保存了下来, 并且为它们在 Git repository 中创建了 blob, 以及一个 tree
来管理这些 blob, 还有一对 stash commit 来管理你的 working tree 和 index 还得记
录下你执行 stash 命令的时间.&lt;/p>
&lt;blockquote>
&lt;p>This is a good practice because, although the next day you’ll just pull your
changes back out of the stash with &lt;code>stash apply&lt;/code>, you’ll have a reflog of all
your stashed changes at the end of every day. Here’s what you’d do after
coming back to work the next morning (WIP here stands for “Work in progress
”):&lt;/p>
&lt;/blockquote>
&lt;p>尽管你第二天会把所有的更改都用 &lt;code>stash apply&lt;/code> 命令从 stash 中重新拉出来, 但是做完
这件事情之后你相当于在 reflog 中存了一个你每天工作的结果的快照. 第二天你回来工
作的时候需要做的事情如下:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git stash list
stash@&lt;span style="color:#f92672">&amp;#123;&lt;/span>0&lt;span style="color:#f92672">}&lt;/span>: WIP on master: 5f1bc85... Initial commit
$ git reflog show stash &lt;span style="color:#75715e"># same output, plus the stash commit's hash id 2add13e... stash@&amp;#123;0}: WIP on master: 5f1bc85... Initial commit&lt;/span>
$ git stash apply
&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中WIP意味着 &amp;ldquo;Work in progress&amp;rdquo;&lt;/p>
&lt;blockquote>
&lt;p>Because your stashed working tree is stored under a commit, you can work with
it like any other branch — at any time! This means you can view the log, see
when you stashed it, and checkout any of your past working trees from the
moment when you stashed them:&lt;/p>
&lt;/blockquote>
&lt;p>因为你已经把 working tree 存在了一个 commit 里面, 所以你任何时候都实际上可以把他
当作一个分支一样来看待. 这意味着你甚至可以查看日志, 可以看你 stash 的时间, 以及
将你过去存进 stash 的 working tree 重新取出:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git stash list
stash@&lt;span style="color:#f92672">&amp;#123;&lt;/span>0&lt;span style="color:#f92672">}&lt;/span>: WIP on master: 73ab4c1... Initial commit
...
stash@&lt;span style="color:#f92672">&amp;#123;&lt;/span>32&lt;span style="color:#f92672">}&lt;/span>: WIP on master: 5f1bc85... Initial commit
$ git log stash@&lt;span style="color:#f92672">&amp;#123;&lt;/span>32&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#75715e"># when did I do it?&lt;/span>
$ git show stash@&lt;span style="color:#f92672">&amp;#123;&lt;/span>32&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#75715e"># show me what I was working on&lt;/span>
$ git checkout -b temp stash@&lt;span style="color:#f92672">&amp;#123;&lt;/span>32&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#75715e"># let’s see that old working tree!&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>This last command is particularly powerful: behold, I’m now playing around in
an uncommitted working tree from over a month ago. I never even added those
files to the index; I just used the simple expedient of calling &lt;code>stash&lt;/code> before
logging out each day (provided you actually had changes in your working tree
to stash), and used &lt;code>stash apply&lt;/code> when I logged back in.&lt;/p>
&lt;/blockquote>
&lt;p>最后那个命令其实非常的有用: 你看, 我可以把一个月前没有 commit 的 working tree 重
新拿出来玩. 我甚至从来没有把这些文件添加到 the index 中; 只不过是在我每天下班关
机之前运行了一次 &lt;code>stash&lt;/code> 命令而已. 然后第二天上班的时候, 只要 &lt;code>stash apply&lt;/code> 就
行了.&lt;/p>
&lt;blockquote>
&lt;p>If you ever want to clean up your stash list — say to keep only the last 30
days of activity — don’t use &lt;code>stash clear&lt;/code>; use the &lt;code>reflog expire&lt;/code> command
instead:&lt;/p>
&lt;/blockquote>
&lt;p>但是如果你什么时候想清理你的 stash 列表了, 比方说想把 30 天之前的 stash 的历史删
掉&lt;sup id="fnref:15">&lt;a class="footnote-ref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fn:15" role="doc-noteref">15&lt;/a>&lt;/sup>, 那么麻烦不要使用 &lt;code>stash clear&lt;/code> — 那样你将会丢失所有 stash 中的历史;
而应该使用 &lt;code>reflog expire&lt;/code> 命令:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ git stash clear &lt;span style="color:#75715e"># DON'T! You'll lose all that history&lt;/span>
$ git reflog expire --expire&lt;span style="color:#f92672">=&lt;/span>30.days refs/stash
&amp;lt;outputs the stash bundles that&lt;span style="color:#960050;background-color:#1e0010">'&lt;/span>ve been kept&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>The beauty of &lt;code>stash&lt;/code> is that it lets you apply unobtrusive version control to
your working process itself: namely, the various stages of your working tree
from day to day. You can even use &lt;code>stash&lt;/code> on a regular basis if you like, with
something like the following &lt;code>snapshot&lt;/code> script:&lt;/p>
&lt;/blockquote>
&lt;p>&lt;code>stash&lt;/code> 的妙处就在于让你可以做一些不那么刻意的版本控制, 比如说像上面那样每天保存
一下工作进度. 你愿意的话甚至可以定期的运行 &lt;code>stash&lt;/code>, 比如通过下面这样的脚本:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ cat &lt;span style="color:#e6db74">&amp;lt;&amp;lt;EOF &amp;gt; /usr/local/bin/git-snapshot
&lt;/span>&lt;span style="color:#e6db74">#!/bin/sh
&lt;/span>&lt;span style="color:#e6db74">git stash &amp;amp;&amp;amp; git stash apply
&lt;/span>&lt;span style="color:#e6db74">EOF&lt;/span>
$ chmod +x $_
$ git-snapshot
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>There’s no reason you couldn’t run this from a &lt;code>cron&lt;/code> job every hour, along
with running the &lt;code>reflog expire&lt;/code> command every week or month.&lt;/p>
&lt;/blockquote>
&lt;p>没有什么理由能阻止你使用 &lt;code>cron&lt;/code> 来每个小时跑一次这个命令, 如果担心仓库太大, 完全
可以同时每个月或者每周跑 &lt;code>reflog expire&lt;/code>.&lt;/p>
&lt;h2 id="5-conclusion--结论">5 Conclusion | 结论
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#5-conclusion--%e7%bb%93%e8%ae%ba">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;blockquote>
&lt;p>Over the years I’ve used many version control systems, and many backup
schemes. They all have facilities for retrieving the past contents of a file.
Most of them have ways to show how a file has differed over time. Many permit
you to go back in time, begin a divergent line of reasoning, and then later
bring these new thoughts back to the present. Still fewer offer fine-grained
control over that process, allowing you to collect your thoughts however you
feel best to present your ideas to the public. Git lets you do all these
things, and with relative ease — once you understand its fundamentals.&lt;/p>
&lt;/blockquote>
&lt;p>在过去的几年中我使用过非常多的版本控制系统, 以及很多种的备份模式. 它们都有一系列
工具来追回过去文件的内容. 大部分工具都有展示一个文件随着时间发展如何一步步被更
改的的方式. 很多工具都可以让你穿越时间回到过去, 让你在过去做一些尝试, 然后把产
生的想法重新带回现在. 还是有很小一部分提供了对这个过程的良好的控制机制, 让你可
以良好的操作过去的历史, 可以以任意你喜欢的方式整理好你的想法, 然后将它展示给公
众. Git 可以让你做到上述的一切. 甚至相对来说还是比较容易地做到这一切 — 只要你
真的搞懂了 Git 的原理.&lt;/p>
&lt;blockquote>
&lt;p>It’s not the only system with this kind of power, nor does it always employ
the best interface to its concepts. What it does have, however, is a solid
base to work from. In the future, I imagine many new methods will be devised
to take advantage of the flexibilities Git allows. Most other systems have led
me to believe they’ve reached their conceptual plateau — that all else from
now will be only a slow refinement of what I’ve seen before. Git gives me the
opposite impression, however. I feel we’ve only begun to see the potential
its deceptively simple design promises.&lt;/p>
&lt;/blockquote>
&lt;p>这不是唯一一个有这些能力的系统, 它也没有对于它的理念提供最好的交互. 但是 Git 拥
有的这些东西, 却的的确确是一个进一步开发的好基础. 在未来, 我可以预想存在着很多
更好的工具被发明出来, 它们能更好地利用 Git 类似的构造提供的这种种便利. 其他的大
部分系统都让我相信它们到达了他们的瓶颈期 — 它们要不然就像我之前见到的那样在慢
慢慢慢地进行改进, 要不然将来也会进入这样的状态. Git 却给了我一个更为乐观的印象.
我感觉我们即将见识到它出乎人意料的简单的涉及理念中爆发出来的潜力.&lt;/p>
&lt;h2 id="6-further-reading--阅读更多">6 Further reading | 阅读更多
&lt;span>
&lt;a href="https://blog.deepin.org/posts/git-from-the-bottom-up/#6-further-reading--%e9%98%85%e8%af%bb%e6%9b%b4%e5%a4%9a">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;blockquote>
&lt;p>If your interest to learn Git more has been piqued, please check out the
following articles:&lt;/p>
&lt;/blockquote>
&lt;p>如果你还想学一些 Git 可以看看下面这些文章:&lt;/p>
&lt;blockquote>
&lt;ul>
&lt;li>A tour of Git: the basics - &lt;a href="https://cworth.org/hgbook-git/tour/">http://cworth.org/hgbook-git/tour/&lt;/a>&lt;/li>
&lt;li>Manage source code using Git -
&lt;a href="https://www.ibm.com/developerworks/linux/library/l-git/">http://www.ibm.com/developerworks/linux/library/l-git/&lt;/a>&lt;/li>
&lt;li>A tutorial introduction to git - &lt;a href="https://git-scm.com/docs/gittutorial">http://git-scm.com/docs/gittutorial&lt;/a>&lt;/li>
&lt;li>GitFaq — GitWiki - &lt;a href="https://git.wiki.kernel.org/index.php/GitFaq">https://git.wiki.kernel.org/index.php/GitFaq&lt;/a>&lt;/li>
&lt;li>A git core tutorial for developers -
&lt;a href="https://www.kernel.org/pub/software/scm/git/docs/gitcore-tutorial.html">http://www.kernel.org/pub/software/scm/git/docs/gitcore-tutorial.html&lt;/a>&lt;/li>
&lt;li>git for the confused -
&lt;a href="https://www.gelato.unsw.edu.au/archives/git/0512/13748.html">http://www.gelato.unsw.edu.au/archives/git/0512/13748.html&lt;/a>&lt;/li>
&lt;li>The thing About Git - &lt;a href="https://tomayko.com/writings/the-thing-about-git">http://tomayko.com/writings/the-thing-about-git&lt;/a>&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;section class="footnotes" role="doc-endnotes">
&lt;hr />
&lt;ol>
&lt;li id="fn:1" role="doc-endnote">
&lt;p>译者注: 这个描述貌似和上面对 working tree 的描述有冲突, 上面对 working tree
的说法是 working tree 包含了 &lt;code>.git&lt;/code> 子目录,但是出于译者对 commit 的认识而
言, commit 中应该没有 &lt;code>.git&lt;/code> 中的相关信息. 这里可以尝试理解为文中说的
working tree 是类似工作区的概念, 并不包括 &lt;code>.git&lt;/code> 子目录.&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:1" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2" role="doc-endnote">
&lt;p>译者注: 这个地方括号中的内容不知道怎么翻译, 可以理解为, &amp;ldquo;我们还有很多关于
commit 的东西马上就要谈到&amp;rdquo;, 但是感觉和上下文不搭.&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:2" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3" role="doc-endnote">
&lt;p>译者注: 有理由相信 github 可以用这个方式来省钱, 这样 fork 出来的 repository
就不会那么占空间了.&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:3" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4" role="doc-endnote">
&lt;p>译者注: 这个地方我皮了一下, 但是实际上我并不确定原文是不是这个意思.&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:4" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:5" role="doc-endnote">
&lt;p>译者注: 最后这句话的翻译不确定是否正确.&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:5" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:6" role="doc-endnote">
&lt;p>译者注: 这个时候真的还没有 &amp;ldquo;master&amp;rdquo; 这个分支么? 为什么可以说是 create 呢?&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:6" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:7" role="doc-endnote">
&lt;p>译者注: 这里的 &amp;ldquo;first&amp;rdquo; 是按提交 commit 的时间排序的么?&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:7" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:8" role="doc-endnote">
&lt;p>译者注: 毕竟你可以将 &amp;ldquo;name^&amp;rdquo; 看作是一个 &amp;ldquo;name&amp;rdquo; 嘛.&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:8" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:9" role="doc-endnote">
&lt;p>译者注: 实际上这些内容可以在命令 &lt;code>git help show-branch&lt;/code> 中看到.&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:9" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:10" role="doc-endnote">
&lt;p>译者注: 虽然实际上并不是这样, 因为每个 commit 中的 tree 都只是一个快照而已.&lt;/p>
&lt;p>另外上面这段话我个人觉得可以这样理解:&lt;/p>
&lt;p>commit 是一个 object, 在 Git 中一个 object 的名字是由它的 SHA1 值来决定的,
而 SHA1 值是由内容决定的. 那么当我们说, 一个 commit 被改变的时候, 我们实际
上想表达的意思是, 这个 commit 的 SHA1 值发生了变化, 也就是 commit 的内容发
生了变化.&lt;/p>
&lt;p>根据前文的说法我们知道, 一个 commit 中, 是包含有它的父 commit 的引用的, 如果
一个 commit 的父 commit 被用 &lt;code>rebase&lt;/code> 指令改掉了, 那么它本身的 SHA1 值是会
发生变化的, 而它本身发生了变化之后, 所有直接或者间接引用了它的其他 commit
的值也都会因为这样的理由而发生变化.&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:10" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:11" role="doc-endnote">
&lt;p>译者注: 这里的 tree 指的是被新 commit 直接管理的那个 tree 的 sub-tree.&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:11" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:12" role="doc-endnote">
&lt;p>译者注: 这里我并不确定是不是这个意思, 因为译者并没有使用过 Quilt, 这句是根据
维基猜的, 主要是前面那个 it 不知道指的是谁.&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:12" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:13" role="doc-endnote">
&lt;p>译者注: 这里的意思应该是, 如果你的暂存区里有多个 commit, 那么你就可以把他们
以任意的组合加到 repository 中, 然后就可以实现上面说的那个事情了.&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:13" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:14" role="doc-endnote">
&lt;p>译者注: 最后一句貌似是个星战梗, 不知道啥意思. Sith 是西斯, 星战的反派.&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:14" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:15" role="doc-endnote">
&lt;p>译者注: 这里按前文的说法, stash 的历史也一样是由 reflog 保存的, 那么 reflog
本身只会保存 30 天历史, 所以这里手动清理应该没什么意义吧, 除非只是希望手动
完成一下垃圾回收器完成的事情?&amp;#160;&lt;a class="footnote-backref" href="https://blog.deepin.org/posts/git-from-the-bottom-up/#fnref:15" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/section></description><author>deepin 社区官方博客</author></item><item><title>Starting sessions with systemd part 2</title><link>https://blog.deepin.org/posts/starting-sessions-with-systemd-part-2/</link><pubDate>Wed, 29 Dec 2021 21:09:07 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/starting-sessions-with-systemd-part-2/</guid><description>&lt;p>接着上一篇文章，这篇文章主要讲解一下具体的实现。&lt;/p>
&lt;p>&lt;a href="https://github.com/linuxdeepin/dde-session">https://github.com/linuxdeepin/dde-session&lt;/a> 已经包含了所有的文件和提交。&lt;/p>
&lt;p>上面文章说过，为了让 dde 使用 systemd &amp;ndash;user 来关系服务，我提供了一组服务：&lt;/p>
&lt;pre>&lt;code>dde-session-initialized.target
dde-session-manager.service
dde-session-manager.target
dde-session-pre.target
dde-session-restart-dbus.service
dde-session-shutdown.service
dde-session-shutdown.target
dde-session.target
dde-session-x11-services-ready.target
dde-session-x11-services.target
dde-session-x11.target
org.deepin.Session.service
&lt;/code>&lt;/pre>&lt;p>lightdm 在认证通过以后，会启动 dde-session 进程，dde-session 会通过 systemd 的 dbus 启动 &lt;code>org.deepin.Session.service&lt;/code>。&lt;/p>
&lt;p>在 &lt;code>org.deepin.Session.service&lt;/code> 中会执行 &lt;code>dde-session-ctl --systemd-service&lt;/code>，启动 &lt;code>dde-session-x11.target&lt;/code>.&lt;/p>
&lt;p>&lt;code>dde-session-x11.target&lt;/code> 关联了所有初始化的服务，systemd 会帮助自动运行这些服务，并且最终会运行到 &lt;code>dde-session-manager.service&lt;/code> 上。&lt;/p>
&lt;blockquote>
&lt;p>dde-session-manager.service&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ini" data-lang="ini">&lt;span style="color:#66d9ef">[Service]&lt;/span>
&lt;span style="color:#a6e22e">Type&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">simple&lt;/span>
&lt;span style="color:#a6e22e">ExecStart&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">/usr/bin/startdde&lt;/span>
&lt;span style="color:#a6e22e">ExecStopPost&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">/usr/lib/libexec/dde-session-ctl --logout&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 &lt;code>org.deepin.Session.service&lt;/code> 中会启动一个特殊的服务，这个服务会监听 dde-session 的退出，因为总要有一个服务去监听 session 的状态，要确保 session 在服务在，session 退服务退，服务退 session 退，共存亡，这也是所有操作中最麻烦的一步。&lt;/p>
&lt;blockquote>
&lt;p>org.deepin.Session.service&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ini" data-lang="ini">&lt;span style="color:#66d9ef">[Service]&lt;/span>
&lt;span style="color:#a6e22e">Type&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">dbus&lt;/span>
&lt;span style="color:#a6e22e">BusName&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">org.deepin.Session&lt;/span>
&lt;span style="color:#a6e22e">ExecStart&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">/usr/bin/dde-session --systemd-service&lt;/span>
&lt;span style="color:#a6e22e">ExecStop&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">/usr/lib/libexec/dde-session-ctl --shutdown&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 dde-session-ctl 中是这么实现 shutdown 的:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-c++" data-lang="c++">&lt;span style="color:#66d9ef">if&lt;/span> (isShutdown) &amp;#123;
&lt;span style="color:#75715e">// kill startdde-session or call login1
&lt;/span>&lt;span style="color:#75715e">&lt;/span> QDBusInterface &lt;span style="color:#a6e22e">systemd&lt;/span>(&lt;span style="color:#e6db74">"org.freedesktop.systemd1"&lt;/span>, &lt;span style="color:#e6db74">"/org/freedesktop/systemd1"&lt;/span>, &lt;span style="color:#e6db74">"org.freedesktop.systemd1.Manager"&lt;/span>);
qInfo() &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> systemd.call(&lt;span style="color:#e6db74">"StartUnit"&lt;/span>, &lt;span style="color:#e6db74">"dde-session-shutdown.target"&lt;/span>, &lt;span style="color:#e6db74">"replace-irreversibly"&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 dde-session-ctl 中是这么实现 logout 的:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#66d9ef">if&lt;/span> (parser.isSet(logout)) &amp;#123;
org&lt;span style="color:#f92672">::&lt;/span>deepin&lt;span style="color:#f92672">::&lt;/span>Session session(&lt;span style="color:#e6db74">"org.deepin.Session"&lt;/span>, &lt;span style="color:#e6db74">"/org/deepin/Session"&lt;/span>, QDBusConnection&lt;span style="color:#f92672">::&lt;/span>sessionBus());
session.Logout();
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>dde-session main.cpp&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-c++" data-lang="c++">&lt;span style="color:#75715e">// 添加启动参数，表示这是通过 systemd 启动的。
&lt;/span>&lt;span style="color:#75715e">&lt;/span>QCommandLineOption &lt;span style="color:#a6e22e">systemd&lt;/span>(QStringList&amp;#123;&lt;span style="color:#e6db74">"d"&lt;/span>, &lt;span style="color:#e6db74">"systemd-service"&lt;/span>, &lt;span style="color:#e6db74">"wait for systemd services"&lt;/span>});
parser.addOption(systemd);
parser.process(app);
&lt;span style="color:#66d9ef">if&lt;/span> (parser.isSet(systemd)) &amp;#123;
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>;
}
QDBusServiceWatcher &lt;span style="color:#f92672">*&lt;/span>watcher &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> QDBusServiceWatcher(&lt;span style="color:#e6db74">"org.deepin.Session"&lt;/span>, QDBusConnection&lt;span style="color:#f92672">::&lt;/span>sessionBus(), QDBusServiceWatcher&lt;span style="color:#f92672">::&lt;/span>WatchForUnregistration);
watcher&lt;span style="color:#f92672">-&amp;gt;&lt;/span>connect(watcher, &lt;span style="color:#f92672">&amp;amp;&lt;/span>QDBusServiceWatcher&lt;span style="color:#f92672">::&lt;/span>serviceUnregistered, [&lt;span style="color:#f92672">=&lt;/span>] &amp;#123;
QDBusInterface &lt;span style="color:#a6e22e">systemdDBus&lt;/span>(&lt;span style="color:#e6db74">"org.freedesktop.systemd1"&lt;/span>, &lt;span style="color:#e6db74">"/org/freedesktop/systemd1"&lt;/span>, &lt;span style="color:#e6db74">"org.freedesktop.systemd1.Manager"&lt;/span>);
qInfo() &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> systemdDBus.call(&lt;span style="color:#e6db74">"StartUnit"&lt;/span>, &lt;span style="color:#e6db74">"dde-session-shutdown.service"&lt;/span>, &lt;span style="color:#e6db74">"replace"&lt;/span>);
qApp&lt;span style="color:#f92672">-&amp;gt;&lt;/span>quit();
});
qInfo() &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> systemdDBus.call(&lt;span style="color:#e6db74">"StartUnit"&lt;/span>, &lt;span style="color:#e6db74">"dde-session-x11.target"&lt;/span>, &lt;span style="color:#e6db74">"replace"&lt;/span>);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>dde-session-shutdown.service&lt;/code> 只负责执行 &lt;code>dde-session-ctl --shutdown&lt;/code>，用于启动 &lt;code>dde-session-shutdown.target&lt;/code>。&lt;/p>
&lt;blockquote>
&lt;p>dde-session-shutdown.service&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ini" data-lang="ini">&lt;span style="color:#66d9ef">[Service]&lt;/span>
&lt;span style="color:#a6e22e">Type&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">oneshot&lt;/span>
&lt;span style="color:#a6e22e">ExecStart&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">/usr/lib/libexec/dde-session-ctl --logout&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>通过 systemd 的接口启动了 &lt;code>dde-session-shutdown.target&lt;/code>，从而启动了清理流程。&lt;/p>
&lt;p>&lt;code>dde-session-shutdown.target&lt;/code> 没有执行任何内容，仅仅是关联了一组服务，但是是冲突这些服务，因为这是启动的时候&lt;/p>
&lt;p>在 &lt;code>dde-session-shutdown.target&lt;/code> 中还关联了最重要的一个服务: &lt;code>dde-session-restart-dbus.service&lt;/code>，这个服务是用来关闭 &lt;code>dbus.service&lt;/code> 服务的，这样就可以保证 session 结束的时候，不会有 dbus 服务逃逸出去。&lt;/p>
&lt;blockquote>
&lt;p>dde-session-restart-dbus.service&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-ini" data-lang="ini">&lt;span style="color:#66d9ef">[Service]&lt;/span>
&lt;span style="color:#a6e22e">Type&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">notify&lt;/span>
&lt;span style="color:#a6e22e">ExecStart&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">/usr/lib/libexec/dde-session-ctl --restart-dbus&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>在 dde-session-ctl 中是这么实现 restart-dbus 的:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#66d9ef">if&lt;/span> (isRestartDBus) &amp;#123;
QDBusInterface &lt;span style="color:#a6e22e">systemd&lt;/span>(&lt;span style="color:#e6db74">"org.freedesktop.systemd1"&lt;/span>, &lt;span style="color:#e6db74">"/org/freedesktop/systemd1"&lt;/span>, &lt;span style="color:#e6db74">"org.freedesktop.systemd1.Manager"&lt;/span>);
qInfo() &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> systemd.call(&lt;span style="color:#e6db74">"StopUnit"&lt;/span>, &lt;span style="color:#e6db74">"dbus.service"&lt;/span>, &lt;span style="color:#e6db74">"replace-irreversibly"&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>复盘一下整个流程，在登录界面输入密码登录以后，lightdm 会启动 &lt;code>dde-session&lt;/code> 作为 session 的入口，&lt;code>dde-session&lt;/code> 会注册一个 &lt;code>org.deepin.Session&lt;/code> 的 dbus 服务，并且会启动关联的 systemd 服务，启动一个 &lt;code>dde-session --systemd-service&lt;/code>，在这个进程里，会启动 &lt;code>dde-session-x11.target&lt;/code>，从而最终启动了 &lt;code>dde-session-manager.service&lt;/code>，将原本的会话入口 startdde 启动了。&lt;/p>
&lt;p>在 &lt;code>dde-session --systemd-service&lt;/code> 会监听 &lt;code>org.deepin.Session&lt;/code> 服务的存活，当服务不存在时，就会开始执行退出流程，从而启动了 &lt;code>dde-session-ctl --shutdown&lt;/code>，在 dde-session-ctl 的帮助下，启动了 &lt;code>dde-session-shutdown.target&lt;/code>，将所有启动的 dde 核心服务都冲突掉，从而完成了服务关闭，在最后阶段再将 &lt;code>dbus.service&lt;/code> 服务停止，完成最终的清理。&lt;/p>
&lt;p>还有一种退出模式，手动启动了 &lt;code>dde-session-shutdown.service&lt;/code> 服务，也会触发完成的退出流程，&lt;code>dde-session-shutdown.service&lt;/code> 会利用 &lt;code>dde-session-ctl --logout&lt;/code> 将会话注销，从而触发上面的执行流程，完成会话与服务的关闭。&lt;/p>
&lt;p>目前还有一些问题没有解决，没有创建独立的 systemd.slice 和 systemd.scope，这可以帮助 dde 细致的划分和管理自己的子 services，退出 dbus.service 的手段非常黑，而且 dbus 服务的程序由于图形服务也已经结束了，导致成片的 X11 Error。这些都是要解决的。&lt;/p>
&lt;p>下一步准备写几个 service，用来代替 startdde 的组件启动，比如 kwin、dde-desktop、dde-dock 和 xdg-autostart 等。&lt;/p>
&lt;p>就目前的情况来看，任重而道远。&lt;/p>
&lt;p>原文链接：&lt;a href="https://blog.justforlxz.com/2021/12/29/Starting-sessions-with-systemd-part-2/">https://blog.justforlxz.com/2021/12/29/Starting-sessions-with-systemd-part-2/&lt;/a>&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>Starting sessions with systemd part 2</title><link>https://blog.mkacg.com/2021/12/29/Starting-sessions-with-systemd-part-2/</link><pubDate>Wed, 29 Dec 2021 13:09:07 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2021/12/29/Starting-sessions-with-systemd-part-2/</guid><description>&lt;p>接着上一篇文章 &lt;a href="https://blog.mkacg.com/2021/12/25/Starting-sessions-with-systemd/" title="Starting sessions with systemd">Starting sessions with systemd&lt;/a>，这篇文章主要讲解一下具体的实现。&lt;/p>&lt;p>&lt;a href="https://github.com/linuxdeepin/dde-session">https://github.com/linuxdeepin/dde-session&lt;/a> 已经包含了所有的文件和提交。&lt;/p>&lt;p>上面文章说过，为了让 dde 使用 systemd –user 来关系服务，我提供了一组服务：&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">dde-session-initialized.target&lt;/span>&lt;br />&lt;span class="line">dde-session-manager.service&lt;/span>&lt;br />&lt;span class="line">dde-session-manager.target&lt;/span>&lt;br />&lt;span class="line">dde-session-pre.target&lt;/span>&lt;br />&lt;span class="line">dde-session-restart-dbus.service&lt;/span>&lt;br />&lt;span class="line">dde-session-shutdown.service&lt;/span>&lt;br />&lt;span class="line">dde-session-shutdown.target&lt;/span>&lt;br />&lt;span class="line">dde-session.target&lt;/span>&lt;br />&lt;span class="line">dde-session-x11-services-ready.target&lt;/span>&lt;br />&lt;span class="line">dde-session-x11-services.target&lt;/span>&lt;br />&lt;span class="line">dde-session-x11.target&lt;/span>&lt;br />&lt;span class="line">org.deepin.Session.service&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>lightdm 在认证通过以后，会启动 dde-session 进程，dde-session 会通过 systemd 的 dbus 启动 &lt;code>org.deepin.Session.service&lt;/code>。&lt;/p>&lt;p>在 &lt;code>org.deepin.Session.service&lt;/code> 中会执行 &lt;code>dde-session-ctl --systemd-service&lt;/code>，启动 &lt;code>dde-session-x11.target&lt;/code>.&lt;/p>&lt;p>&lt;code>dde-session-x11.target&lt;/code> 关联了所有初始化的服务，systemd 会帮助自动运行这些服务，并且最终会运行到 &lt;code>dde-session-manager.service&lt;/code> 上。&lt;/p>&lt;blockquote>&lt;p>dde-session-manager.service&lt;/p>&lt;/blockquote>&lt;figure class="highlight ini">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="section">[Service]&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="attr">Type&lt;/span>=simple&lt;/span>&lt;br />&lt;span class="line">&lt;span class="attr">ExecStart&lt;/span>=/usr/bin/startdde&lt;/span>&lt;br />&lt;span class="line">&lt;span class="attr">ExecStopPost&lt;/span>=/usr/lib/libexec/dde-session-ctl --logout&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>在 &lt;code>org.deepin.Session.service&lt;/code> 中会启动一个特殊的服务，这个服务会监听 dde-session 的退出，因为总要有一个服务去监听 session 的状态，要确保 session 在服务在，session 退服务退，服务退 session 退，共存亡，这也是所有操作中最麻烦的一步。&lt;/p>&lt;blockquote>&lt;p>org.deepin.Session.service&lt;/p>&lt;/blockquote>&lt;figure class="highlight ini">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="section">[Service]&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="attr">Type&lt;/span>=dbus&lt;/span>&lt;br />&lt;span class="line">&lt;span class="attr">BusName&lt;/span>=org.deepin.Session&lt;/span>&lt;br />&lt;span class="line">&lt;span class="attr">ExecStart&lt;/span>=/usr/bin/dde-session --systemd-service&lt;/span>&lt;br />&lt;span class="line">&lt;span class="attr">ExecStop&lt;/span>=/usr/lib/libexec/dde-session-ctl --shutdown&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>在 dde-session-ctl 中是这么实现 shutdown 的:&lt;/p>&lt;figure class="highlight c++">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="keyword">if&lt;/span> (isShutdown) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">// kill startdde-session or call login1&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="function">QDBusInterface &lt;span class="title">systemd&lt;/span>&lt;span class="params">(&lt;span class="string">&amp;quot;org.freedesktop.systemd1&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;/org/freedesktop/systemd1&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;org.freedesktop.systemd1.Manager&amp;quot;&lt;/span>)&lt;/span>&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">qInfo&lt;/span>() &amp;lt;&amp;lt; systemd.&lt;span class="built_in">call&lt;/span>(&lt;span class="string">&amp;quot;StartUnit&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;dde-session-shutdown.target&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;replace-irreversibly&amp;quot;&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="number">0&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>在 dde-session-ctl 中是这么实现 logout 的:&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="keyword">if&lt;/span> (parser.&lt;span class="built_in">isSet&lt;/span>(logout)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> org::&lt;span class="function">deepin::Session &lt;span class="title">session&lt;/span>&lt;span class="params">(&lt;span class="string">&amp;quot;org.deepin.Session&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;/org/deepin/Session&amp;quot;&lt;/span>, QDBusConnection::sessionBus())&lt;/span>&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> session.&lt;span class="built_in">Logout&lt;/span>();&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="number">0&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;blockquote>&lt;p>dde-session main.cpp&lt;/p>&lt;/blockquote>&lt;figure class="highlight c++">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="comment">// 添加启动参数，表示这是通过 systemd 启动的。&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="function">QCommandLineOption &lt;span class="title">systemd&lt;/span>&lt;span class="params">(QStringList&amp;#123;&lt;span class="string">&amp;quot;d&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;systemd-service&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;wait for systemd services&amp;quot;&lt;/span>&amp;#125;)&lt;/span>&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">parser.&lt;span class="built_in">addOption&lt;/span>(systemd);&lt;/span>&lt;br />&lt;span class="line">parser.&lt;span class="built_in">process&lt;/span>(app);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="keyword">if&lt;/span> (parser.&lt;span class="built_in">isSet&lt;/span>(systemd)) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="number">-1&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">QDBusServiceWatcher *watcher = &lt;span class="keyword">new&lt;/span> &lt;span class="built_in">QDBusServiceWatcher&lt;/span>(&lt;span class="string">&amp;quot;org.deepin.Session&amp;quot;&lt;/span>, QDBusConnection::&lt;span class="built_in">sessionBus&lt;/span>(), QDBusServiceWatcher::WatchForUnregistration);&lt;/span>&lt;br />&lt;span class="line"> watcher-&amp;gt;&lt;span class="built_in">connect&lt;/span>(watcher, &amp;amp;QDBusServiceWatcher::serviceUnregistered, [=] &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> QDBusInterface &lt;span class="built_in">systemdDBus&lt;/span>(&lt;span class="string">&amp;quot;org.freedesktop.systemd1&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;/org/freedesktop/systemd1&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;org.freedesktop.systemd1.Manager&amp;quot;&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">qInfo&lt;/span>() &amp;lt;&amp;lt; systemdDBus.&lt;span class="built_in">call&lt;/span>(&lt;span class="string">&amp;quot;StartUnit&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;dde-session-shutdown.service&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;replace&amp;quot;&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> qApp-&amp;gt;&lt;span class="built_in">quit&lt;/span>();&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">qInfo&lt;/span>() &amp;lt;&amp;lt; systemdDBus.&lt;span class="built_in">call&lt;/span>(&lt;span class="string">&amp;quot;StartUnit&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;dde-session-x11.target&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;replace&amp;quot;&lt;/span>);&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>&lt;code>dde-session-shutdown.service&lt;/code> 只负责执行 &lt;code>dde-session-ctl --shutdown&lt;/code>，用于启动 &lt;code>dde-session-shutdown.target&lt;/code>。&lt;/p>&lt;blockquote>&lt;p>dde-session-shutdown.service&lt;/p>&lt;/blockquote>&lt;figure class="highlight ini">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="section">[Service]&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="attr">Type&lt;/span>=&lt;span class="literal">on&lt;/span>eshot&lt;/span>&lt;br />&lt;span class="line">&lt;span class="attr">ExecStart&lt;/span>=/usr/lib/libexec/dde-session-ctl --logout&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>通过 systemd 的接口启动了 &lt;code>dde-session-shutdown.target&lt;/code>，从而启动了清理流程。&lt;/p>&lt;p>&lt;code>dde-session-shutdown.target&lt;/code> 没有执行任何内容，仅仅是关联了一组服务，但是是冲突这些服务，因为这是启动的时候&lt;/p>&lt;p>在 &lt;code>dde-session-shutdown.target&lt;/code> 中还关联了最重要的一个服务: &lt;code>dde-session-restart-dbus.service&lt;/code>，这个服务是用来关闭 &lt;code>dbus.service&lt;/code> 服务的，这样就可以保证 session 结束的时候，不会有 dbus 服务逃逸出去。&lt;/p>&lt;blockquote>&lt;p>dde-session-restart-dbus.service&lt;/p>&lt;/blockquote>&lt;figure class="highlight ini">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="section">[Service]&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="attr">Type&lt;/span>=notify&lt;/span>&lt;br />&lt;span class="line">&lt;span class="attr">ExecStart&lt;/span>=/usr/lib/libexec/dde-session-ctl --restart-dbus&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>在 dde-session-ctl 中是这么实现 restart-dbus 的:&lt;/p>&lt;figure class="highlight cpp">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="keyword">if&lt;/span> (isRestartDBus) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="function">QDBusInterface &lt;span class="title">systemd&lt;/span>&lt;span class="params">(&lt;span class="string">&amp;quot;org.freedesktop.systemd1&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;/org/freedesktop/systemd1&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;org.freedesktop.systemd1.Manager&amp;quot;&lt;/span>)&lt;/span>&lt;/span>;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="built_in">qInfo&lt;/span>() &amp;lt;&amp;lt; systemd.&lt;span class="built_in">call&lt;/span>(&lt;span class="string">&amp;quot;StopUnit&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;dbus.service&amp;quot;&lt;/span>, &lt;span class="string">&amp;quot;replace-irreversibly&amp;quot;&lt;/span>);&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="keyword">return&lt;/span> &lt;span class="number">0&lt;/span>;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>复盘一下整个流程，在登录界面输入密码登录以后，lightdm 会启动 &lt;code>dde-session&lt;/code> 作为 session 的入口，&lt;code>dde-session&lt;/code> 会注册一个 &lt;code>org.deepin.Session&lt;/code> 的 dbus 服务，并且会启动关联的 systemd 服务，启动一个 &lt;code>dde-session --systemd-service&lt;/code>，在这个进程里，会启动 &lt;code>dde-session-x11.target&lt;/code>，从而最终启动了 &lt;code>dde-session-manager.service&lt;/code>，将原本的会话入口 startdde 启动了。&lt;/p>&lt;p>在 &lt;code>dde-session --systemd-service&lt;/code> 会监听 &lt;code>org.deepin.Session&lt;/code> 服务的存活，当服务不存在时，就会开始执行退出流程，从而启动了 &lt;code>dde-session-ctl --shutdown&lt;/code>，在 dde-session-ctl 的帮助下，启动了 &lt;code>dde-session-shutdown.target&lt;/code>，将所有启动的 dde 核心服务都冲突掉，从而完成了服务关闭，在最后阶段再将 &lt;code>dbus.service&lt;/code> 服务停止，完成最终的清理。&lt;/p>&lt;p>还有一种退出模式，手动启动了 &lt;code>dde-session-shutdown.service&lt;/code> 服务，也会触发完成的退出流程，&lt;code>dde-session-shutdown.service&lt;/code> 会利用 &lt;code>dde-session-ctl --logout&lt;/code> 将会话注销，从而触发上面的执行流程，完成会话与服务的关闭。&lt;/p>&lt;p>目前还有一些问题没有解决，没有创建独立的 systemd.slice 和 systemd.scope，这可以帮助 dde 细致的划分和管理自己的子 services，退出 dbus.service 的手段非常黑，而且 dbus 服务的程序由于图形服务也已经结束了，导致成片的 X11 Error。这些都是要解决的。&lt;/p>&lt;p>下一步准备写几个 service，用来代替 startdde 的组件启动，比如 kwin、dde-desktop、dde-dock 和 xdg-autostart 等。&lt;/p>&lt;p>就目前的情况来看，任重而道远。&lt;/p></description><author>justforlxz</author></item><item><title>Starting sessions with systemd part 1</title><link>https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/</link><pubDate>Sat, 25 Dec 2021 22:00:53 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/</guid><description>&lt;p>DDE 现在正在做 Wayland 的支持，所以我们需要对目前的桌面环境结构进行调整，
考虑到 GNOME 和 KDE 都已经使用 &lt;code>systemd&lt;/code> 来管理 session，我认为 deepin 团队也可以考虑这一步了。&lt;/p>
&lt;h2 id="为什么需要-systemd">为什么需要 systemd？
&lt;span>
&lt;a href="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/#%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81-systemd">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>目前 &lt;code>systemd&lt;/code> 作为事实上胜利的 init 进程，它现在负责的功能已经越来越多了，支持但不限于：&lt;code>udev&lt;/code>、&lt;code>sleep/hibernate/suspend&lt;/code>、&lt;code>dbus&lt;/code>、&lt;code>services&lt;/code> 等。现在越来越多的程序也开始使用 &lt;code>systemd&lt;/code> 管理后台服务。&lt;/p>
&lt;h2 id="不使用-systemd-来管理-session-会有什么问题吗">不使用 systemd 来管理 session 会有什么问题吗？
&lt;span>
&lt;a href="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/#%e4%b8%8d%e4%bd%bf%e7%94%a8-systemd-%e6%9d%a5%e7%ae%a1%e7%90%86-session-%e4%bc%9a%e6%9c%89%e4%bb%80%e4%b9%88%e9%97%ae%e9%a2%98%e5%90%97">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>存在一个进程逃逸问题，不过这个问题与是否采用 &lt;code>systemd&lt;/code> 管理 &lt;code>session&lt;/code> 没有太大关系。&lt;/p>
&lt;p>我先来简单介绍一下目前 DDE 的工作模型：&lt;/p>
&lt;p>首先，DDE 使用 &lt;code>LightDM&lt;/code> 作为显示服务器（Display Manager），DDE 提供了 &lt;code>lightdm-deepin-greeter&lt;/code> 作为登录界面，greeter 可以通过调用 LightDM 的 api 进行 linux-pam 的认证。&lt;/p>
&lt;p>当认证通过后，LightDM 会启动一个会话（Session），之后运行的程序都将是以登录的用户身份启动，然后 &lt;code>systemd --user&lt;/code> 进程会被启动，同时 &lt;code>dbus-daemon&lt;/code> 也会启动，之后 LightDM 会启动 &lt;code>startdde。&lt;/code>&lt;/p>
&lt;p>此时就开始执行到 DDE &lt;code>的初始化阶段，startdde&lt;/code> 会启动 &lt;code>dde-session-daemon&lt;/code> 和 DDE 的核心组件，之后就并行启动 autostart 中的 desktop 文件。&lt;/p>
&lt;p>&lt;code>startdde&lt;/code> 目前的流程本身没有问题，问题出在 &lt;code>systemd&lt;/code> 接管了 dbus，并且从 &lt;code>systemd 226&lt;/code> 版本开始，&lt;code>/etc/pam.d/system-login&lt;/code> 默认配置中的 &lt;code>pam_systemd&lt;/code> 模块会在用户首次登录的时候, 自动运行一个 &lt;code>systemd --user&lt;/code> 实例。 只要用户还有会话存在，这个进程就不会退出；用户所有会话退出时，进程将会被销毁。当#随系统自动启动 &lt;code>systemd&lt;/code> 用户实例启用时, 这个用户实例将在系统启动时加载，并且不会被销毁。&lt;/p>
&lt;blockquote>
&lt;p>&lt;code>systemd --user&lt;/code> 实例是针对每个用户处理的，而不是针对会话。这样做的原理是用户服务处理的大部分资源，像 socket 或状态文件是针对每个用户的（存活于用户的主目录下）而不是会话。这意味着所有的用户服务是独立于会话之外运行的。最终，我们得出结论：基于会话运行的程序可能会导致用户服务中断。&lt;code>systemd&lt;/code> 处理用户会话的方式是非常生硬的（pretty much in flux）。&lt;/p>
&lt;/blockquote>
&lt;p>那么问题就来了，上面说了，systemd 接管了 dbus 服务，通过 dbus 启动的程序会在 &lt;code>dbus.service&lt;/code> 下面，成为它的子进程，而 &lt;code>systemd --user&lt;/code> 在有用户其它登录的会话存在时，并不会停止 &lt;code>systemd --user&lt;/code>，所以 &lt;code>dbus.service&lt;/code> 也不会停止。但是 dde 的会话
已经完成注销了，但是通过 dde 启动的程序却没有被退出，从而导致了程序 &lt;strong>逃逸&lt;/strong>。&lt;/p>
&lt;h2 id="解决方案">解决方案？
&lt;span>
&lt;a href="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/#%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>如果只是想解决逃逸问题，那么想一个办法，在 &lt;code>session&lt;/code> 停止的时候，主动把 &lt;code>dbus.service&lt;/code> 服务停掉其实就可以解决了，但是我们想要利用 &lt;code>systemd&lt;/code> 的自动依赖解决，这样就不用重复造一点点小轮子了。&lt;/p>
&lt;p>那么需要怎么修改才能符合桌面环境的要求？&lt;/p>
&lt;p>GNOME 和 KDE 现在已经完成了 &lt;code>systemd session&lt;/code> 的工作，我们可以在这两个老大哥身上学习。&lt;/p>
&lt;blockquote>
&lt;p>提供了一个新的会话入口点 &lt;code>gnome-session-systemd&lt;/code>。 这需要一个参数，即开始会话的单元（通常是 .target 单元）。 它将执行一些清理功能，初始化 systemd 环境，然后启动单元并等待它停止。会话通过修改它们的 Exec 行并删除 &lt;code>RequiredComponents&lt;/code> 来选择以这种方式启动。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>XDG autostart 现在 &lt;code>systemd&lt;/code> 提供了一个 wrapper，可以自动生成出 service 单元，所以功能可以复用。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>在 systemd 管理的系统上，每个用户都被分配了一个 &lt;code>user-x.slice (systemd.slice(5))&lt;/code>,并且用户的会话将在 &lt;code>session-Y.scope (systemd.scope(5))&lt;/code> 中运行。您还可以在主机上看到一些其它用户特定的单元，包括 &lt;code>user@x.service&lt;/code>,它是用户的 &lt;code>systemd&lt;/code> 实例。这是用户的一个单独的 &lt;code>systemd&lt;/code> 进程，如果用户不再登录，它将再次关闭。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>随着 &lt;code>systemd&lt;/code> 的移动，不仅 DBus 激活的应用程序和服务，您的整个会话现在都使用用户的 &lt;code>systemd&lt;/code> 实例启动。 这有一些副作用，起初可能看起来很奇怪。 例如，之前提到的 session-Y.scope 曾经包含 200 多个进程，现在减少到仅 4 个进程。 另一个副作用是更难理解进程属于哪个会话（这与许多服务相关）或者 ps 将不再显示 tty。&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>但是根据 GNOME blog 的文章说明，这些副作用已经被处理了。GNOME会话仍然始终绑定到&lt;code>session-Y.scope&lt;/code>（例如，使用 &lt;code>loginctl kill-session&lt;/code> 继续可靠地工作）。&lt;/p>
&lt;/blockquote>
&lt;p>查看了 GNOME 提供的 dbus 服务单元文件，最终明白了 GNOME 是怎么做到防止 dbus 程序逃逸的，其实操作非常简单，就是提供了一个 &lt;code>gnome-session-shutdown.target&lt;/code>，在这个 target 里关联了 &lt;code>gnome-sessino-restart-dbus.service&lt;/code>，里面执行的是 &lt;code>gnome-session-ctl --restart-dbus&lt;/code>，
其实就是通过 DBus 调用了 &lt;code>systemd --user&lt;/code> 的 dbus 方法，将 &lt;code>dbus.service&lt;/code> 给停掉了。（- - |）&lt;/p>
&lt;h2 id="改造方案">改造方案
&lt;span>
&lt;a href="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/#%e6%94%b9%e9%80%a0%e6%96%b9%e6%a1%88">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>既然了解了前因后果，那么我们可以着手改造 DDE 了。&lt;/p>
&lt;p>还需要补充一个知识，会话启动以后，会有一个进程作为会话的入口，会话所有的程序都是从这个入口开始启动的，这个入口就是 &lt;code>startdde&lt;/code>。&lt;/p>
&lt;p>我们既然希望利用 &lt;code>systemd&lt;/code> 的服务来启动，并且帮助我们自动解决依赖，但是拆分项目目前压力比较大，我们还计划重写一部分后端功能，可以在重写时进行调整。&lt;/p>
&lt;h3 id="启动入口">启动入口
&lt;span>
&lt;a href="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/#%e5%90%af%e5%8a%a8%e5%85%a5%e5%8f%a3">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>首先我们需要创建一系列的 &lt;code>service&lt;/code> 和 &lt;code>target&lt;/code> 文件。&lt;/p>
&lt;pre>&lt;code>dde-session-initialized.target
dde-session-manager.service
dde-session-manager.target
dde-session-pre.target
dde-session-restart-dbus.service
dde-session-shutdown.service
dde-session-shutdown.target
dde-session.target
dde-session-x11-services-ready.target
dde-session-x11-services.target
dde-session-x11.target
org.deepin.Session.service
&lt;/code>&lt;/pre>&lt;p>看起来非常的多，不用担心，他们都有各自的作用，你可以认为这是将生命周期在 &lt;code>systemd&lt;/code> 实现了。&lt;/p>
&lt;p>由于 &lt;code>systemd&lt;/code> 会帮助我们自动完成依赖解析，那么我们只需要保留一个入口服务，其它服务都禁止手动启动即可。&lt;/p>
&lt;p>我选择使用 &lt;code>dde-session-x11.target&lt;/code> 作为入口，因为未来 deepin 还要支持 wayland，那么在这里就区分开会比较方便一些，因为桌面环境的后续启动与使用什么图形服务没有太大关系。&lt;/p>
&lt;p>根据文件名称就可以方便的了解到这些文件是在什么阶段被执行的。&lt;/p>
&lt;h3 id="退出时清理">退出时清理
&lt;span>
&lt;a href="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/#%e9%80%80%e5%87%ba%e6%97%b6%e6%b8%85%e7%90%86">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>创建的 &lt;code>dde-session-shutdown.target&lt;/code> 用来关联所有退出时需要执行的 &lt;code>services&lt;/code>。&lt;/p>
&lt;h3 id="清理-dbusservice">清理 dbus.service
&lt;span>
&lt;a href="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/#%e6%b8%85%e7%90%86-dbusservice">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>提供了一个 &lt;code>dde-session-restart-dbus.service&lt;/code> 用来注销以后关闭 &lt;code>dbus.service&lt;/code>，不要问，问就是没办法～。&lt;/p>
&lt;p>在 &lt;code>dde-session-shutdown.target&lt;/code> 中会关联这个服务，当用户的会话注销或者桌面环境服务出现问题时，就可以退出所有的 &lt;code>dbus.service&lt;/code>。&lt;/p>
&lt;p>当发现会话注销时，&lt;code>dde-session-manager.service&lt;/code> 会执行退出，在服务关闭时启动 &lt;code>dde-session-shutdown.target&lt;/code>，并且使用 &lt;code>replace-irreversibly&lt;/code> 标记为不可撤销。&lt;/p>
&lt;p>&lt;code>dde-session-shutdown.target&lt;/code> 中又会清理 &lt;code>dbus.service&lt;/code> 下的所有程序，这样就避免了服务可以通过 dbus 逃逸出会话。&lt;/p>
&lt;h3 id="架构模型">架构模型
&lt;span>
&lt;a href="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/#%e6%9e%b6%e6%9e%84%e6%a8%a1%e5%9e%8b">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;center>
&lt;p>&lt;img alt="" src="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/model.svg" />&lt;/p>
&lt;/center>
&lt;h3 id="最终效果">最终效果
&lt;span>
&lt;a href="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/#%e6%9c%80%e7%bb%88%e6%95%88%e6%9e%9c">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>可以看到 startdde 和它的进程树挂在 systemd 下面。&lt;/p>
&lt;center>
&lt;p>&lt;img alt="" src="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/110276978.jpeg" />&lt;/p>
&lt;/center>
&lt;p>原本的位置现在只有一个占位的程序。&lt;/p>
&lt;center>
&lt;p>&lt;img alt="" src="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/3503592248.jpeg" />&lt;/p>
&lt;/center>
&lt;h2 id="引用资料">引用资料
&lt;span>
&lt;a href="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/#%e5%bc%95%e7%94%a8%e8%b5%84%e6%96%99">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;blockquote>
&lt;p>&lt;a href="https://blogs.gnome.org/benzea/2019/10/01/gnome-3-34-is-now-managed-using-systemd/">https://blogs.gnome.org/benzea/2019/10/01/gnome-3-34-is-now-managed-using-systemd/&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>原文链接：&lt;a href="https://blog.justforlxz.com/2021/12/25/Starting-sessions-with-systemd/">https://blog.justforlxz.com/2021/12/25/Starting-sessions-with-systemd/&lt;/a>&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>Starting sessions with systemd</title><link>https://blog.mkacg.com/2021/12/25/Starting-sessions-with-systemd/</link><pubDate>Sat, 25 Dec 2021 14:00:53 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2021/12/25/Starting-sessions-with-systemd/</guid><description>&lt;p>DDE 现在正在做 Wayland 的支持，所以我们需要对目前的桌面环境结构进行调整，&lt;br />考虑到 GNOME 和 KDE 都已经使用 &lt;code>systemd&lt;/code> 来管理 session，我认为 deepin 团队也可以考虑这一步了。&lt;/p>&lt;h2 id="为什么需要-systemd？">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#为什么需要-systemd？" title="为什么需要 systemd？">&lt;/a>为什么需要 systemd？&lt;/h2>&lt;p>目前 &lt;code>systemd&lt;/code> 作为事实上胜利的 init 进程，它现在负责的功能已经越来越多了，支持但不限于：&lt;code>udev&lt;/code>、&lt;code>sleep/hibernate/suspend&lt;/code>、&lt;code>dbus&lt;/code>、&lt;code>services&lt;/code> 等。现在越来越多的程序也开始使用 &lt;code>systemd&lt;/code> 管理后台服务。&lt;/p>&lt;h2 id="不使用-systemd-来管理-session-会有什么问题吗？">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#不使用-systemd-来管理-session-会有什么问题吗？" title="不使用 systemd 来管理 session 会有什么问题吗？">&lt;/a>不使用 systemd 来管理 session 会有什么问题吗？&lt;/h2>&lt;p>存在一个进程逃逸问题，不过这个问题与是否采用 &lt;code>systemd&lt;/code> 管理 &lt;code>session&lt;/code> 没有太大关系。&lt;/p>&lt;p>我先来简单介绍一下目前 DDE 的工作模型：&lt;/p>&lt;p>首先，DDE 使用 &lt;code>LightDM&lt;/code> 作为显示服务器（Display Manager），DDE 提供了 &lt;code>lightdm-deepin-greeter&lt;/code> 作为登录界面，greeter 可以通过调用 LightDM 的 api 进行 linux-pam 的认证。&lt;/p>&lt;p>当认证通过后，LightDM 会启动一个会话（Session），之后运行的程序都将是以登录的用户身份启动，然后 &lt;code>systemd --user&lt;/code> 进程会被启动，同时 &lt;code>dbus-daemon&lt;/code> 也会启动，之后 LightDM 会启动 &lt;code>startdde。&lt;/code>&lt;/p>&lt;p>此时就开始执行到 DDE &lt;code>的初始化阶段，startdde&lt;/code> 会启动 &lt;code>dde-session-daemon&lt;/code> 和 DDE 的核心组件，之后就并行启动 autostart 中的 desktop 文件。&lt;/p>&lt;p>&lt;code>startdde&lt;/code> 目前的流程本身没有问题，问题出在 &lt;code>systemd&lt;/code> 接管了 dbus，并且从 &lt;code>systemd 226&lt;/code> 版本开始，&lt;code>/etc/pam.d/system-login&lt;/code> 默认配置中的 &lt;code>pam_systemd&lt;/code> 模块会在用户首次登录的时候, 自动运行一个 &lt;code>systemd --user&lt;/code> 实例。 只要用户还有会话存在，这个进程就不会退出；用户所有会话退出时，进程将会被销毁。当#随系统自动启动 &lt;code>systemd&lt;/code> 用户实例启用时, 这个用户实例将在系统启动时加载，并且不会被销毁。&lt;/p>&lt;blockquote>&lt;p>&lt;code>systemd --user&lt;/code> 实例是针对每个用户处理的，而不是针对会话。这样做的原理是用户服务处理的大部分资源，像 socket 或状态文件是针对每个用户的（存活于用户的主目录下）而不是会话。这意味着所有的用户服务是独立于会话之外运行的。最终，我们得出结论：基于会话运行的程序可能会导致用户服务中断。&lt;code>systemd&lt;/code> 处理用户会话的方式是非常生硬的（pretty much in flux）。&lt;/p>&lt;/blockquote>&lt;p>那么问题就来了，上面说了，systemd 接管了 dbus 服务，通过 dbus 启动的程序会在 &lt;code>dbus.service&lt;/code> 下面，成为它的子进程，而 &lt;code>systemd --user&lt;/code> 在有用户其它登录的会话存在时，并不会停止 &lt;code>systemd --user&lt;/code>，所以 &lt;code>dbus.service&lt;/code> 也不会停止。但是 dde 的会话&lt;br />已经完成注销了，但是通过 dde 启动的程序却没有被退出，从而导致了程序 &lt;strong>逃逸&lt;/strong>。&lt;/p>&lt;h2 id="解决方案？">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#解决方案？" title="解决方案？">&lt;/a>解决方案？&lt;/h2>&lt;p>如果只是想解决逃逸问题，那么想一个办法，在 &lt;code>session&lt;/code> 停止的时候，主动把 &lt;code>dbus.service&lt;/code> 服务停掉其实就可以解决了，但是我们想要利用 &lt;code>systemd&lt;/code> 的自动依赖解决，这样就不用重复造一点点小轮子了。&lt;/p>&lt;p>那么需要怎么修改才能符合桌面环境的要求？&lt;/p>&lt;p>GNOME 和 KDE 现在已经完成了 &lt;code>systemd session&lt;/code> 的工作，我们可以在这两个老大哥身上学习。&lt;/p>&lt;blockquote>&lt;p>提供了一个新的会话入口点 &lt;code>gnome-session-systemd&lt;/code>。 这需要一个参数，即开始会话的单元（通常是 .target 单元）。 它将执行一些清理功能，初始化 systemd 环境，然后启动单元并等待它停止。会话通过修改它们的 Exec 行并删除 &lt;code>RequiredComponents&lt;/code> 来选择以这种方式启动。&lt;/p>&lt;/blockquote>&lt;blockquote>&lt;p>XDG autostart 现在 &lt;code>systemd&lt;/code> 提供了一个 wrapper，可以自动生成出 service 单元，所以功能可以复用。&lt;/p>&lt;/blockquote>&lt;blockquote>&lt;p>在 systemd 管理的系统上，每个用户都被分配了一个 &lt;code>user-x.slice (systemd.slice(5))&lt;/code>,并且用户的会话将在 &lt;code>session-Y.scope (systemd.scope(5))&lt;/code> 中运行。您还可以在主机上看到一些其它用户特定的单元，包括 &lt;code>user@x.service&lt;/code>,它是用户的 &lt;code>systemd&lt;/code> 实例。这是用户的一个单独的 &lt;code>systemd&lt;/code> 进程，如果用户不再登录，它将再次关闭。&lt;/p>&lt;/blockquote>&lt;blockquote>&lt;p>随着 &lt;code>systemd&lt;/code> 的移动，不仅 DBus 激活的应用程序和服务，您的整个会话现在都使用用户的 &lt;code>systemd&lt;/code> 实例启动。 这有一些副作用，起初可能看起来很奇怪。 例如，之前提到的 session-Y.scope 曾经包含 200 多个进程，现在减少到仅 4 个进程。 另一个副作用是更难理解进程属于哪个会话（这与许多服务相关）或者 ps 将不再显示 tty。&lt;/p>&lt;/blockquote>&lt;blockquote>&lt;p>但是根据 GNOME blog 的文章说明，这些副作用已经被处理了。GNOME会话仍然始终绑定到&lt;code>session-Y.scope&lt;/code>（例如，使用 &lt;code>loginctl kill-session&lt;/code> 继续可靠地工作）。&lt;/p>&lt;/blockquote>&lt;p>查看了 GNOME 提供的 dbus 服务单元文件，最终明白了 GNOME 是怎么做到防止 dbus 程序逃逸的，其实操作非常简单，就是提供了一个 &lt;code>gnome-session-shutdown.target&lt;/code>，在这个 target 里关联了 &lt;code>gnome-sessino-restart-dbus.service&lt;/code>，里面执行的是 &lt;code>gnome-session-ctl --restart-dbus&lt;/code>，&lt;br />其实就是通过 DBus 调用了 &lt;code>systemd --user&lt;/code> 的 dbus 方法，将 &lt;code>dbus.service&lt;/code> 给停掉了。（- - |）&lt;/p>&lt;h2 id="改造方案">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#改造方案" title="改造方案">&lt;/a>改造方案&lt;/h2>&lt;p>既然了解了前因后果，那么我们可以着手改造 DDE 了。&lt;/p>&lt;p>还需要补充一个知识，会话启动以后，会有一个进程作为会话的入口，会话所有的程序都是从这个入口开始启动的，这个入口就是 &lt;code>startdde&lt;/code>。&lt;/p>&lt;p>我们既然希望利用 &lt;code>systemd&lt;/code> 的服务来启动，并且帮助我们自动解决依赖，但是拆分项目目前压力比较大，我们还计划重写一部分后端功能，可以在重写时进行调整。&lt;/p>&lt;h3 id="启动入口">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#启动入口" title="启动入口">&lt;/a>启动入口&lt;/h3>&lt;p>首先我们需要创建一系列的 &lt;code>service&lt;/code> 和 &lt;code>target&lt;/code> 文件。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">dde-session-initialized.target&lt;/span>&lt;br />&lt;span class="line">dde-session-manager.service&lt;/span>&lt;br />&lt;span class="line">dde-session-manager.target&lt;/span>&lt;br />&lt;span class="line">dde-session-pre.target&lt;/span>&lt;br />&lt;span class="line">dde-session-restart-dbus.service&lt;/span>&lt;br />&lt;span class="line">dde-session-shutdown.service&lt;/span>&lt;br />&lt;span class="line">dde-session-shutdown.target&lt;/span>&lt;br />&lt;span class="line">dde-session.target&lt;/span>&lt;br />&lt;span class="line">dde-session-x11-services-ready.target&lt;/span>&lt;br />&lt;span class="line">dde-session-x11-services.target&lt;/span>&lt;br />&lt;span class="line">dde-session-x11.target&lt;/span>&lt;br />&lt;span class="line">org.deepin.Session.service&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>看起来非常的多，不用担心，他们都有各自的作用，你可以认为这是将生命周期在 &lt;code>systemd&lt;/code> 实现了。&lt;/p>&lt;p>由于 &lt;code>systemd&lt;/code> 会帮助我们自动完成依赖解析，那么我们只需要保留一个入口服务，其它服务都禁止手动启动即可。&lt;/p>&lt;p>我选择使用 &lt;code>dde-session-x11.target&lt;/code> 作为入口，因为未来 deepin 还要支持 wayland，那么在这里就区分开会比较方便一些，因为桌面环境的后续启动与使用什么图形服务没有太大关系。&lt;/p>&lt;p>根据文件名称就可以方便的了解到这些文件是在什么阶段被执行的。&lt;/p>&lt;h3 id="退出时清理">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#退出时清理" title="退出时清理">&lt;/a>退出时清理&lt;/h3>&lt;p>创建的 &lt;code>dde-session-shutdown.target&lt;/code> 用来关联所有退出时需要执行的 &lt;code>services&lt;/code>。&lt;/p>&lt;h3 id="清理-dbus-service">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#清理-dbus-service" title="清理 dbus.service">&lt;/a>清理 dbus.service&lt;/h3>&lt;p>提供了一个 &lt;code>dde-session-restart-dbus.service&lt;/code> 用来注销以后关闭 &lt;code>dbus.service&lt;/code>，不要问，问就是没办法～。&lt;/p>&lt;p>在 &lt;code>dde-session-shutdown.target&lt;/code> 中会关联这个服务，当用户的会话注销或者桌面环境服务出现问题时，就可以退出所有的 &lt;code>dbus.service&lt;/code>。&lt;/p>&lt;p>当发现会话注销时，&lt;code>dde-session-manager.service&lt;/code> 会执行退出，在服务关闭时启动 &lt;code>dde-session-shutdown.target&lt;/code>，并且使用 &lt;code>replace-irreversibly&lt;/code> 标记为不可撤销。&lt;/p>&lt;p>&lt;code>dde-session-shutdown.target&lt;/code> 中又会清理 &lt;code>dbus.service&lt;/code> 下的所有程序，这样就避免了服务可以通过 dbus 逃逸出会话。&lt;/p>&lt;h3 id="架构模型">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#架构模型" title="架构模型">&lt;/a>架构模型&lt;/h3>&lt;center>&lt;img alt="systemd model" src="https://blog.mkacg.com/rss/model.svg" />&lt;/center>&lt;h3 id="最终效果">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#最终效果" title="最终效果">&lt;/a>最终效果&lt;/h3>&lt;p>可以看到 startdde 和它的进程树挂在 systemd 下面。&lt;/p>&lt;center>&lt;img alt="systemd session" src="https://blog.mkacg.com/rss/110276978.jpeg" />&lt;/center>&lt;p>原本的位置现在只有一个占位的程序。&lt;/p>&lt;center>&lt;img alt="systemd session" src="https://blog.mkacg.com/rss/3503592248.jpeg" />&lt;/center>&lt;h2 id="引用资料">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#引用资料" title="引用资料">&lt;/a>引用资料&lt;/h2>&lt;blockquote>&lt;p>&lt;a href="https://blogs.gnome.org/benzea/2019/10/01/gnome-3-34-is-now-managed-using-systemd/">https://blogs.gnome.org/benzea/2019/10/01/gnome-3-34-is-now-managed-using-systemd/&lt;/a>&lt;/p>&lt;/blockquote></description><author>justforlxz</author></item><item><title>CSS浮动笔记</title><link>https://blog.mkacg.com/2021/08/07/CSS%E6%B5%AE%E5%8A%A8%E7%AC%94%E8%AE%B0/</link><pubDate>Sat, 07 Aug 2021 03:02:20 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2021/08/07/CSS%E6%B5%AE%E5%8A%A8%E7%AC%94%E8%AE%B0/</guid><description>&lt;h2 id="DOM文档排版的特点">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#DOM文档排版的特点" title="DOM文档排版的特点">&lt;/a>DOM文档排版的特点&lt;/h2>&lt;p>块级元素只能垂直排版，行内元素只能水平排版。&lt;/p>&lt;p>块级元素可以设置宽高属性，行内元素没有宽高属性，只能被内部元素撑起来，水平放置不下时会换行继续排版。&lt;/p>&lt;h2 id="CSS-浮动">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#CSS-浮动" title="CSS 浮动">&lt;/a>CSS 浮动&lt;/h2>&lt;p>浮动就是让文档脱离文档流，在父元素内部可以移动到指定位置。&lt;/p>&lt;p>浮动的元素可以设置宽高，像块级元素一样，又向行内元素一样可以水平排版。&lt;/p>&lt;figure class="highlight html">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="meta">&amp;lt;!doctype &lt;span class="keyword">html&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">html&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">head&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">meta&lt;/span> &lt;span class="attr">charset&lt;/span>=&lt;span class="string">&amp;quot;utf-8&amp;quot;&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">title&lt;/span>&amp;gt;&lt;/span>元素的浮动&lt;span class="tag">&amp;lt;/&lt;span class="name">title&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">style&lt;/span> &lt;span class="attr">type&lt;/span>=&lt;span class="string">&amp;quot;text/css&amp;quot;&lt;/span>&amp;gt;&lt;/span>&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="comment">/*定义父元素的样式*/&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="selector-class">.father&lt;/span> &amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">background&lt;/span>: &lt;span class="number">#ccc&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">border&lt;/span>: &lt;span class="number">1px&lt;/span> dashed &lt;span class="number">#999&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="comment">/*定义 box01、 box02、 box03 三个盒子的样式*/&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="selector-class">.box01&lt;/span>, &lt;span class="selector-class">.box02&lt;/span>, &lt;span class="selector-class">.box03&lt;/span> &amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">height&lt;/span>: &lt;span class="number">50px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">width&lt;/span>: &lt;span class="number">50px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">background&lt;/span>: &lt;span class="number">#FF9&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">border&lt;/span>: &lt;span class="number">1px&lt;/span> solid &lt;span class="number">#F33&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">margin&lt;/span>: &lt;span class="number">15px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">padding&lt;/span>: &lt;span class="number">0px&lt;/span> &lt;span class="number">10px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="comment">/*定义段落文本的样式*/&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="selector-tag">p&lt;/span> &amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">background&lt;/span>: &lt;span class="number">#FCF&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">border&lt;/span>: &lt;span class="number">1px&lt;/span> dashed &lt;span class="number">#F33&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">padding&lt;/span>: &lt;span class="number">0px&lt;/span> &lt;span class="number">10px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;span class="tag">&amp;lt;/&lt;span class="name">style&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">style&lt;/span>&amp;gt;&lt;/span>&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="selector-class">.box01&lt;/span>, &lt;span class="selector-class">.box02&lt;/span>, &lt;span class="selector-class">.box03&lt;/span> &amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">float&lt;/span>: left;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;span class="tag">&amp;lt;/&lt;span class="name">style&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;/&lt;span class="name">head&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">body&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;father&amp;quot;&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;box01&amp;quot;&lt;/span>&amp;gt;&lt;/span>box01&lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;box02&amp;quot;&lt;/span>&amp;gt;&lt;/span>box02&lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;box03&amp;quot;&lt;/span>&amp;gt;&lt;/span>box03&lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">p&lt;/span>&amp;gt;&lt;/span>12&lt;span class="tag">&amp;lt;/&lt;span class="name">p&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="comment">&amp;lt;!--不定义float属性，float属性值都为其默认值 none--&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;/&lt;span class="name">body&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;/&lt;span class="name">html&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;center>&lt;img alt="元素的浮动" src="https://blog.mkacg.com/rss/%E5%85%83%E7%B4%A0%E7%9A%84%E6%B5%AE%E5%8A%A8.png" />&lt;/center>&lt;p>运行后，box01、box02、box03 及段落文本从上到下一一罗列。 可见如果不对元素设置浮动，则该元素及其内部的子元素将按照标准文档流的样式显示，即块元素占据页面整行。&lt;/p>&lt;p>接下来以 box01 为设置对象，对其应用左浮动样式，在 head 的 style 标签下添加新的 style 标签，具体CSS代码如下：&lt;/p>&lt;figure class="highlight css">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="selector-class">.box01&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attribute">float&lt;/span>: left;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>可以看到 box01 已经脱离了文档流，接下来为 box02 和 box03 设置左浮动：&lt;/p>&lt;figure class="highlight css">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="selector-class">.box02&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attribute">float&lt;/span>: left;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="selector-class">.box03&lt;/span> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attribute">float&lt;/span>: left;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;center>&lt;img alt="元素的浮动" src="https://blog.mkacg.com/rss/%E5%85%83%E7%B4%A0%E7%9A%84%E6%B5%AE%E5%8A%A8_1.png" />&lt;/center>&lt;p>上述代码运行后，box01、 box02、 box03 三个盒子排列在同一行，同时，周围的段落文本将环绕盒子，出现了图文混排的网页效果。&lt;/p>&lt;h3 id="CSS-浮动清除">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#CSS-浮动清除" title="CSS 浮动清除">&lt;/a>CSS 浮动清除&lt;/h3>&lt;p>由于浮动元素不占用原文档流的位置，使用浮动时会影响后面相邻的固定元素。&lt;/p>&lt;p>CSS 提供了 clear 属性，可以清除掉浮动元素。&lt;/p>&lt;p>&lt;code>clear&lt;/code> 有三个属性值：&lt;/p>&lt;p>&lt;code>left&lt;/code> ：不允许左侧有浮动元素(清除左侧浮动的影响)&lt;/p>&lt;p>&lt;code>right&lt;/code> ：不允许右侧有浮动元素(清除右侧浮动的影响)&lt;/p>&lt;p>&lt;code>both&lt;/code> ：同时清除左右两侧浮动的影响&lt;/p>&lt;p>浮动清除存在一个问题，浮动清除本质上处理的是左右元素，如果父元素内所有的元素都浮动了，此时因为文档流中没有其他元素可以在内部撑起父元素的高度，父元素此时会坍缩为 0 高度。&lt;/p>&lt;h4 id="方法一：使用空标记清除浮动">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#方法一：使用空标记清除浮动" title="方法一：使用空标记清除浮动">&lt;/a>&lt;strong>方法一&lt;/strong>：使用空标记清除浮动&lt;/h4>&lt;p>在浮动元素之后添加空标记，并对该标记应用 &lt;code>clear:both&lt;/code> 样式，可清除元素浮动所产生的影响，这个空标记可以为 &lt;code>&amp;lt;div&amp;gt;&lt;/code> 、 &lt;code>&amp;lt;p&amp;gt;&lt;/code> 、 &lt;code>&amp;lt;hr /&amp;gt;&lt;/code> 等任何标记。&lt;/p>&lt;p>需要注意的是，上述方法虽然可以清除浮动， 但是在无形中增加了毫无意义的结构元素(空标记)，因此在实际工作中不建议使用。&lt;/p>&lt;p>(空标记)因此在实际工作中不建议使用。&lt;/p>&lt;figure class="highlight html">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="meta">&amp;lt;!doctype &lt;span class="keyword">html&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">html&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">head&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">meta&lt;/span> &lt;span class="attr">charset&lt;/span>=&lt;span class="string">&amp;quot;utf-8&amp;quot;&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">title&lt;/span>&amp;gt;&lt;/span>空标记清除浮动&lt;span class="tag">&amp;lt;/&lt;span class="name">title&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">style&lt;/span> &lt;span class="attr">type&lt;/span>=&lt;span class="string">&amp;quot;text/css&amp;quot;&lt;/span>&amp;gt;&lt;/span>&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="comment">/*没有给父元素定义高度*/&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="selector-class">.father&lt;/span> &amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">background&lt;/span>: &lt;span class="number">#ccc&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">border&lt;/span>: lpx dashed &lt;span class="number">#999&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="comment">/*定义box01、box02、box03三个盒子左浮动*/&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="selector-class">.box01&lt;/span>, &lt;span class="selector-class">.box02&lt;/span>, &lt;span class="selector-class">.box03&lt;/span> &amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">height&lt;/span>: &lt;span class="number">50px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">line-height&lt;/span>: &lt;span class="number">50px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">background&lt;/span>: &lt;span class="number">#f9c&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">border&lt;/span>:&lt;span class="number">1px&lt;/span> dashed &lt;span class="number">999&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">margin&lt;/span>: &lt;span class="number">15px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">padding&lt;/span>: &lt;span class="number">0px&lt;/span> &lt;span class="number">10px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">float&lt;/span>: left;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="comment">/*对空标记应用clear:both;*/&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="selector-class">.box04&lt;/span> &amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">clear&lt;/span>: both;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;span class="tag">&amp;lt;/&lt;span class="name">style&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;/&lt;span class="name">head&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">body&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;father&amp;quot;&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;box01&amp;quot;&lt;/span>&amp;gt;&lt;/span>box01&lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;box02&amp;quot;&lt;/span>&amp;gt;&lt;/span>box02&lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;box03&amp;quot;&lt;/span>&amp;gt;&lt;/span>box03&lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;box04&amp;quot;&lt;/span>&amp;gt;&lt;/span>&lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="comment">&amp;lt;!--在浮动元素后添加空标记--&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;/&lt;span class="name">body&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;/&lt;span class="name">html&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;center>&lt;img alt="空标记清除浮动" src="https://blog.mkacg.com/rss/%E7%A9%BA%E6%A0%87%E8%AE%B0%E6%B8%85%E9%99%A4%E6%B5%AE%E5%8A%A8.png" />&lt;/center>&lt;h4 id="方法二：使用-overflow-属性清除浮动">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#方法二：使用-overflow-属性清除浮动" title="方法二：使用 overflow 属性清除浮动">&lt;/a>&lt;strong>方法二&lt;/strong>：使用 overflow 属性清除浮动&lt;/h4>&lt;p>对元素应用 &lt;code>overflow: hidden;&lt;/code> 也可以清除浮动对该元素的影响，该方法弥补了空标记清除浮动的不足。&lt;/p>&lt;p>对父元素应用 &lt;code>overflow: hidden;&lt;/code> 样式来清除子元素浮动对父元素的影响。&lt;/p>&lt;p>父元素又被其子元素撑开了，即子元素浮动对父元素的影响已经不存在。&lt;/p>&lt;figure class="highlight html">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="meta">&amp;lt;!doctype &lt;span class="keyword">html&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">html&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">head&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">meta&lt;/span> &lt;span class="attr">charset&lt;/span>=&lt;span class="string">&amp;quot;utf-8&amp;quot;&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">title&lt;/span>&amp;gt;&lt;/span>overflow属性清除浮动&lt;span class="tag">&amp;lt;/&lt;span class="name">title&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">style&lt;/span> &lt;span class="attr">type&lt;/span>=&lt;span class="string">&amp;quot;text/css&amp;quot;&lt;/span>&amp;gt;&lt;/span>&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="comment">/*没有给父元素定义高度*/&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="selector-class">.father&lt;/span> &amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">background&lt;/span>: &lt;span class="number">#ccc&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">border&lt;/span>: &lt;span class="number">1px&lt;/span> dashed &lt;span class="number">#999&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="comment">/*对父元素应用overflow:hidden;*/&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">overflow&lt;/span>: hidden;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="comment">/*定义box01、box02、box03三个盒子左浮动*/&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="selector-class">.box01&lt;/span>, &lt;span class="selector-class">.box02&lt;/span>, &lt;span class="selector-class">.box03&lt;/span> &amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">height&lt;/span>: &lt;span class="number">50px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">line-height&lt;/span>: &lt;span class="number">50px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">background&lt;/span>: &lt;span class="number">#f9c&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">border&lt;/span>: &lt;span class="number">1px&lt;/span> dashed &lt;span class="number">#999&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">margin&lt;/span>: &lt;span class="number">15px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">padding&lt;/span>: &lt;span class="number">0px&lt;/span> &lt;span class="number">10px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">float&lt;/span>: left;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;span class="tag">&amp;lt;/&lt;span class="name">style&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;/&lt;span class="name">head&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">body&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;father&amp;quot;&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;box01&amp;quot;&lt;/span>&amp;gt;&lt;/span>box01&lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;box02&amp;quot;&lt;/span>&amp;gt;&lt;/span>box02&lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;box03&amp;quot;&lt;/span>&amp;gt;&lt;/span>box03&lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;/&lt;span class="name">body&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;/&lt;span class="name">html&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;center>&lt;img alt="overflow 清除浮动" src="https://blog.mkacg.com/rss/%E7%A9%BA%E6%A0%87%E8%AE%B0%E6%B8%85%E9%99%A4%E6%B5%AE%E5%8A%A8.png" />&lt;/center>&lt;h4 id="方法三-使用-after-伪对象清除浮动">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#方法三-使用-after-伪对象清除浮动" title="方法三: 使用 after 伪对象清除浮动">&lt;/a>&lt;strong>方法三&lt;/strong>: 使用 after 伪对象清除浮动&lt;/h4>&lt;p>使用 &lt;code>after&lt;/code> 伪对象也可以清除浮动，但是该方法只适用于 IE8 及以上版本浏览器和其他非 IE 浏览器。&lt;/p>&lt;p>另外，使用 &lt;code>after&lt;/code> 伪对象清除浮动时需要注意：&lt;/p>&lt;p>(1) 必须为需要清除浮动的元素伪对象设置 &lt;code>height: ();&lt;/code> 样式，否则该元素会比其实际高度高出若干像素。&lt;/p>&lt;p>(2) 必须在伪对象中设置 &lt;code>content&lt;/code> 属性，属性值可以为空，如 &lt;code>content：&amp;quot; &amp;quot;;&lt;/code> 。&lt;/p>&lt;figure class="highlight html">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="meta">&amp;lt;!doctype &lt;span class="keyword">html&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">html&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">head&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">meta&lt;/span> &lt;span class="attr">charset&lt;/span>=&lt;span class="string">&amp;quot;utf-8&amp;quot;&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">title&lt;/span>&amp;gt;&lt;/span>使用after伪对象清除浮动&lt;span class="tag">&amp;lt;/&lt;span class="name">title&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">style&lt;/span> &lt;span class="attr">type&lt;/span>=&lt;span class="string">&amp;quot;text/css&amp;quot;&lt;/span>&amp;gt;&lt;/span>&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="comment">/*没有给父元素定义高度*/&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="selector-class">.father&lt;/span> &amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="attribute">background&lt;/span>: &lt;span class="number">#ccc&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="attribute">border&lt;/span>: &lt;span class="number">1px&lt;/span> dashed &lt;span class="number">#999&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="comment">/*对父元素应用after伪对象样式*/&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="selector-class">.father&lt;/span>&lt;span class="selector-pseudo">:after&lt;/span> &amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">display&lt;/span>: block;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">clear&lt;/span>: both;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">content&lt;/span>:&lt;span class="string">&amp;quot; &amp;quot;&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">visibility&lt;/span>: hidden;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">height&lt;/span>: &lt;span class="number">0&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="comment">/*定义box01、box02、box03三个盒子左浮动*/&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;span class="selector-class">.box01&lt;/span>, &lt;span class="selector-class">.box02&lt;/span>, &lt;span class="selector-class">.box03&lt;/span> &amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">height&lt;/span>: &lt;span class="number">50px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">line-height&lt;/span>: &lt;span class="number">50px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">background&lt;/span>: &lt;span class="number">#f9c&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">border&lt;/span>: &lt;span class="number">1px&lt;/span> dashed &lt;span class="number">#999&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">margin&lt;/span>: &lt;span class="number">15px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">padding&lt;/span>: &lt;span class="number">0px&lt;/span> &lt;span class="number">10px&lt;/span>;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css"> &lt;span class="attribute">float&lt;/span>: left;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="language-css">&lt;/span>&lt;span class="tag">&amp;lt;/&lt;span class="name">style&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;/&lt;span class="name">head&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;&lt;span class="name">body&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;father&amp;quot;&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;box01&amp;quot;&lt;/span>&amp;gt;&lt;/span>box01&lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;box02&amp;quot;&lt;/span>&amp;gt;&lt;/span>box02&lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;&lt;span class="name">div&lt;/span> &lt;span class="attr">class&lt;/span>=&lt;span class="string">&amp;quot;box03&amp;quot;&lt;/span>&amp;gt;&lt;/span>box03&lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="tag">&amp;lt;/&lt;span class="name">div&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;/&lt;span class="name">body&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="tag">&amp;lt;/&lt;span class="name">html&lt;/span>&amp;gt;&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;center>&lt;img alt="使用after伪对象清除浮动" src="https://blog.mkacg.com/rss/%E7%A9%BA%E6%A0%87%E8%AE%B0%E6%B8%85%E9%99%A4%E6%B5%AE%E5%8A%A8.png" />&lt;/center></description><author>justforlxz</author></item><item><title>翻译：记 2019 年的 X.org 开发者研讨会 - emersion</title><link>https://blog.deepin.org/posts/xdc2019-wrap-up/</link><pubDate>Thu, 11 Mar 2021 03:03:00 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/xdc2019-wrap-up/</guid><description>&lt;p>原文：https://emersion.fr/blog/2019/xdc2019-wrap-up&lt;/p>
&lt;p>&amp;gt; XDC 2019 wrap-up 2019-10-10&lt;/p>
&lt;p>这篇文章是我从加拿大蒙特利尔飞往赫尔辛基的路上写的，我刚参加完 X.Org 的开发者研讨会。从去年西班牙那次会议以来，这是我第二次参加 XDC（译者注：X.Org Developer’s Conference）。&lt;/p>
&lt;p>今年的研讨会非常的给力。我遇到了很多一起工作过的同志，他们来自四面八方的各个组织和项目。会议内容也很牛，我会尝试总结一下这次讨论的一些事情。&lt;/p>
&lt;h1 id="libliftoff">libliftoff
&lt;span>
&lt;a href="https://blog.deepin.org/posts/xdc2019-wrap-up/#libliftoff">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;p>前一阵子，我搞了一个新项目，名字叫 &lt;a href="https://github.com/emersion/libliftoff">libliftoff&lt;/a>。它是我在这次会议上的亮点，所以接下来我会详细的介绍它。带着问题往下看：“libliftoff 是用来解决什么问题的呢？”&lt;/p>
&lt;h1 id="libliftoff-是什么">libliftoff 是什么
&lt;span>
&lt;a href="https://blog.deepin.org/posts/xdc2019-wrap-up/#libliftoff-%e6%98%af%e4%bb%80%e4%b9%88">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;p>它是一个合成器的中间层，负责从 clients 中获取 buffers，把它们绘制在一个独立的 buffer 上，并把这个 buffer 显示到屏幕上。假设我现在打开了一个“文本编辑器”和一个“终端”，合成器会把它们的窗口 buffer 复制到屏幕的 buffer 上，一般是用 OpenGL 做这个事。&lt;/p>
&lt;p>不过，复制 buffers 的操作很浪费资源，因为它会做很多事情，比如：它总是进行 alpha 混合、在不同格式之间转换、让渲染引擎一直干活（注：GPU 在执行 OpenGL/Vulkan 的命令）等等，这些操作不仅占用时间，而且也会增加耗电量。&lt;/p>
&lt;p>为了改善性能，许多 GPU 都提供了硬件实现的“叠加平面（planes）”支持（下文直接称为“叠加平面”）。“叠加平面”可以作为显示引擎负责合成工作，这种行为称为&lt;code>直接显示&lt;/code>，可以避免全让合成器做复制 buffers 的活。&lt;/p>
&lt;p>在 Android 上，硬件“叠加平面”在“Hardware Composer”组件中用的很广。许多 Wayland 合成器也会为光标使用“叠加平面”，但是用在其它场景的情况还是很少见。Weston（译者注：一个 Wayland 合成器） 是仅有的使用“叠加平面”的合成器之一。&lt;/p>
&lt;p>使用“叠加平面”并不是一件容易的事情，它有一些限制，比如：不支持 Alpha 通道、不是任意的 buffer 都可以用。虽然这些限制往往会让人觉得很扯淡，但它是由硬件特性决定的。举个例子：对于某些格式的 buffers，Intel 的硬件只能将其放在“叠加平面”的偶数坐标位置、某些 buffers 是在内存上分配的，“叠加平面”干脆就不支持这一类的 buffer、另外，显示设备一般都有带宽限制，在“叠加平面”上使用过大的 buffer 时会失败、某些 ARM 设备上，多个“叠加平面”之间不支持重叠。&lt;/p>
&lt;p>目前为止，关于硬件的这些限制信息，合成器程序还查询不了，因为硬件之间的差异性很大，所以很难设计一个能查询硬件限制信息的 API，甚至在某些新出 GPU 上还会有更奇怪的限制。唯一的方法是搞一些前置工作，用它做一些尝试性的使用。&lt;/p>
&lt;p>因此，设计和实现一个能有效的使用“叠加平面”的代码是一个相当复杂的事情。我一直在想，共享这些代码，是不是会对合成器使用“叠加平面”有所帮助，于是我就奔着这个目标设计了 libliftoff。&lt;/p>
&lt;h1 id="libliftoff-的研讨会">libliftoff 的研讨会
&lt;span>
&lt;a href="https://blog.deepin.org/posts/xdc2019-wrap-up/#libliftoff-%e7%9a%84%e7%a0%94%e8%ae%a8%e4%bc%9a">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;p>&lt;img alt="图一" src="https://sr.ht/Fd_h.jpg" />&lt;/p>
&lt;p>我在 XDC 搞了一个&lt;a href="https://xdc2019.x.org/event/5/contributions/583/">研讨会&lt;/a>，讨论关于 libliftoff 的事情，目的是把合成器和驱动专家凑一块，一起商量如何才让合成器有效的用上“叠加平面”。&lt;/p>
&lt;p>libliftoff 的受欢迎程度让我很惊讶，连桌子附近都坐满了人：&lt;/p>
&lt;ul>
&lt;li>在合成器这边，有 wlroots 团队的人（Drew DeVault, Scott Anderson 和我自己），KDE 的 Roman Gilg，Weston 的 Daniel Stone，还有 X server 的 Keith Packard。&lt;/li>
&lt;li>在驱动这边，有很多来自于不同厂商的开发者：有 AMD、Arm、Google、Nvidia、Qualcomm 等等。&lt;/li>
&lt;/ul>
&lt;p>大家貌似都很兴奋，为 libliftoff 提了很多宝贵的意见。我在此感谢所有参与研讨会的人！&lt;/p>
&lt;p>短期的计划是，让 libliftoff 为合成器提供能实际使用的功能（实验性的），不过有个事我还需要弄清楚，那就是应该怎么样支持多个 output：因为每个 output 都有自己的时序，所以，如何将“叠加平面”从一个 output 迁移到另一个 output 是一件很难搞的问题。最终，如果能实现这样的功能就完美了：为每一个图层分配一个优先级，并把那些频繁在别的图层之前更新的图层放在同一个“叠加平面”上。&lt;/p>
&lt;p>另外也讨论了一些长期计划。&lt;/p>
&lt;p>首先，我们想搞定一个跟内存相关的问题：clients 往往会在内存上分配 buffer，这些 buffer 没法直接用在“叠加平面”上。通常，合成器都会给一些提示信息：“你现在正在使用 &lt;code>Y_TILED&lt;/code>，所以我不能将你用在“叠加平面”上，但是如果你能使用 &lt;code>X_TILED&lt;/code> 我就能把你用在“叠加平面”上”，这样 client 就可以决定切换它的 buffer 格式。我大概在一年前给 Wayland 提了一个&lt;a href="https://patchwork.freedesktop.org/series/52370/">补丁&lt;/a>，这个补丁新增的信息能被 libliftoff 识别出来，在这种情况下，合成器可以把这个信息转发给 clients。&lt;/p>
&lt;p>接下来是关于内核 API 的问题，现在只有一个办法能让我们知道是否可以使用“叠加平面”，那就是用 atomic 相关的 test 接口测试（译者注：atomic 是 DRM 提供的一系列原子操作接口，支持事务），这个做法相当的不清真，因为我们必须得遍历尝试多个组合条件，基本上是暴力求解。除此之外，比较好的做法都要跟具体的硬件相关。&lt;/p>
&lt;p>为了搞定这个问题，我们必须让内核提供更多的信息，但是因为不同硬件的差异性很大，设计一个足够通用的 API 将是一个很蛋疼的事情，另一个解决方案是在 libliftoff 中添加特定厂商的插件，允许每个驱动添加自己的代码，以此更好的匹配自己的硬件。目前来说，貌似这是最好的方案了。&lt;/p>
&lt;p>这是本次讨论的总结(&lt;a href="https://fs.emersion.fr/protected/presentations/present.html?src=libliftoff-xdc2019/index.md">PPT&lt;/a>)：&lt;/p>
&lt;p>&lt;a href="https://youtu.be/JIry8jpbPUY">视频：XDC 2019 - Day 3&lt;/a>&lt;/p>
&lt;p>Scott Anderson 也写了一些&lt;a href="https://lists.freedesktop.org/archives/wayland-devel/2019-October/040924.html">总结&lt;/a>，是关于 Wayland 协议的一些想法的详细介绍。&lt;/p>
&lt;h1 id="分配器">分配器
&lt;span>
&lt;a href="https://blog.deepin.org/posts/xdc2019-wrap-up/#%e5%88%86%e9%85%8d%e5%99%a8">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;p>在三年前的 XDC 上，Nvidia 的 James Jones 提出了这个&lt;a href="https://www.x.org/wiki/Events/XDC2016/Program/jones_unix_device_mem_alloc/">分配器项目&lt;/a>，用于解决 GBM/EGLStreams 现有的问题。今年，他做了一个关于 GBM、Nouveau 和 transitions 的&lt;a href="https://xdc2019.x.org/event/5/contributions/335/">新的分享&lt;/a>。跟之前的提案相比，这个提案的目的是在现有 API 的基础上建立一种叫 transition 的机制。&lt;/p>
&lt;p>&lt;a href="https://youtu.be/HYa4UvVtMOE">视频：XDC 2019 - Day 2&lt;/a>&lt;/p>
&lt;p>transition 可以用来减少渲染引擎的带宽占用。某些 GPU 支持使用压缩的 buffer，与未压缩的 buffer 相比，GPU 可以在这些压缩后的 buffer 上直接执行 OpenGL/Vulkan 的操作，这样做效率很高。所以，我们应该在渲染时使用压缩后的 buffers。&lt;/p>
&lt;p>不过，压缩后的 buffers 不能直接用在“叠加平面”上，需要先将它们解压才能使用，有一个方法是把它解压为一个新的 buffer，但是这样需要复制数据，速度会很慢。&lt;/p>
&lt;p>在某些 GPU 上有个更好的方法，它们支持在“叠加平面”中直接解压 buffer。这指的是：当一个 Wayland client 在压缩后的 buffer 上渲染，如果这个 buffer 可以在“叠加平面”中解压，并且之后可以直接使用它，那我们就将这种“buffer 就地解压的程序”称为 transition。&lt;/p>
&lt;p>之后我们在一个研讨会中继续探讨 transitions，讨论了应该在何时何地进行 transition 操作。想法一是：如果合成器准备把一个 buffer 用到“叠加平面”中，那就在合成器中做这个事，可以用 libliftoff 判断什么时候应该使用 transition。另一个想法是：在 client 把 buffer 提交给合成器之前做这个事，client 需要从合成器那获取一个信息，以便知道应该什么时候执行操作（这就是我之前提到的那个 &lt;a href="https://patchwork.freedesktop.org/series/52370/">Wayland 协议的补丁&lt;/a>可以做的事情）。&lt;/p>
&lt;p>这是这次研讨会的总结：&lt;/p>
&lt;p>&lt;a href="https://youtu.be/JIry8jpbPUY">视频：XDC 2019 - Day 3&lt;/a>&lt;/p>
&lt;h1 id="可变刷新率vrr">可变刷新率（VRR）
&lt;span>
&lt;a href="https://blog.deepin.org/posts/xdc2019-wrap-up/#%e5%8f%af%e5%8f%98%e5%88%b7%e6%96%b0%e7%8e%87vrr">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;p>AMD 的 Harry Wentland 搞了一个关于“自适应同步”（DisplayPort 的技术）、“可变刷新率”（HDMI 的技术）和“FreeSync”（AMD 的技术）的研讨会。所有这些技术都是为了适配屏幕的功能，允许它从“固定为 60Hz”的模式变成“可以稍微等待下一帧”的模式（译者注：动态刷新率的模式）。&lt;/p>
&lt;p>&lt;a href="https://youtu.be/HYa4UvVtMOE">视频：XDC 2019 - Day 2&lt;/a>&lt;/p>
&lt;p>这东西的主要使用场景是游戏应用。游戏通常是用动态刷新率的方式更新画面（依赖于场景的复杂度），下一帧可以更快也可以更慢。游戏还希望能降低延迟，即避免渲染到在屏幕上显示的时间差过长。&lt;/p>
&lt;p>在屏幕固定刷新率的情况下，如果有一帧的渲染用时稍微长了一点，它错过了显示器的刷新时机，这样就会导致这一帧的显示有些滞后，而 VRR 则允许增加屏幕刷新时的等待时间，以免丢帧。&lt;/p>
&lt;p>另一种场景是视频播放。视频有固定的帧率，但是通常会与屏幕的刷新率不同，视频播放器需要用帧插值的方式，才能适应屏幕的刷新率，而 VRR 则允许降低刷新率以匹配能完美播放视频的时序。&lt;/p>
&lt;p>屏幕内容通常都是处于静止状态，在这种情况下，VRR 还可以降低电量消耗。比如用户在使用文本编辑器时，完全不需要 60FPS，这时候合成器可以降低屏幕的刷新率。&lt;/p>
&lt;p>游戏的使用场景比较简单，合成器可以比 deadline 稍微晚一点点提交帧，硬件可以搞定这种情况。其它场景需要做更多的工作，要想把 VRR 用在视频播放器上，我们需要某种定时提交帧的 API。要想用 VRR 做节能，合成器要能支持帧率改变时的平滑过渡，否则屏幕会有闪烁问题（这是支持 VRR 功能的屏幕的限制）。&lt;/p>
&lt;p>在我看来，我们目前应该把精力放在 Wayland 协议对游戏场景的支持上，因为搞其它两种场景需要做的事情太多了，它们不仅需要更多的新 API（内核和 Wayland 的都需要），还需要做很多的实验工作。&lt;/p>
&lt;h1 id="chamelium">Chamelium
&lt;span>
&lt;a href="https://blog.deepin.org/posts/xdc2019-wrap-up/#chamelium">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h1>&lt;p>我在 Intel 实习期间，参与了一个叫 Chamelium 的项目，它是一个基础的屏幕模拟器，你可以通过网络向它发送命令，它已经被用在了 ChromeOS 的 i915 显卡环境的 CI 服务中。&lt;/p>
&lt;p>我搞了一个关于这个项目以及我在项目内的工作的研讨会：&lt;/p>
&lt;p>&lt;a href="https://youtu.be/JIry8jpbPUY">视频：XDC 2019 - Day 3&lt;/a>&lt;/p>
&lt;p>除了这三个主题，我们还讨论了很多其它的东西，但是不能全塞到这一篇博客中讲，所以就说到这吧。感谢所有参加 XDC 的人，感谢 Mark Filion 组织了这次活动，还要感谢所有赞助商，没有你们的赞助就不会有的这次活动！&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>如何给Arch打一个包</title><link>https://blog.mkacg.com/2021/03/04/package-manager-for-arch/</link><pubDate>Thu, 04 Mar 2021 03:23:42 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2021/03/04/package-manager-for-arch/</guid><description>&lt;p>Arch 使用的是 pacman 包管理器，包格式是 tar.zst。Arch 提供了一些工具用于创建 tar.zst 包，首先需要安装 base-devel 包和 devtools 包。&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">pacman -S base-devel devtools&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>Arch 的打包流程是这样的，要先写一个 PKGBUILD 文件，这个文件描述了构建一个包所需的全部信息，如从哪里下载源码，依赖有哪些，构建的版本是多少，如何进行构建等。&lt;/p>&lt;h1 id="PKGBUILD">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#PKGBUILD" title="PKGBUILD">&lt;/a>PKGBUILD&lt;/h1>&lt;p>一个基本的 PKGBUILD 格式如下：&lt;/p>&lt;figure class="highlight text">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line"># Maintainer: justforlxz &amp;lt;justforlxz@gmail.com&amp;gt;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">pkgname=dtkcore-git&lt;/span>&lt;br />&lt;span class="line">pkgver=5.4.0.r1.ge7e7a99&lt;/span>&lt;br />&lt;span class="line">pkgrel=1&lt;/span>&lt;br />&lt;span class="line">pkgdesc=&amp;#x27;DTK core modules&amp;#x27;&lt;/span>&lt;br />&lt;span class="line">arch=(&amp;#x27;x86_64&amp;#x27;)&lt;/span>&lt;br />&lt;span class="line">url=&amp;quot;https://github.com/linuxdeepin/dtkcore&amp;quot;&lt;/span>&lt;br />&lt;span class="line">license=(&amp;#x27;LGPL3&amp;#x27;)&lt;/span>&lt;br />&lt;span class="line">depends=(&amp;#x27;dconf&amp;#x27; &amp;#x27;deepin-desktop-base-git&amp;#x27; &amp;#x27;python&amp;#x27; &amp;#x27;gsettings-qt&amp;#x27; &amp;#x27;lshw&amp;#x27;)&lt;/span>&lt;br />&lt;span class="line">makedepends=(&amp;#x27;git&amp;#x27; &amp;#x27;qt5-tools&amp;#x27; &amp;#x27;gtest&amp;#x27;)&lt;/span>&lt;br />&lt;span class="line">conflicts=(&amp;#x27;dtkcore&amp;#x27;)&lt;/span>&lt;br />&lt;span class="line">provides=(&amp;#x27;dtkcore&amp;#x27;)&lt;/span>&lt;br />&lt;span class="line">groups=(&amp;#x27;deepin-git&amp;#x27;)&lt;/span>&lt;br />&lt;span class="line">source=(&amp;quot;$pkgname::git://github.com/linuxdeepin/dtkcore.git&amp;quot;)&lt;/span>&lt;br />&lt;span class="line">sha512sums=(&amp;#x27;SKIP&amp;#x27;)&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">pkgver() &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> cd $pkgname&lt;/span>&lt;br />&lt;span class="line"> git describe --long --tags | sed &amp;#x27;s/\([^-]*-g\)/r\1/;s/-/./g&amp;#x27;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">build() &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> cd $pkgname&lt;/span>&lt;br />&lt;span class="line"> qmake-qt5 PREFIX=/usr DTK_VERSION=$pkgver LIB_INSTALL_DIR=/usr/lib&lt;/span>&lt;br />&lt;span class="line"> make&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">package() &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> cd $pkgname&lt;/span>&lt;br />&lt;span class="line"> make INSTALL_ROOT=&amp;quot;$pkgdir&amp;quot; install&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>以 dtkcore 为例，整个配置文件十分清晰明了，一看就知道构建工具是如何一步步制作出一个包的。&lt;/p>&lt;p>将配置文件保存到目录里，以 &lt;code>PKGBUILD&lt;/code> 命名，然后执行 &lt;code>makepkg&lt;/code>。&lt;/p>&lt;p>makepkg 是用于执行 PKGBUILD 文件的工具，它根据文件中的描述，进行包的构建。&lt;/p>&lt;h1 id="如何提交一个包到-aur">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#如何提交一个包到-aur" title="如何提交一个包到 aur">&lt;/a>如何提交一个包到 aur&lt;/h1>&lt;p>当你希望自己打的包可以被别人使用的时候，一般都会请求上传到官方仓库，但是进入官方仓库的流程异常繁琐，而且对贡献者的要求会比较高。Arch 提供了一个用户仓库，供用户自由分享配置文件，只需要下载配置文件，在本机打包就可以了。&lt;/p>&lt;p>根据 aur 的 &lt;a href="https://wiki.archlinux.org/index.php/AUR_submission_guidelines">贡献指南&lt;/a>，我们可以很轻松的上传自己的配置文件，并借用yay等aur工具自动下载和构建软件包。&lt;/p>&lt;blockquote>&lt;p>用户可以通过 Arch User Repository 分享 PKGBUILD。AUR 中不包含任何二进制包，仅包含用户上传的 PKGBUILD，供其他用户下载使用。所有软件包都是非官方的，使用风险自担。&lt;/p>&lt;/blockquote>&lt;p>提交aur之前，需要先了解一下 aur 的一些要求，&lt;a href="https://wiki.archlinux.org/index.php/AUR_submission_guidelines#Rules_of_submission">Rules of submission&lt;/a> 提供了一些规则，只要我们按照规则来，就可以上传和维护我们自己的包。&lt;/p>&lt;p>首先需要去 &lt;a href="https://aur.archlinux.org/">aur官网&lt;/a> 注册一个账号，并上传自己的 ssh key 我们通过克隆一个新的仓库来作为上传的方式。&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">git clone ssh://aur@aur.archlinux.org/&amp;lt;pkgname&amp;gt;.git&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>以dtkcore为例，克隆的时候把pkgname改成dtkcore。&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">git clone ssh://aur@aur.archlinux.org/dtkcore.git&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>&lt;strong>如果是第一次创建，会提示你克隆的一个空仓库。&lt;/strong>&lt;/p>&lt;p>把 PKGBUILD 文件复制到克隆的目录，然后执行 &lt;code>makepkg --printsrcinfo &amp;gt; .SRCINFO&lt;/code> 创建一个软件包信息，将 &lt;code>.SRCINFO&lt;/code> 和 &lt;code>PKGBUILD&lt;/code> 文件都添加到 git 中，然后提交 commit 信息，推送到服务器。&lt;/p>&lt;blockquote>&lt;p>注意： 如果您忘记在首次提交中包含 .SRCINFO，您可以使用 rebasing with –root 或是 filtering the tree 使得 AUR 接受您的第一次推送&lt;/p>&lt;/blockquote>&lt;blockquote>&lt;p>提示： 为了保持工作目录和提交尽可能的干净，可以创建 gitignore 文件来排除所有文件，然后再按需添加文件。&lt;/p>&lt;/blockquote>&lt;p>参考资料：&lt;/p>&lt;blockquote>&lt;p>&lt;a href="https://wiki.archlinux.org/index.php/PKGBUILD">https://wiki.archlinux.org/index.php/PKGBUILD&lt;/a>&lt;/p>&lt;/blockquote></description><author>justforlxz</author></item><item><title>2020 Review</title><link>https://blog.mkacg.com/2021/02/26/2020review/</link><pubDate>Fri, 26 Feb 2021 14:51:45 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2021/02/26/2020review/</guid><description>&lt;h1 id="2020">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#2020" title="2020">&lt;/a>2020&lt;/h1>&lt;p>拖了这么久才更新博客，想必大家都在 2020 年过的挺不好的，年初武汉的疫情打了全国一个措不及防，甚至已经一整年了，全球疫情还是十分紧张。截止到 2021 年 2 月 26 日，全球每日新增仍然有30 多万，而且全球累计死亡已有 250 万人之多。&lt;/p>&lt;h1 id="疫情">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#疫情" title="疫情">&lt;/a>疫情&lt;/h1>&lt;p>生命是如此的脆弱。有些时候和朋友们一起吃饭，会说起一月份时大家是如何 &lt;code>逃离&lt;/code> 武汉的，在家隔离又多了哪些事情，我们活着是多么的不容易。虽然人类已经可以生产 5 纳米的芯片了，但是就如同我们对这个宇宙的认知一样，几百亿光年外任我们看，可太阳系内有什么我们都不知道，甚至在地球上还有很多我们未知的东西。&lt;/p>&lt;h1 id="计划失误">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#计划失误" title="计划失误">&lt;/a>计划失误&lt;/h1>&lt;p>2020 年我的计划几乎没有一件事是完成的，我计划应该一整年掌握 &lt;code>TypeScript&lt;/code> 和 &lt;code>web&lt;/code> 开发，并开发自己的blog系统，但是方案被我推到重来了三次，我就没耐心继续开发了，只完成了基本的 &lt;code>md&lt;/code> 文件编译和服务器渲染就暂停了。计划阅读 &lt;code>《1984》&lt;/code> 、 &lt;code>《TensorFlow》&lt;/code> 和 &lt;code>《TypeScript实战》&lt;/code> ，然而只有 &lt;code>《TypeScript实战》&lt;/code>阅读完了，剩余两本书我则是只看了一丢丢，看来只能放到 2021 年读完了。&lt;/p>&lt;h1 id="她">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#她" title="她">&lt;/a>她&lt;/h1>&lt;p>在 &lt;code>2019Review&lt;/code> 的文章里，我提到了我遇到了生命中的那个她，而 2020 年，我和她 &lt;code>结婚&lt;/code> 啦。&lt;/p>&lt;p>在家隔离的日子里，有她陪伴着我，虽然老家的人都挺敌视从武汉回来的我，但是大门一关我谁也看不到，眼不见心不烦，在家一块搓麻将挺好的，还一起练 (zao) 习 (ta)了厨艺，朋友圈做饭比赛第一名。&lt;/p>&lt;h1 id="学习">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#学习" title="学习">&lt;/a>学习&lt;/h1>&lt;p>虽然我读书拉后了，但是我的英语 &lt;code>坚持&lt;/code> 了下来，我使用多邻国进行英语和日语的学习，已经坚持快6个月了，需要继续加油努力，争取早日把学校里拉下的英语 &lt;code>补&lt;/code> 回来。&lt;/p>&lt;img alt="2020review" src="https://blog.mkacg.com/rss/Untitled.png" />&lt;h1 id="工作">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#工作" title="工作">&lt;/a>工作&lt;/h1>&lt;p>2020 年最大的收益可能是创建了 &lt;code>deepin-git&lt;/code> 的仓库，提供了 &lt;code>dde&lt;/code> 所有组件在 &lt;code>arch&lt;/code> 上的git版，并且还成功申请到了 &lt;code>archlinuxcn&lt;/code> 的贡献者。&lt;/p>&lt;h1 id="文章">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#文章" title="文章">&lt;/a>文章&lt;/h1>&lt;p>2020 年一共水了18篇文章。&lt;/p>&lt;figure class="highlight jsx">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="number">12&lt;/span>-&lt;span class="number">23&lt;/span> ccls&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">11&lt;/span>-&lt;span class="number">17&lt;/span> &lt;span class="title class_">Qt&lt;/span>多线程&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">09&lt;/span>-&lt;span class="number">17&lt;/span> deepin-wine中文乱码&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">09&lt;/span>-&lt;span class="number">08&lt;/span> 使用rEFInd来安全启动系统&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">09&lt;/span>-&lt;span class="number">06&lt;/span> deepin git version&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">09&lt;/span>-&lt;span class="number">03&lt;/span> 使用&lt;span class="title class_">VSCode&lt;/span>远程开发&lt;span class="variable constant_">DDE&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">08&lt;/span>-&lt;span class="number">06&lt;/span> 在&lt;span class="title class_">ArchLinux&lt;/span>上开发startdde&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">07&lt;/span>-&lt;span class="number">27&lt;/span> use github action to check dde-launcher&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">07&lt;/span>-&lt;span class="number">21&lt;/span> 使用perf工具分析程序性能&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">06&lt;/span>-&lt;span class="number">16&lt;/span> &lt;span class="title class_">CTest&lt;/span> &amp;amp; &lt;span class="title class_">QTest&lt;/span>/&lt;span class="title class_">GTest&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">06&lt;/span>-&lt;span class="number">15&lt;/span> &lt;span class="variable constant_">CPP&lt;/span>项目的一些坑&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">06&lt;/span>-&lt;span class="number">15&lt;/span> 使用inquirer提供交互式git commit&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">06&lt;/span>-&lt;span class="number">01&lt;/span> vue-router路由复用后页面没有刷新&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">05&lt;/span>-&lt;span class="number">31&lt;/span> vue3升级遇到的坑&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">02&lt;/span>-&lt;span class="number">01&lt;/span> &lt;span class="title class_">JavaScript&lt;/span>建造者模式&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">01&lt;/span>-&lt;span class="number">31&lt;/span> 浅谈&lt;span class="title class_">Javascript&lt;/span>构造器模式&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">01&lt;/span>-&lt;span class="number">01&lt;/span> &lt;span class="number">2019&lt;/span> &lt;span class="title class_">Review&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="number">01&lt;/span>-&lt;span class="number">01&lt;/span> 使用伪元素创建一个圆点&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>研究了 &lt;code>vscode&lt;/code> 的插件，开发了一个 &lt;code>qmake&lt;/code> 的插件原型，可以打开 &lt;code>dtk&lt;/code> 等 &lt;code>qt pro&lt;/code> 的项目，利用 &lt;code>bear&lt;/code> 拦截 &lt;code>make&lt;/code> 的编译信息，生成 &lt;code>ccls&lt;/code> 需要的数据库，这样就可以为项目提供自动补全和语法检查等功能了。&lt;/p>&lt;p>写了一些关于远程开发的文章，也是利用 &lt;code>vscode&lt;/code> 的远程功能，不得不说 &lt;code>vscode&lt;/code> 真的可以说是宇宙第一编辑器了，让老牌的 &lt;code>vim&lt;/code> 和 &lt;code>emacs&lt;/code> 知道了什么叫易用性的力量。&lt;/p>&lt;h1 id="展望">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#展望" title="展望">&lt;/a>展望&lt;/h1>&lt;p>2021 年，不能松懈，今年要继续努力学习，努力提升自己。&lt;/p>&lt;p>在 2021 年里，我将会负责 &lt;code>deepin&lt;/code> 的社区，由于前两年公司需要快速扩张，导致忽略了社区，现在我们的首要任务就是恢复社区，并让社区良性的自由发展，社区是我们的根本。&lt;/p>&lt;p>挖了很多坑，今年争取都填一下。&lt;/p>&lt;h1 id="其他">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#其他" title="其他">&lt;/a>其他&lt;/h1>&lt;p>又搬家了，找了一个一室一厅，开启了二人的小日子，过了年以后把狗子也带来了，现在天天早上起来打狗，晚上回来打狗。&lt;/p>&lt;p>我又给 &lt;code>肝疼3&lt;/code> 氪了一个月卡，这次只忘了一天没登录，不然 &lt;code>30&lt;/code> 块大洋又要白花了。&lt;/p>&lt;img alt="2020review" src="https://blog.mkacg.com/rss/Untitled%201.png" />&lt;img alt="2020review" src="https://blog.mkacg.com/rss/Untitled%202.png" />&lt;p>祝大家幸福安康，新年快乐！(&lt;em>^▽^&lt;/em>)&lt;/p></description><author>justforlxz</author></item><item><title>deepin-wine中文乱码</title><link>https://blog.mkacg.com/2020/09/17/deepin-wine-chinese-problem/</link><pubDate>Thu, 17 Sep 2020 08:32:12 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2020/09/17/deepin-wine-chinese-problem/</guid><description>&lt;p>众所周知，使用wine来运行windows下的一些软件是linux用户的常用操作，deepin为社区贡献了好几款中国用户必备的软件，例如QQ、微信、企业微信，以此来让更多的人无痛的切换到linux来。近年来value也一直在linux上布局，先后推出了steam主机和proton,前者是基于kvm和steam大屏幕模式的系统，而proton则是wine的分支，value提供了几组补丁用来提高性能和与Windows游戏的兼容性。&lt;/p>&lt;p>Arch上有非常方便的aur仓库，有很多用户都自己提交一些软件到aur上面，就有几个维护者将deepin打包的deepin wine和对应的软件包移植到Arch上面来，已经是很多人在Arch上面运行QQ和微信的首选方案。&lt;/p>&lt;p>但是使用的过程中我遇到了以下几个问题：&lt;/p>&lt;ul>&lt;li>输入框中文乱码&lt;/li>&lt;li>在DDE桌面使用kwin的情况下最小化会卡死&lt;/li>&lt;li>KDE桌面环境无法使用deepin wine的程序&lt;/li>&lt;/ul>&lt;p>第二个问题比较特殊，因为dde在deepin和uos下运行的是fork版本的kwin,而Arch上运行的则是原版的kwin,一些操作代码并不具备，所以会出现一些奇葩的状况。&lt;/p>&lt;p>第三个问题则是xsettings的原因，在移植者的仓库有对应的issue讨论 &lt;a href="https://github.com/wszqkzqk/deepin-wine-ubuntu/issues/12">《KDE环境完全无法使用wine-tim》&lt;/a>&lt;/p>&lt;p>第一个问题解决起来也比较简单，是因为缺少了宋体文件，从而无法正确的渲染中文字体。而比较奇葩的是，把方块复制再粘贴，就可以正确的渲染了。&lt;/p>&lt;p>解决的方法也很简单，把windows的的字体复制过来就可以了。由于版权的问题，没办法直接提供文件，需要各位自己复制了。&lt;/p>&lt;blockquote>&lt;p>引用资料&lt;/p>&lt;/blockquote>&lt;p>&lt;a href="https://github.com/wszqkzqk/deepin-wine-ubuntu/issues/136#issuecomment-517576733">https://github.com/wszqkzqk/deepin-wine-ubuntu/issues/136&lt;/a>&lt;/p></description><author>justforlxz</author></item><item><title>使用rEFInd来安全启动系统</title><link>https://blog.mkacg.com/2020/09/08/use-refind-to-boot-system/</link><pubDate>Tue, 08 Sep 2020 11:54:26 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2020/09/08/use-refind-to-boot-system/</guid><description>&lt;p>今年的七夕，我老婆给我买了一台surface laptop 2代，8G内存 + 256G存储版本，我也成功的用上了田牌的机器。&lt;/p>&lt;span id="more">&lt;/span>&lt;blockquote>&lt;p>2020&amp;#x2f;09&amp;#x2f;17更新： 不知道为啥，反正是开了对内核签名以后，哪怕是BIOS关闭了安全启动，仍然出现mkinitcpio会卡在autodetect上，无奈全部都删掉重来了，没有弄签名，希望各位看到本文章以后解决了这个问题能回复一下，谢谢。&lt;/p>&lt;/blockquote>&lt;p>surface默认是开启了安全启动(Microsoft签名)和bitlocker来保障设备和系统安全，我作为一个linux系统的开发者，当然是需要在surface上装一个linux了，但是前两年zccrs已经踩过坑了，linux不识别surface键盘，同样的触摸、网卡、声卡等设备也工作的不是很正常，本来以为我要开启远程开发的生活了，还特意写了一篇&lt;a href="https://blog.justforlxz.com/2020/09/03/use-vscode-to-remotely-develop-dde/">《使用VSCode远程开发DDE》&lt;/a>，来给公司里有同样烦恼的人，让他们也感受一下远程开发的魅力。&lt;/p>&lt;p>苍天不负有心人，我成功的！&lt;/p>&lt;p>我成功的使用上了ArchLinux,并且工作的十分良好。这多亏了github上的一个组织&lt;a href="https://github.com/linux-surface/linux-surface/">linux-surface&lt;/a>，有这么一些人，他们付出劳动来让手里的surface设备也用上linux,并且要和普通的x86兼容机一样工作，感谢他们的付出，也让我吃上了螃蟹，安装的过程我就不在这里详细说了，其实非常简单。&lt;/p>&lt;p>首先因为surface只有一个usb口，而键盘并不能工作，所以需要一个usb的扩展器。先按fn+f6进bios关闭掉安全启动，修改引导顺序为usb优先，之后就是正常的安装系统，但是不需要安装仓库里的内核，我们需要安装linux-surface提供的仓库里的内核。&lt;/p>&lt;p>这些都是非常正常的步骤，linux-surface提供了自己的仓库和内核，我们正常使用即可。&lt;/p>&lt;p>这里开始就是我研究了半天的内容， &lt;strong>开启安全模式!&lt;/strong>&lt;/p>&lt;p>首先我看了一下arch wiki上有关于安全启动的内容，写的挺详细的，就是看不懂。讲了各种的知识点，各种签名的方式，但是真正到我开始用的时候，我是一直失败的，失败的方式我就不说了，直接说我如何成功的。&lt;/p>&lt;p>首先我放弃了grub,一个原因是grub的安全启动我一直没有尝试成功，另外一个是我只有一个系统，没必要用grub。我换成了rEFInd来作为我的bootloader，首先安装rEFInd的引导。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">yay -S refind shim-signed sbsigntools&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo refind-install --shim /usr/share/shim-signed/shimx64.efi --localkeys&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>来解释一下上面两条明令。第一个是安装必要的软件包，refind是bootloader本体，shim-signed是aur里面的用于安全启动的包，shim提供了一种并行的安全启动验证功能，我们使用它来启动refind的efi,再通过refind的efi启动内核，达到终极套娃启动。sbsigntools是用于给文件签名的工具，我们安装完refind以后，refind会帮助我们生成一份默认的key,我们需要使用这个key来为内核进行签名。&lt;/p>&lt;p>在执行第二条明令以后，会有几次询问，都选择Y回车就行。&lt;/p>&lt;p>然后使用sbsigntools来对内核进行签名。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo sbsign --key /etc/refind.d/keys/refind_local.key --cert /etc/refind.d/keys/refind_local.crt --output /boot/vmlinuz-linux-surface /boot/vmlinuz-linux-surface&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>准备工作已经进行一半了，我们只需要写一下refind的配置文件，就可以启动了。&lt;/p>&lt;p>refind的配置文件有两个地方，一个是boot分区下面的refind_linux.conf，还有一个是在efi分区里的EFI&amp;#x2f;refind&amp;#x2f;refind.conf，我们需要修改的是后者。&lt;/p>&lt;p>默认配置文件都是注释的，其实我们全部删了就可以了，有需要修改的地方去看原始文件或者文档就行了。&lt;/p>&lt;p>添加一个menuentry，就可以启动系统了。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">also_scan_dirs +,ArchFS/boot&lt;/span>&lt;br />&lt;span class="line">dont_scan_dirs ESP:/EFI/boot,EFI/boot&lt;/span>&lt;br />&lt;span class="line">dont_scan_files shim.efi,MokManager.efi,fbx64.efi,mmx64.efi,shimx64.efi&lt;/span>&lt;br />&lt;span class="line">scan_all_linux_kernels false&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">menuentry &amp;quot;Arch Linux&amp;quot; &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> icon /EFI/refind/icons/os_arch.png&lt;/span>&lt;br />&lt;span class="line"> volume 8B131F77-62D7-4B4A-82D4-B60D7ACA2F6C&lt;/span>&lt;br />&lt;span class="line"> loader /ArchFS/boot/vmlinuz-linux-surface&lt;/span>&lt;br />&lt;span class="line"> initrd /ArchFS/boot/intel-ucode.img&lt;/span>&lt;br />&lt;span class="line"> initrd /ArchFS/boot/initramfs-linux-surface.img&lt;/span>&lt;br />&lt;span class="line"> options &amp;quot;root=UUID=9f8f9556-8ec1-4feb-9519-435beac8376f rw rootflags=subvol=ArchFS loglevel=3 quiet add_efi_memmap&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> submenuentry &amp;quot;Boot using fallback initramfs&amp;quot; &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> initrd /ArchFS/boot/initramfs-linux-surface-fallback.img&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> submenuentry &amp;quot;Boot to terminal&amp;quot; &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> add_options &amp;quot;systemd.unit=multi-user.target&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>我用的是btrfs文件系统，所以配置文件有点罗嗦。解释一下上面的内容。&lt;/p>&lt;p>also_scan_dirs是指定扫描某个目录，因为我是btrfs文件系统，必须使用这个才能让refind扫描到内核文件，否则会无法启动。&lt;/p>&lt;p>dont_scan_dirs是跳过指定的目录，因为refind默认是会扫描所有的efi文件，我们自己提供了emnuentry,所以不需要让它扫描了。&lt;/p>&lt;p>dont_scan_files是跳过指定的文件，这里是防止其他目录出现对应的efi也被扫描到。&lt;/p>&lt;p>scan_all_linux_kernels是扫描所有linux内核，这样所有的内核就会出现在启动列表里，我们同样也是不需要的。&lt;/p>&lt;p>menuentry里面需要修改的地方有，volume是分区的partuuid,我因为这个uuid就测试了好几次，最后才反应过来不是filesystem uuid,要求的是partition uuid. 所有遇到ArchFS的地方都是不需要的，因为btrfs支持字卷，我的系统是在一个叫ArchFS的卷里面的，如果不是btrfs的文件系统，这个是不需要的，同样options里的rootflags选项也是不需要的，这是传递给内核的参数，让内核可以正确的加载根分区。&lt;/p>&lt;p>这样就算完工了，重启系统，然后进bios里把安全启动改成Microsoft &amp;amp; 3rd party CA，然后重新启动。&lt;/p>&lt;p>当第一次加载rEFInd的时候，因为我们的证书是才生成的，主板并没有存储对应的签名，rEFInd会启动mmx64.efi来让我们加载证书，证书的位置在&lt;code>/etc/refind.d/keys&lt;/code>下，选择&lt;code>refind_local.cer&lt;/code>导入，然后选择重启，重新进入系统就可以了。&lt;/p>&lt;p>导入证书这部分我其实不太确定，因为我除了使用shim方案，我还测试了preloader方案，那个方案会一开始就启动一个MOK的工具进行证书导入，我记不太清shim到底需不需要手动导入了，如果出现了，那就导入一下就行了，没出现的话就能正常的看到引导界面和进入系统了。&lt;/p>&lt;p>还有一个后续的动作需要处理，就是内核升级以后，我们需要对内核重新签名，否则会被bios拒绝启动。&lt;/p>&lt;p>编辑&lt;code>/etc/pacman.d/hooks/99-secureboot.hook&lt;/code>,并写入以下配置：&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">[Trigger]&lt;/span>&lt;br />&lt;span class="line">Operation = Install&lt;/span>&lt;br />&lt;span class="line">Operation = Upgrade&lt;/span>&lt;br />&lt;span class="line">Type = Package&lt;/span>&lt;br />&lt;span class="line">Target = linux&lt;/span>&lt;br />&lt;span class="line">Target = linux-surface&lt;/span>&lt;br />&lt;span class="line">Target = systemd&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">[Action]&lt;/span>&lt;br />&lt;span class="line">Description = Signing Kernel for SecureBoot&lt;/span>&lt;br />&lt;span class="line">When = PostTransaction&lt;/span>&lt;br />&lt;span class="line">Exec = /usr/bin/sh -c &amp;quot;/usr/bin/find /boot/ -type f \( -name &amp;#x27;vmlinuz-*&amp;#x27; -o -name &amp;#x27;systemd*&amp;#x27; \) -exec /usr/bin/sh -c &amp;#x27;if ! /usr/bin/sbverify --list &amp;#123;&amp;#125; 2&amp;gt;/dev/null | /usr/bin/grep -q \&amp;quot;signature certificates\&amp;quot;; then /usr/bin/sbsign --key /etc/refind.d/keys/refind_local.key --cert /etc/refind.d/keys/refind_local.crt --output &amp;#123;&amp;#125; &amp;#123;&amp;#125;; fi&amp;#x27; \;&amp;quot;&lt;/span>&lt;br />&lt;span class="line">Depends = sbsigntools&lt;/span>&lt;br />&lt;span class="line">Depends = findutils&lt;/span>&lt;br />&lt;span class="line">Depends = grep&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>享受安全启动吧～&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/IMG_1089.JPG" />&lt;img alt="图片" src="https://blog.mkacg.com/rss/IMG_1090.JPG" />&lt;img alt="图片" src="https://blog.mkacg.com/rss/IMG_1091.JPG" /></description><author>justforlxz</author></item><item><title>deepin git version</title><link>https://blog.mkacg.com/2020/09/06/deepin-git-version/</link><pubDate>Sun, 06 Sep 2020 04:51:08 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2020/09/06/deepin-git-version/</guid><description>&lt;p>This repository only provides the git version of deepin. You can replace the deepin group in the community by installing the deepin-git group.&lt;/p>&lt;span id="more">&lt;/span>&lt;p>The PKGBUILD for all packages are there &lt;a href="https://github.com/justforlxz/deepin-git-repo">https://github.com/justforlxz/deepin-git-repo&lt;/a>, Each branch saves the corresponding software.&lt;/p>&lt;p>Before adding this repository, you should first add the key used to sign the packages in it. You can do this by running the following commands:&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">wget -qO - https://packages.justforlxz.com/deepingit.asc \&lt;/span>&lt;br />&lt;span class="line"> | sudo pacman-key --add -&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>It is recommended that you now fingerprint it by running&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo pacman-key --finger DCAF15B4605D5BEB&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>and in a final step, you have to locally sign the key to trust it via&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo pacman-key --lsign-key DCAF15B4605D5BEB&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>More infos on this process can be found at &lt;a href="https://wiki.archlinux.org/index.php/Pacman/Package_signing#Adding_unofficial_keys">https://wiki.archlinux.org/index.php/Pacman/Package_signing#Adding_unofficial_keys&lt;/a>. You can now add the repository by editing &amp;#x2f;etc&amp;#x2f;pacman.conf and adding&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">[deepingit]&lt;/span>&lt;br />&lt;span class="line">Server = https://packages.justforlxz.com/&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>at the end of the file. See &lt;a href="https://wiki.archlinux.org/index.php/Pacman#Repositories_and_mirrors">https://wiki.archlinux.org/index.php/Pacman#Repositories_and_mirrors&lt;/a> for details.&lt;/p>&lt;p>to install deepin git version:&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo pacman -Syy deepin-git&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>If you don’t want to use the repository anymore, you can uninstall deepin git, or install the deepin group in Community.&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo pacman -Rscn deepin-git&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>to install deepin group for community.&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo pacman -S deepin&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure></description><author>justforlxz</author></item><item><title>使用VSCode远程开发DDE</title><link>https://blog.mkacg.com/2020/09/03/use-vscode-to-remotely-develop-dde/</link><pubDate>Thu, 03 Sep 2020 03:14:38 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2020/09/03/use-vscode-to-remotely-develop-dde/</guid><description>&lt;p>本文将介绍如何使用VSCode的远程开发套件连接到Deepin主机，进行DDE和其他软件的开发与调试.&lt;/p>&lt;span id="more">&lt;/span>&lt;h2 id="介绍">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#介绍" title="介绍">&lt;/a>介绍&lt;/h2>&lt;p>Visual Studio Code（简称VS Code）是一个由微软开发，同时支持Windows 、 Linux和macOS等操作系统的免费代码编辑器，它支持测试，并内置了Git 版本控制功能，同时也具有开发环境功能，例如代码补全（类似于 IntelliSense）、代码片段和代码重构等。该编辑器支持用户个性化配置，例如改变主题颜色、键盘快捷方式等各种属性和参数，同时还在编辑器中内置了扩展程序管理的功能。&lt;/p>&lt;p>在VSCode出来之前，Sublime曾经是前端开发者必备的软件，它使用python作为插件运行环境，并且也拥有不少的插件，但是很遗憾的是插件不能更改界面元素，可玩性不是很高。&lt;/p>&lt;p>再后来Atom作为GitHub的顶梁柱出现了，它基于使用Chromium和Node.js的跨平台应用框架Electron（最初名为Atom Shell），并使用CoffeeScript和Less撰写，并且支持js开发的插件，一时间拥有非常多的用户，并且从Sublime那里拉拢了非常多的前端开发者。&lt;/p>&lt;p>但是一切都在VSCode面世以后变了。VSCode同样也是基于Chromim和Electron开发，并且支持TypeScript开发插件，而且启动速度比Atom快很多，而且作为微软面向开源社区的主力产品，它和TypeScript一样，吸收了社区的很多意见和贡献，使得软件越来越好用。在语言支持方面，对 C#、JavaScript、和 TypeScript 等编程语言的原生支持最为完善。&lt;/p>&lt;h2 id="安装VS-Code">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#安装VS-Code" title="安装VS Code">&lt;/a>安装VS Code&lt;/h2>&lt;p>官网提供的有vscode的安装包，windows用户下载stable版本的exe(System Installer)。&lt;/p>&lt;p>System Installer可以自动下载对应语言的环境包，推荐安装此版本。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/W1KvXk0ylxHa9w8E.png" />&lt;p>安装完成后就可以安装插件了。&lt;/p>&lt;h2 id="安装插件">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#安装插件" title="安装插件">&lt;/a>安装插件&lt;/h2>&lt;p>插件系统是一个编辑器的左膀右臂，emacs和vim作为终端下开发经常使用的编辑器，就拥有非常丰富的插件，几乎每个大佬使用的emacs和vim都不能互换使用。&lt;/p>&lt;p>dde的项目几乎都是cmake的项目，所以需要安装cmake插件和c++的插件，安装了这两个插件以后。vscode打开项目工程就会自动解析CMakeLists.txt，并且开启vscode的快速调试功能，还可以开始构建项目和调试项目了。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/XvMxNUQrs9oyEYJF.png" />&lt;p>安装CMake、CMake Tools这两个插件就可以开发了。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/tBB60YiLW79oMLC1.png" />&lt;p>安装C&amp;#x2f;C++和C++ Intellisense这两个插件可以对项目中的c++代码进行智能感知和代码补全，推荐安装。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/idQexXGZXPrqCBby.png" />&lt;p>安装Remote - SSH插件，可以让vscode通过ssh连接到目标机器，打开远程机器的目录和文件，并且在该模式下，部分插件可以自动切换成本地&amp;#x2f;远程模式，这样就可以在本机直接开发，但是操作的内容都是远程环境的。&lt;/p>&lt;p>安装完Remote - SSH插件以后，vscode的左下角就会有一个绿色的按钮，可以用来切换模式。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/MWorTAHEAsJRt47E.png" />&lt;h2 id="配置远程环境">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#配置远程环境" title="配置远程环境">&lt;/a>配置远程环境&lt;/h2>&lt;p>因为是要在Windows上进行远程开发，如果是直接在UOS或者Deepin上开发DDE，这一部分是可以不用看的，上面的插件安装完成以后就可以开发项目了。&lt;/p>&lt;p>点击左下角的绿色按钮，在弹出的面板选择Remote-SSH: Connect to Host。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/WFN9jpqyRRXORtuP.png" />&lt;p>会继续弹出一个面板，用来选择配置ssh的连接。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/Cw7Z7sF3rb8RiBzZ.png" />&lt;p>选择Add New SSH Host添加一个服务器。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/qcXsO8ayHXyTMr62.png" />&lt;p>输入ssh的命令，例如 ssh &lt;a href="mailto:&amp;#x6c;&amp;#x78;&amp;#x7a;&amp;#64;&amp;#x31;&amp;#48;&amp;#46;&amp;#x32;&amp;#48;&amp;#x2e;&amp;#51;&amp;#x32;&amp;#x2e;&amp;#x35;&amp;#x34;">lxz@10.20.32.54&lt;/a>。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/9Cla2rDtfzfsHVSh.png" />&lt;p>然后选择一个保存配置的位置，一般默认选择用户家目录的.ssh目录即可。然后就提示添加成功，此时可以点击Connect按钮进行连接。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/Do8KoBS0lwM1g09G.png" />&lt;p>输入密码&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/J7dPRyLYI4QBcma5.png" />&lt;p>登录以后会打开一个新的窗口，并提示正在连接。连接成功以后可以在左下角看到机器的信息。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/LjLHBXGQeuvXMk8k.png" />&lt;img alt="图片" src="https://blog.mkacg.com/rss/VJQhRC7LXbhWyWdN.png" />&lt;p>然后打开命令面板，选择在SSH中安装本地扩展。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/XJEqsfmwIO5FpUKS.png" />&lt;p>在打开的列表选择全选，然后安装。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/zmVjGPVL7gphBZ7h.png" />&lt;p>等待全部安装成功。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/P1yXbz7pkrwXOEAS.png" />&lt;img alt="图片" src="https://blog.mkacg.com/rss/WbxesoOf0RF1QqtZ.png" />&lt;h2 id="开发和调试">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#开发和调试" title="开发和调试">&lt;/a>开发和调试&lt;/h2>&lt;h3 id="功能介绍">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#功能介绍" title="功能介绍">&lt;/a>功能介绍&lt;/h3>&lt;p>CMake插件提供了编译、运行和调试的功能和命令，可以点击下方面板中的select target，选择要运行的目标程序，选择切换编译模式，可以选择Debug或者Release。还可以选择使用哪个编译器进行构建。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/je8iADCEbmCT7sMZ.png" />&lt;img alt="图片" src="https://blog.mkacg.com/rss/uUIxae7PVFDwSyUB.png" />&lt;img alt="图片" src="https://blog.mkacg.com/rss/Ah6NUPpjjoDIL93K.png" />&lt;img alt="图片" src="https://blog.mkacg.com/rss/UbibgDc1gL0tXNPy.png" />&lt;h3 id="设置启动参数">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#设置启动参数" title="设置启动参数">&lt;/a>设置启动参数&lt;/h3>&lt;p>如果程序启动不需要提供参数，则可以直接点击下方面板的Debug按钮，或者打开命令面板选择CMake Debug Target，如果没有选择过Target，则会询问一次设置Target。&lt;/p>&lt;p>点击左侧的调试按钮，选择添加配置。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/kTjwUYxwt19QlwPt.png" />&lt;p>在弹出的面板选择GDB&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/5Q4JOQWUZnDMtBNx.png" />&lt;p>此时vscode会创建出一个json文件，并生成了默认的配置文件。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/l1IZdMrxfd0dH45a.png" />&lt;p>我们需要进行一些调整，以便使用该配置文件进行调试。&lt;/p>&lt;p>program字段是程序二进制文件的位置，一般情况下我们是要手动写好路径，但是如果项目的二进制特别多，更换配置文件就会非常麻烦，而且配置文件里写死路径也不是很方便，我查阅了CMake插件的文档，发现CMake插件提供了两个很重要的变量，可以让我们方便的查找到路径。&lt;/p>&lt;figure class="highlight json">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="attr">&amp;quot;program&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;$&amp;#123;command:cmake.launchTargetPath&amp;#125;&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>CMake插件提供了launchTargetPath的变量，它对应的是CMake插件选择的默认target，启动调试之前需要我们先选择好Target。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&amp;quot;value&amp;quot;: &amp;quot;$PATH:$&amp;#123;command:cmake.launchTargetDirectory&amp;#125;&amp;quot;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>CMake插件还提供了launchTargetDirectory变量，用于获取程序启动所在的目录，一般需要我们指定到本次调试所需的环境变量中。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&amp;quot;environment&amp;quot;: [&lt;/span>&lt;br />&lt;span class="line"> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &amp;quot;name&amp;quot;: &amp;quot;PATH&amp;quot;,&lt;/span>&lt;br />&lt;span class="line"> &amp;quot;value&amp;quot;: &amp;quot;$PATH:$&amp;#123;command:cmake.launchTargetDirectory&amp;#125;&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;,&lt;/span>&lt;br />&lt;span class="line">]&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>然后我们就可以添加启动参数了。&lt;br />args字段保存了程序启动会传递的参数列表，例如这里会给fuse传递-d和&amp;#x2f;tmp&amp;#x2f;x。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&amp;quot;args&amp;quot;: [&lt;/span>&lt;br />&lt;span class="line"> &amp;quot;-d&amp;quot;,&lt;/span>&lt;br />&lt;span class="line"> &amp;quot;/tmp/x&amp;quot;&lt;/span>&lt;br />&lt;span class="line">]&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>完整的配置如下：&lt;/p>&lt;figure class="highlight json">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="attr">&amp;quot;name&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;(gdb) fuse&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="attr">&amp;quot;type&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;cppdbg&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="attr">&amp;quot;request&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;launch&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="attr">&amp;quot;program&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;$&amp;#123;command:cmake.launchTargetPath&amp;#125;&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="attr">&amp;quot;args&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">[&lt;/span>&lt;/span>&lt;br />&lt;span class="line">        &lt;span class="string">&amp;quot;-d&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">        &lt;span class="string">&amp;quot;/tmp/x&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="punctuation">]&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="attr">&amp;quot;stopAtEntry&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="literal">&lt;span class="keyword">false&lt;/span>&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="attr">&amp;quot;cwd&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;$&amp;#123;workspaceFolder&amp;#125;&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="attr">&amp;quot;environment&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">[&lt;/span>&lt;/span>&lt;br />&lt;span class="line">        &lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">            &lt;span class="attr">&amp;quot;name&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;PATH&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">            &lt;span class="attr">&amp;quot;value&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;$PATH:$&amp;#123;command:cmake.launchTargetDirectory&amp;#125;&amp;quot;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">        &lt;span class="punctuation">&amp;#125;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="punctuation">]&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="attr">&amp;quot;externalConsole&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="literal">&lt;span class="keyword">false&lt;/span>&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="attr">&amp;quot;MIMode&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;gdb&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="attr">&amp;quot;setupCommands&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="punctuation">[&lt;/span>&lt;/span>&lt;br />&lt;span class="line">        &lt;span class="punctuation">&amp;#123;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">            &lt;span class="attr">&amp;quot;description&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;为 gdb 启用整齐打印&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">            &lt;span class="attr">&amp;quot;text&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="string">&amp;quot;-enable-pretty-printing&amp;quot;&lt;/span>&lt;span class="punctuation">,&lt;/span>&lt;/span>&lt;br />&lt;span class="line">            &lt;span class="attr">&amp;quot;ignoreFailures&amp;quot;&lt;/span>&lt;span class="punctuation">:&lt;/span> &lt;span class="literal">&lt;span class="keyword">true&lt;/span>&lt;/span>&lt;/span>&lt;br />&lt;span class="line">        &lt;span class="punctuation">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;span class="line">    &lt;span class="punctuation">]&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="punctuation">&amp;#125;&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h3 id="gdb调试">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#gdb调试" title="gdb调试">&lt;/a>gdb调试&lt;/h3>&lt;p>此时我们就可以先通过CMake插件构建整个项目，再切换到运行面板，启动调试。&lt;/p>&lt;p>点击下方面板的Build按钮，构建项目。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/FAkfeLuV4JBsc4Mx.png" />&lt;p>再点击左侧的gdb fuse(deepin-turbo)按钮，因为配置文件里面我们起的名字是gdb fuse。我们在main函数添加一个断点，用来测试gdb是否工作正常。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/mKDIuN2GI1pSfVpQ.png" />&lt;p>一切都工作正常，在调试控制台可以使用-exec作为前缀来执行gdb的命令。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/sxNL1dJG5uFm22O1.png" />&lt;h3 id="调试图形程序">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#调试图形程序" title="调试图形程序">&lt;/a>调试图形程序&lt;/h3>&lt;p>调试图形程序稍微有一些麻烦，因为是远程开发，图形程序又只能工作在目标机器，这里提供两个可行的方案。&lt;/p>&lt;ol>&lt;li>synergy之类的键盘鼠标共享软件&lt;/li>&lt;li>在windows安装xserver&lt;/li>&lt;/ol>&lt;p>第一种方案是通过共享本机的键盘鼠标到远程机器，这样就可以在远程环境上面进行直接操作，好处是除了调试，也可以同时操作远程机器进行使用。&lt;/p>&lt;p>第二种方案是利用X11协议的网络透明，既图形程序和显示服务不一定在同一台机器上运行，我们只需要在Windows安装XServer程序，就可以让远程机器上的程序的画面显示到当前机器，并且可以操作。但是此方案有缺点，虽然设计上这种分离结构设计的很巧妙，但是因为远程OpenGL调用并不支持，所以图形无法调用3D程序渲染，并且和远程机器沟通需要大量的带宽，所以用起来体验并不好。&lt;/p>&lt;p>为了使用这两种方案，我们都需要在调试的launch.json中添加一个环境变量。&lt;/p>&lt;p>在运行面板点击齿轮按钮，可以编辑当前方案。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/X32Q5bhGX2B7CLGg.png" />&lt;p>在打开的json文件中，找到environment字段，添加DISPLAY环境变量。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&amp;quot;environment&amp;quot;: [&lt;/span>&lt;br />&lt;span class="line"> &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> &amp;quot;name&amp;quot;: &amp;quot;DISPLAY&amp;quot;,&lt;/span>&lt;br />&lt;span class="line"> &amp;quot;value&amp;quot;: &amp;quot;:0&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">]&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>这样程序启动就有DISPLAY环境变量，我们就可以让程序在目标机器的屏幕上运行了。&lt;/p>&lt;img alt="图片" src="https://blog.mkacg.com/rss/7JuWfnnURIt1NrdR.png" />&lt;img alt="图片" src="https://blog.mkacg.com/rss/20200903_023112910_iOS.jpg" />&lt;img alt="图片" src="https://blog.mkacg.com/rss/20200903_023116260_iOS.jpg" />&lt;p>还有一种自动化测试的方案，该方案是我个人认为所有开发都应该掌握的，通过自动化测试，我们就可以完全使用远程开发来完成开发任务，调试的时候只需要等待自动测试结果返回即可，设想一下，某个模块需要点击很多地方才可以重现一个问题，我们只需要设置好断点，让程序自动开始执行所有函数，并在最终出现问题的地方停下，我们就可以开始手动单步跟踪问题，完全不需要使用鼠标人工点击。（然而理想很美好，现实很残酷，我个人目前都没有掌握自动化测试的方式，现在也是只能通过鼠标点点点来重现问题。&lt;/p></description><author>justforlxz</author></item><item><title>在ArchLinux上开发startdde</title><link>https://blog.mkacg.com/2020/08/06/develop-startdde-on-archlinux/</link><pubDate>Thu, 06 Aug 2020 08:14:26 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2020/08/06/develop-startdde-on-archlinux/</guid><description>&lt;p>dde 后端使用 go 作为主要的开发语言，使用 dbus 提供接口，主要使用 gsettings 来保存配置。 所以在进行后端开发前需要对以上内容有基本的了解，这里假定本文档的阅读者熟悉 dbus 和 gsettings，并有一定的开发经验。&lt;/p>&lt;span id="more">&lt;/span>&lt;h2 id="安装依赖">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#安装依赖" title="安装依赖">&lt;/a>安装依赖&lt;/h2>&lt;p>虽然本项目是go语言开发的，但是我们并没有直接使用go的mod作为依赖管理方案，而是走系统包管理器的方式，所以要先安装startdde的编译依赖。&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo pacman -Sy golang-github-linuxdeepin-go-dbus-factory golang-deepin-gir golang-deepin-lib golang-deepin-dde-api go git jq golang-golang-x-net golang-github-linuxdeepin-go-x11-client&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>这些包会被安装到系统的&amp;#x2f;usr&amp;#x2f;share&amp;#x2f;gocode目录下。还需要手动go get一个依赖到本地的GOPATH中。&lt;/p>&lt;figure class="highlight go">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="keyword">go&lt;/span> get -v github.com/cryptix/wav&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h2 id="设置GOPATH">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#设置GOPATH" title="设置GOPATH">&lt;/a>设置GOPATH&lt;/h2>&lt;p>为了方便以后的开发，可以将GOPATH环境变量定义到~&amp;#x2f;.xprofile等文件中，或者shell的配置文件。例如我使用的zsh：&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">export GOPATH=$HOME/Develop/Go:/usr/share/gocode&lt;/span>&lt;br />&lt;span class="line">export PATH=$HOME/Develop/Go/bin:$PATH&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h2 id="设置项目目录">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#设置项目目录" title="设置项目目录">&lt;/a>设置项目目录&lt;/h2>&lt;p>go要求项目目录必须在GOPATH中，所以要将startdde放到GOPATH的pkg.deepin.io&amp;#x2f;dde&amp;#x2f;目录下，但是GOPATH每次进入不方便，可以采用软链的形式将startdde的目录链接到GOPATH下。&lt;/p>&lt;figure class="highlight shell">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">cd ~/Develop/Deepin&lt;/span>&lt;br />&lt;span class="line">git clone https://github.com/linuxdeepin/startdde&lt;/span>&lt;br />&lt;span class="line">mkdir -p ~/Develop/Go/src/pkg.deepin.io/dde/&lt;/span>&lt;br />&lt;span class="line">ln -sf ~/Develop/Deepin/startdde ~/Develop/Go/src/pkg.deepin.io/dde/startdde&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>这样就可以在一个方便的目录进行开发了。&lt;/p>&lt;h2 id="vscode开发工具">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#vscode开发工具" title="vscode开发工具">&lt;/a>vscode开发工具&lt;/h2>&lt;p>我个人推荐使用vscode当作开发工具，打开vscode安装go的插件，打开startdde目录，vscode会提示安装一些go的工具，选择全部安装即可。&lt;/p></description><author>justforlxz</author></item><item><title>use github action to check dde-launcher</title><link>https://blog.mkacg.com/2020/07/27/use-github-action-to-check-dde-launcher/</link><pubDate>Mon, 27 Jul 2020 10:14:21 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2020/07/27/use-github-action-to-check-dde-launcher/</guid><description>&lt;p>本来打算7月份给dde添加github action验证，但是被各种事情耽误了，然后发现麒麟居然抢在我前面部署了全套的github action，这不能忍，赶紧把dde的github action也提上日程。并且打算听&lt;a href="https://github.com/felixonmars">肥肥猫&lt;/a>大佬的话，在aur给dde弄一套commit构建包，这样就可以在arch上使用比testing仓库更testing的dde了！&lt;/p>&lt;span id="more">&lt;/span>&lt;p>github actions是github官方出的持续集成功能，以前大家在github上的项目都使用的第三方的Travis CI或者自建jenkins构建，但是github被微软收购以后，微软为了表现出给社区和用户的诚意，将大量github的付费功能免费公开给开发者使用，希望能将github打造成开发者中心，于是在2019年微软推出了免费的github actions，每个项目都可以免费使用官方提供的持续集成和持续部署功能，这对第三方业务无疑是个巨大的打击，虽然Travis CI和jenkins等方式仍然有一定的市场，但是对于中小项目的开源项目，使用官方提供的功能无疑是方便的。&lt;/p>&lt;p>github actions的配置十分简单，只需要几个简单的步骤就可以实现构建、执行和测试代码。并且可以使用Linux、Windows和MacOS环境，机器性能也十分强劲，编译速度非常的快。&lt;/p>&lt;p>这是给dde-launcher的一份基础配置，需要将配置文件放在.github&amp;#x2f;workflows&amp;#x2f;目录下，以build.yaml文件名保存。&lt;/p>&lt;figure class="highlight yaml">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&lt;span class="attr">name:&lt;/span> &lt;span class="string">CI&lt;/span> &lt;span class="string">Build&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="attr">on:&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">push:&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">branches:&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="bullet">-&lt;/span> &lt;span class="string">UOS&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">pull_request:&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">branches:&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="bullet">-&lt;/span> &lt;span class="string">UOS&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">&lt;span class="attr">jobs:&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">archlinux:&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">name:&lt;/span> &lt;span class="string">Archlinux&lt;/span> &lt;span class="string">Build&lt;/span> &lt;span class="string">Check&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">runs-on:&lt;/span> &lt;span class="string">ubuntu-latest&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">container:&lt;/span> &lt;span class="string">docker.io/library/archlinux:latest&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">steps:&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="bullet">-&lt;/span> &lt;span class="attr">name:&lt;/span> &lt;span class="string">Checkout&lt;/span> &lt;span class="string">branch&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">uses:&lt;/span> &lt;span class="string">actions/checkout@v2&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="bullet">-&lt;/span> &lt;span class="attr">name:&lt;/span> &lt;span class="string">Refresh&lt;/span> &lt;span class="string">pacman&lt;/span> &lt;span class="string">repository&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">run:&lt;/span> &lt;span class="string">pacman&lt;/span> &lt;span class="string">-Syy&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="bullet">-&lt;/span> &lt;span class="attr">name:&lt;/span> &lt;span class="string">Install&lt;/span> &lt;span class="string">build&lt;/span> &lt;span class="string">dependencies&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">run:&lt;/span> &lt;span class="string">pacman&lt;/span> &lt;span class="string">-S&lt;/span> &lt;span class="string">--noconfirm&lt;/span> &lt;span class="string">base-devel&lt;/span> &lt;span class="string">cmake&lt;/span> &lt;span class="string">ninja&lt;/span> &lt;span class="string">qt5-tools&lt;/span> &lt;span class="string">deepin-qt-dbus-factory&lt;/span> &lt;span class="string">dtkwidget&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="bullet">-&lt;/span> &lt;span class="attr">name:&lt;/span> &lt;span class="string">CMake&lt;/span> &lt;span class="string">&amp;amp;&lt;/span> &lt;span class="string">Make&lt;/span>&lt;/span>&lt;br />&lt;span class="line"> &lt;span class="attr">run:&lt;/span> &lt;span class="string">|&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="string"> mkdir build&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="string"> cd build&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="string"> cmake ../ -G Ninja&lt;/span>&lt;/span>&lt;br />&lt;span class="line">&lt;span class="string"> ninja&lt;/span>&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>介绍一下配置文件吧，name是设置ci的名字，github允许有多个ci存在，可以做不同的事情，例如部署三个ci，一个做语法检查，一个做静态检查，一个做编译检查。name就是用来在界面上显示的。on是设置ci对哪些事件感兴趣，在这里我设置了push和pull_request，当发生push和pull request时，这个ci就会被启动，执行接下来的jobs的内容。jobs里是可以设置多个任务的，同样name字段也是用来展示本次动作的名称。runs-on是设置该job工作的环境，ubuntu-latest是linux环境，container是指使用哪个docker容器，github actions是可以使用docker的，也可以将自己的ci配置共享给其他人使用。run就是执行命令了，在配置文件中我手动运行了刷新仓库和编译项目所需的命令。job的steps可以理解成shell中一次动作的执行，uses是使用其他人封装好的命令，run则是执行本地命令。&lt;/p>&lt;p>可以看出github actions的配置是十分简单的，并且构建速度也非常的快，并且构建环境是使用的arch linux环境，为什么要选择arch作为ci的基础构建环境呢，原因当然不是因为和肥肥猫有py交易，arch上的dde更新速度很快，并且很多用户都使用arch+dde的方式使用linux，deepin自己维护的发行版因为基础仓库更新较慢，不适合一些用户，所以为了能让dde被更多的人接受和使用，在arch上及时更新dde是十分有必要的。所以才选择actions的环境为arch linux。&lt;/p></description><author>justforlxz</author></item><item><title>使用perf工具分析程序性能</title><link>https://blog.mkacg.com/2020/07/21/use-perf-to-analytics-program/</link><pubDate>Tue, 21 Jul 2020 01:15:11 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2020/07/21/use-perf-to-analytics-program/</guid><description>&lt;p>最近在对DDE进行性能优化，所以补习了一下linux下的各种分析工具的使用方法。&lt;/p>&lt;img alt="bpf_performance_tools_book" src="https://blog.mkacg.com/rss/bpf_performance_tools_book.png" />&lt;p>这张图是来自Brendan Gregg大佬提供的linux分析工具的应用场景，可以看出几乎包含了系统每个地方应该用什么工具去分析。&lt;/p>&lt;span id="more">&lt;/span>&lt;h2 id="Linux-Perf-Tool">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#Linux-Perf-Tool" title="Linux Perf Tool">&lt;/a>Linux Perf Tool&lt;/h2>&lt;h3 id="允许系统进行分析">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#允许系统进行分析" title="允许系统进行分析">&lt;/a>允许系统进行分析&lt;/h3>&lt;p>为了能够正常分析，首先需要打开系统的调试功能，允许我们去对其他进程进行访问。&lt;/p>&lt;h3 id="SysCtl">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#SysCtl" title="SysCtl">&lt;/a>SysCtl&lt;/h3>&lt;p>较新的Linux内核具有sysfs可调参数&lt;code>/proc/sys/kernel/perf_event_paranoid&lt;/code>，该参数允许用户调整perf_events非root用户的可用功能，数量越大则越安全（相应地提供较少的功能）：&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">Consider tweaking /proc/sys/kernel/perf_event_paranoid:&lt;/span>&lt;br />&lt;span class="line"> -1 - Not paranoid at all&lt;/span>&lt;br />&lt;span class="line"> 0 - Disallow raw tracepoint access for unpriv&lt;/span>&lt;br />&lt;span class="line"> 1 - Disallow cpu events for unpriv&lt;/span>&lt;br />&lt;span class="line"> 2 - Disallow kernel profiling for unpriv&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>默认值是不允许获取任何信息，所以我们需要修改为1或者0，允许我们访问CPU的事件信息。&lt;/p>&lt;ul>&lt;li>临时修改&lt;/li>&lt;/ul>&lt;p>执行命令向内核接口直接写入值。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo tee /proc/sys/kernel/perf_event_paranoid &amp;lt;&amp;lt;&amp;lt; 1&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;ul>&lt;li>永久修改&lt;/li>&lt;/ul>&lt;p>使用sysctl来配置其值，创建&lt;code>/etc/sysctl.d/50_perf_event_paranoid.conf&lt;/code>文件，并写入&lt;code>kernel.perf_event_paranoid=1&lt;/code>，执行&lt;code>sysctl -p&lt;/code>来刷新系统配置。&lt;/p>&lt;h3 id="perf-采样">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#perf-采样" title="perf 采样">&lt;/a>perf 采样&lt;/h3>&lt;blockquote>&lt;p>性能优化相关的三种类型的工具，一种是sampling类型的，即采样，这种工具就是不停“询问”程序在做什么，perf在我们使用的这种模式下就是 sampling模式，如果是追踪某些event，就工作在trace模式，实际上就是第二种类型的工具，这种工具主要依靠事件或者hook，程序在运行的过程中不停主动告诉工具它自己在做什么，比如 strace；第三种是 instrument 类型的，这种主要就是依赖编译器进行插桩，精确知道代码行级别的执行情况（参考gcc instrumentation ）。&lt;/p>&lt;p>by hualet on &lt;a href="https://docsin.uniontech.com/?p=735">deepin 15.7&lt;/a>&lt;/p>&lt;/blockquote>&lt;p>我们通过perf record命令才对程序进行采样记录。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">perf record -g --call-graph=dwarf -F 99 /usr/bin/dde-shutdown&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>命令介绍：&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">-g: 即采样全部信息&lt;/span>&lt;br />&lt;span class="line">--call-graph: 设置并启用调用图（堆栈链/回溯）记录，参数有fp(frame pointers)、dwarf(debug information)和lbr(Last Branch Record)。&lt;/span>&lt;br />&lt;span class="line">-F: 采样率&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>perf可以直接启动一个程序进行分析，也可以使用-p参数指定一个pid进行采样。&lt;/p>&lt;h3 id="查看-perf-的采样结果">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#查看-perf-的采样结果" title="查看 perf 的采样结果">&lt;/a>查看 perf 的采样结果&lt;/h3>&lt;p>当我们通过perf record完成采样以后，会在执行目录生成perf.data文件，此时我们就可以使用perf report命令对data文件进行数据分析了。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">perf report --stdio&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>perf report会自动打开当前目录下的perf.data文件，当然也可以在最后指定perf.data文件的路径。&lt;/p>&lt;p>perf report会根据–call-graph参数来生成不同的图，使用dwarf参数时会以函数调用栈的顺序来显示，使用这种方式可以方便的看出哪个函数执行的时间比较长，因为每次采样都能落到该函数上，也就意味着函数执行的时间非常长，再通过调用栈的深度来分析函数执行期间都在做什么事情。&lt;/p>&lt;h3 id="hotspot火焰图">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#hotspot火焰图" title="hotspot火焰图">&lt;/a>hotspot火焰图&lt;/h3>&lt;p>在命令行下查看函数调用不是特别方便，所以就有图形化的工具用来方便的查看perf工具的生成结果，其中使用比较友好的是kde开发的hotspot工具，该工具可以直接打开perf.data文件，并生成对应的火焰图，火焰图是函数调用的另一种表现形式，火焰越高，也就意味着调用栈越深，火焰越广，也就意味着函数执行的时间很长。&lt;/p></description><author>justforlxz</author></item><item><title>2019 Review</title><link>https://blog.mkacg.com/2020/01/01/2019review/</link><pubDate>Wed, 01 Jan 2020 13:25:54 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2020/01/01/2019review/</guid><description>&lt;p>上一次写年终总结还是18年回家的动车上，可惜写了一半没发表，觉得一年了没有什么能够回想起来的，就又删除了。今年不同了，今年有好多想说的。&lt;/p>&lt;span id="more">&lt;/span>&lt;h2 id="脱单">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#脱单" title="脱单">&lt;/a>脱单&lt;/h2>&lt;p>&lt;strong>第一件重要的事是我遇到了生命中的她。&lt;/strong>&lt;/p>&lt;p>自从工作以后，我妈天天念叨我的就是找对象，和我预想的没错，上学的时候盼我毕业，毕业以后盼我工作，工作以后盼我找对象结婚，找对象以后盼我赶紧生个娃让她抱。&lt;del>（大家的父母应该都这样）&lt;/del>&lt;/p>&lt;h2 id="加薪">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#加薪" title="加薪">&lt;/a>加薪&lt;/h2>&lt;p>这件事确实也令我挺开心的，我的工资在2019年成功涨到了0.375乔(1乔等于**元 &lt;a href="https://blog.nanpuyue.com/">@nanpuyue&lt;/a>)&lt;/p>&lt;p>涨工资谁不高兴，我估计也就马云不高兴了，毕竟他看不上钱。&lt;/p>&lt;h2 id="学习">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#学习" title="学习">&lt;/a>学习&lt;/h2>&lt;p>今年看了很多C++的资料，对C++和编译器都有了更深的了解。想2016年半夜&lt;a href="https://zcc.rs/">@zccrs&lt;/a>在家教我编译原理，到现在我可以理解一门语言从设计到实现，再到使用模板完成编译时计算，我走了快三年，这三年里我一直没停下学习的脚步，在学习各种知识，从各种编程语言到各种框架原理，到图形界面的实现。还学习了单元测试，并且&lt;a href="https://hualet.org/">@hualet&lt;/a>大佬给我讲了单元测试是什么，以及单元测试的重要性，从那以后我才算真正的了解单元测试的重要性，也使我在写代码的时候注重通过单元测试来保障我的功能。&lt;/p>&lt;p>去年对深度学习进行了一波学习，今年对Web工程化和TypeScript也学习了一下，也算是对目前最热门的两个领域进行了一定的了解。&lt;/p>&lt;h2 id="读书">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#读书" title="读书">&lt;/a>读书&lt;/h2>&lt;p>去年买的TensorFlow看了没一半，今年倒是没买书，开始在微信读书上读书，利用一些空余时间读一点，我也推荐大家多利用空闲时间读读书，少刷抖音和bilibili。&lt;/p>&lt;ul>&lt;li>《TensorFlow》未读完&lt;/li>&lt;li>《TypeScript实战》正在读&lt;/li>&lt;/ul>&lt;h2 id="博客">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#博客" title="博客">&lt;/a>博客&lt;/h2>&lt;p>2019年我一共水了15篇文章。&lt;/p>&lt;ul>&lt;li>&lt;p>12-26 &lt;a href="https://blog.justforlxz.com/2019/12/26/use-serial-port-debug-deepin-on-archlinux/">在ArchLinux通过串口调试VMware虚拟机中的deepin&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>12-26 &lt;a href="https://blog.justforlxz.com/2019/12/26/cpp-sort/">使用标准库std::sort函数进行排序&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>12-09 &lt;a href="https://blog.justforlxz.com/2019/12/09/%E8%AE%B0%E5%BD%95%E4%B8%80%E4%B8%AA%E5%9D%91%E7%88%B9%E7%9A%84usb%E7%BD%91%E5%8D%A1/">记录一个坑爹的usb网卡&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>12-09 &lt;a href="https://blog.justforlxz.com/2019/12/09/%E8%AE%B0%E5%BD%95%E4%B8%80%E4%B8%AA%E5%9D%91%E7%88%B9%E7%9A%84usb%E7%BD%91%E5%8D%A1/">使用github actions自动部署hexo文章到html仓库&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>12-08 &lt;a href="https://blog.justforlxz.com/2019/12/08/vue-component-props/">Vue父子组件传值 —— props &amp;amp; $emit&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>12-08 &lt;a href="https://blog.justforlxz.com/2019/12/08/vue-transitions/">添加Vue动画&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>11-25 &lt;a href="https://blog.justforlxz.com/2019/11/25/%E4%BD%BF%E7%94%A8webpack-dev-server%E6%9D%A5%E7%9B%91%E5%90%AC%E9%A1%B9%E7%9B%AE%E5%8F%98%E5%8C%96/">使用webpack-dev-server来监听项目变化&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>10-24 &lt;a href="https://blog.justforlxz.com/2019/10/24/%E7%BB%99Archlinux%E5%BC%80%E5%90%AFBFQ%E5%92%8CMuQSS/">给Archlinux开启BFQ和MuQSS&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>10-22 &lt;a href="https://blog.justforlxz.com/2019/10/22/%E4%BD%BF%E7%94%A8webpack%E6%89%93%E5%8C%85Vue%E5%92%8CTypeScript/">使用webpack打包Vue和TypeScript&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>10-14 &lt;a href="https://blog.justforlxz.com/2019/10/14/webpack%E5%85%A5%E9%97%A8/">webpack入门&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>06-16 &lt;a href="https://blog.justforlxz.com/2019/06/16/wsl2%E7%9A%84%E4%BD%BF%E7%94%A8%E4%BD%93%E9%AA%8C/">wsl2的使用体验&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>06-15 &lt;a href="https://blog.justforlxz.com/2019/06/15/%E5%85%A5%E5%9D%91typescript%E4%BA%86/">入坑typescript了&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>05-23 &lt;a href="https://blog.justforlxz.com/2019/05/23/CMake-CTests-for-dde-control-center/">CMake CTests for dde-control-center&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>02-21 &lt;a href="https://blog.justforlxz.com/2019/02/21/how-to-use-LNMP-on-deepin/">如何在Deepin上使用LNMP&lt;/a>&lt;/p>&lt;/li>&lt;li>&lt;p>02-23 &lt;a href="https://blog.justforlxz.com/2019/01/23/fuck-taobao/">解决用了xposed后淘宝闪退&lt;/a>&lt;/p>&lt;/li>&lt;/ul>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">2019&lt;/span>&lt;br />&lt;span class="line">├── 01&lt;/span>&lt;br />&lt;span class="line">│   └── 23&lt;/span>&lt;br />&lt;span class="line">│   └── fuck-taobao&lt;/span>&lt;br />&lt;span class="line">│   └── index.html&lt;/span>&lt;br />&lt;span class="line">├── 02&lt;/span>&lt;br />&lt;span class="line">│   └── 21&lt;/span>&lt;br />&lt;span class="line">│   └── how-to-use-LNMP-on-deepin&lt;/span>&lt;br />&lt;span class="line">│   └── index.html&lt;/span>&lt;br />&lt;span class="line">├── 05&lt;/span>&lt;br />&lt;span class="line">│   └── 23&lt;/span>&lt;br />&lt;span class="line">│   └── CMake-CTests-for-dde-control-center&lt;/span>&lt;br />&lt;span class="line">│   └── index.html&lt;/span>&lt;br />&lt;span class="line">├── 06&lt;/span>&lt;br />&lt;span class="line">│   ├── 15&lt;/span>&lt;br />&lt;span class="line">│   │   └── 入坑typescript了&lt;/span>&lt;br />&lt;span class="line">│   │   ├── 深度录屏_选择区域_20190615202044.gif&lt;/span>&lt;br />&lt;span class="line">│   │   └── index.html&lt;/span>&lt;br />&lt;span class="line">│   └── 16&lt;/span>&lt;br />&lt;span class="line">│   └── wsl2的使用体验&lt;/span>&lt;br />&lt;span class="line">│   ├── index.html&lt;/span>&lt;br />&lt;span class="line">│   ├── Snipaste_2019-06-16_21-49-15.png&lt;/span>&lt;br />&lt;span class="line">│   └── Snipaste_2019-06-16_21-52-42.png&lt;/span>&lt;br />&lt;span class="line">├── 10&lt;/span>&lt;br />&lt;span class="line">│   ├── 14&lt;/span>&lt;br />&lt;span class="line">│   │   └── webpack入门&lt;/span>&lt;br />&lt;span class="line">│   │   └── index.html&lt;/span>&lt;br />&lt;span class="line">│   ├── 22&lt;/span>&lt;br />&lt;span class="line">│   │   └── 使用webpack打包Vue和TypeScript&lt;/span>&lt;br />&lt;span class="line">│   │   └── index.html&lt;/span>&lt;br />&lt;span class="line">│   └── 24&lt;/span>&lt;br />&lt;span class="line">│   └── 给Archlinux开启BFQ和MuQSS&lt;/span>&lt;br />&lt;span class="line">│   └── index.html&lt;/span>&lt;br />&lt;span class="line">├── 11&lt;/span>&lt;br />&lt;span class="line">│   └── 25&lt;/span>&lt;br />&lt;span class="line">│   └── 使用webpack-dev-server来监听项目变化&lt;/span>&lt;br />&lt;span class="line">│   └── index.html&lt;/span>&lt;br />&lt;span class="line">└── 12&lt;/span>&lt;br />&lt;span class="line"> ├── 08&lt;/span>&lt;br />&lt;span class="line"> │   ├── vue-component-props&lt;/span>&lt;br />&lt;span class="line"> │   │   └── index.html&lt;/span>&lt;br />&lt;span class="line"> │   └── vue-transitions&lt;/span>&lt;br />&lt;span class="line"> │   └── index.html&lt;/span>&lt;br />&lt;span class="line"> ├── 09&lt;/span>&lt;br />&lt;span class="line"> │   ├── 记录一个坑爹的usb网卡&lt;/span>&lt;br />&lt;span class="line"> │   │   └── index.html&lt;/span>&lt;br />&lt;span class="line"> │   └── use-github-actions-to-depoly-hexo&lt;/span>&lt;br />&lt;span class="line"> │   └── index.html&lt;/span>&lt;br />&lt;span class="line"> └── 26&lt;/span>&lt;br />&lt;span class="line"> ├── cpp-sort&lt;/span>&lt;br />&lt;span class="line"> │   └── index.html&lt;/span>&lt;br />&lt;span class="line"> └── use-serial-port-debug-deepin-on-archlinux&lt;/span>&lt;br />&lt;span class="line"> └── index.html&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">34 directories, 18 files&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h2 id="科普视频">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#科普视频" title="科普视频">&lt;/a>科普视频&lt;/h2>&lt;ol>&lt;li>妈咪叔 (一个较真的理工男)&lt;/li>&lt;/ol>&lt;p>这个名字我第一眼看到的时候，还以为是个卖母婴的，没想到居然是个搞科普的，而且内容讲的也很好，有数学、物理、化学和天文学。&lt;/p>&lt;ol start="2">&lt;li>李永乐老师&lt;/li>&lt;/ol>&lt;p>以前偶尔看过老师的视频，因为一直都在热榜，所以没想到关注，后来是youtube上看到了，就点了关注，youtube上更新的和bilibili的还不是一样的，看最后结尾的时候youtube的只说youtube帐号关注，而bilibili的是bilibili，有时候还要多个平台去看。&lt;/p>&lt;ol start="3">&lt;li>萝王二号&lt;/li>&lt;/ol>&lt;p>之前在科普区随便看的时候，对生物学产生了一些兴趣，萝王讲的风格我很喜欢，特别是他注重昆虫分类学(骨包皮，皮包骨啊2333)，还有一些辟谣视频。&lt;/p>&lt;ol start="4">&lt;li>芳斯塔夫 (鬼古)&lt;/li>&lt;/ol>&lt;p>也使对生物学产生了一些兴趣，鬼古说以他很中二的风格带领我学习了一波古生物的相关知识(旧日支配者！！！)。&lt;/p>&lt;ol start="5">&lt;li>木偶君&lt;/li>&lt;/ol>&lt;p>和鬼古一样是专门讲古生物的，不过每次结束的比较仓促，突然就结束了。&lt;/p>&lt;ol start="6">&lt;li>木鱼水心&lt;/li>&lt;/ol>&lt;p>木鱼并不是今年才关注的，最开始关注是他做EVA剧场版解析，后来《木鱼说》开始做一些科普，我开始一直关注了。&lt;/p>&lt;ol start="7">&lt;li>宇宙视觉 (永远不要停止思考)&lt;/li>&lt;/ol>&lt;p>一个讲天文的科普up，不过年底的时候换了配音，疑似配音出去单干了。&lt;/p>&lt;h2 id="电影">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#电影" title="电影">&lt;/a>电影&lt;/h2>&lt;ol>&lt;li>流浪地球&lt;/li>&lt;li>阿丽塔：战斗天使&lt;/li>&lt;li>战狼2&lt;/li>&lt;li>惊奇队长&lt;/li>&lt;li>复仇者联盟4&lt;/li>&lt;li>何以为家&lt;/li>&lt;li>速度与激情： 特别行动&lt;/li>&lt;li>叶问4&lt;/li>&lt;/ol>&lt;h2 id="纪录片">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#纪录片" title="纪录片">&lt;/a>纪录片&lt;/h2>&lt;ol>&lt;li>混沌：数学探秘&lt;/li>&lt;li>维度：数学漫步&lt;/li>&lt;/ol>&lt;h2 id="动漫">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#动漫" title="动漫">&lt;/a>动漫&lt;/h2>&lt;ol>&lt;li>刀剑神域&lt;/li>&lt;li>紫罗兰永恒花园&lt;/li>&lt;li>darling in the franxx&lt;/li>&lt;li>心理测量者&lt;/li>&lt;li>进击的巨人第三季&lt;/li>&lt;li>五等分的新娘&lt;/li>&lt;li>citrus～柑橘味香气～&lt;/li>&lt;/ol></description><author>justforlxz</author></item><item><title>在ArchLinux通过串口调试VMware虚拟机中的deepin</title><link>https://blog.mkacg.com/2019/12/26/use-serial-port-debug-deepin-on-archlinux/</link><pubDate>Thu, 26 Dec 2019 09:26:13 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2019/12/26/use-serial-port-debug-deepin-on-archlinux/</guid><description>&lt;p>电脑主板上的接口：进行串行传输的接口，它一次只能传输1Bit。串行端口可以用于连接外置调制解调器、绘图仪或串行打印机。它也可以控制台连接的方式连接网络设备，例如路由器和交换机，主要用来配置它们。消费性电子已经由USB取代串列接口；但在非消费性用途，如网络设备等，串列接口仍是主要的传输控制方式。&lt;/p>&lt;span id="more">&lt;/span>&lt;p>首先给虚拟机分配一个串口设备，选择Settings-&amp;gt;Add-&amp;gt;Serial Port。分配好串口设备以后，我们需要选择一个串口设备的调试方式，一个是将输出转向一个文件，或者是通过socket。&lt;/p>&lt;p>如果只是查看方式，选择outpu file即可。如果需要调试，则可以通过socket方式来进行。&lt;/p>&lt;p>socket方式需要给一个固定的路径分配&amp;#x2f;tmp&amp;#x2f;&lt;socket>，我调试的时候给出的是&amp;#x2f;tmp&amp;#x2f;vhost，From选择Server，To选择An Application。From的意思是信息从哪里来，信息是虚拟机里的系统发出的，所以这里选择的是Server，如果是反向操作，需要选择Client。To也是有两个选项，第一个是An Virtual Machine，第二个是An Application。用于把消息发送给另外的虚拟机，或者是宿主机的一个应用程序。&lt;/p>&lt;p>安装minicom包，用于进行调试，minicom这个东西，不是太好用，退出方式是先按Ctrl+A，然后按q，有时候还不一定管用，不知道是没接受到，还是按错了。&lt;/p>&lt;p>先minicom -s 进行初始化，选择&lt;code>Serial port setup&lt;/code>，按A编辑&lt;code>Serial Device&lt;/code>，这里需要注意一下，通过socket进行调试，需要使用&lt;code>unix#&lt;/code>前缀，然后加上在虚拟机里写的路径 &lt;code>unix#/tmp/vhost&lt;/code>。然后保存，选择Exit，退出以后其实重启minicom，就进入minicom的调试界面了，然后此时开启虚拟机，给内核添加一个console&amp;#x3d;ttyS0的参数，就看到minicom显示输出的信息了，还可以交互。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">[ 3.855725] [drm:vmw_fb_setcolreg [vmwgfx]] *ERROR* Bad regno 254.&lt;/span>&lt;br />&lt;span class="line">[ 3.857125] [drm:vmw_fb_setcolreg [vmwgfx]] *ERROR* Bad regno 255.&lt;/span>&lt;br />&lt;span class="line">deepin Login:&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">CTRL-A Z for help | unix-socket | NOR | Minicom 2.7.1 | VT102 | Offline | unix#/tmp/vhost&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>此时就可以交互了，用法和tty一样，最后一行是minicom的输出，可以看到CTRL-A Z可以看help，minicom的版本，和访问的串口socket。&lt;/p></description><author>justforlxz</author></item><item><title>开发DDE的屏保</title><link>https://blog.deepin.org/posts/screensaver/</link><pubDate>Tue, 03 Sep 2019 12:41:00 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/screensaver/</guid><description>&lt;p>还记得刚推出屏幕保护功能那会儿，我偶逛论坛，围观大家对这个功能的评价。其中让我印象最深的一句话就是：“一股Windows98风”，总之，评价总结出来就是一个字：“吃藕”。&lt;/p>
&lt;p>大家追求美好事物的诚挚之心深深地打动了我，而且，我个人做事情的风格是喜欢未雨绸缪，在屏幕保护程序开发之初，就已经定好了易于扩展的架构，所以我当时就下定了决心，为大家开发一个非Windows98风格的屏保。&lt;/p>
&lt;p>为了达到绝对“非Windows98”的目的，我特意选择了Windows10中的默认屏保作为参考，在无数个周末的战斗下，最终成功将名为“泡泡”的屏保应用发布到了商店（项目地址：&lt;a href="https://github.com/zccrs/screensaver-pp">https://github.com/zccrs/screensaver-pp&lt;/a> ）。&lt;/p>
&lt;p>本着“授人以鱼不如授人以渔”的理念，特地整理了这篇文章协助大家开发一款属于自己的时尚屏保应用。&lt;/p>
&lt;h2 id="正文">正文
&lt;span>
&lt;a href="https://blog.deepin.org/posts/screensaver/#%e6%ad%a3%e6%96%87">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>在Linux+X11生态环境中，xscreensaver是最“流行”的屏幕保护程序，有着非常多的屏保资源，所以deepin-screensaver必然要兼容它的资源。&lt;/p>
&lt;p>但是，xscreensaver对屏保资源的扩展方式并不符合deepin的开发理念，因此，deepin-screensaver实现了一套全新的屏保扩展方式。&lt;/p>
&lt;p>支持使用Qt qml模块编写屏保应用，一个标准的屏保应用只需要包含一个 &amp;ldquo;xx.rcc&amp;rdquo; 文件，将文件安装到 /usr/lib/deepin-screensaver/resources目录。&lt;/p>
&lt;p>rcc 格式是一个编译之后的Qt资源文件，在这个资源文件中至少要包含两个文件：qml代码文件、屏保封面图。&lt;/p>
&lt;p>&lt;img alt="image" src="https://user-images.githubusercontent.com/13449038/218910865-688f689f-6d0c-49dd-b916-5b6a75bd354c.png" />&lt;/p>
&lt;p>图中文件名括号内为其别名，也就是屏保主应用加载文件时能读取到的文件名。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>qml代码文件：屏保应用的代码入口，会被屏保主程序加载显示&lt;/p>
&lt;/li>
&lt;li>
&lt;p>屏保封面图：设置屏保入口显示的预览图，支持svg png jpeg bmp等格式
所有的文件必须以特定的目录结构组织到一个Qt资源文件（qrc文件），以“泡泡”屏保为例：qml.qrc 为其资源文件，包含三个前缀路径&lt;/p>
&lt;/li>
&lt;li>
&lt;p>/deepin-screensaver/modules：放置屏保应用的主qml文件，此路径下的所有qml文件都会被当做一个独立的屏保应用，因此，项目中的其它文件需要额外建立新的前缀放置&lt;/p>
&lt;/li>
&lt;li>
&lt;p>/deepin-screensaver/modules/cover：放置屏保应用封面图文件，文件名称必须和modules目录中的qml文件一致，且包含它的 &amp;ldquo;.qml&amp;rdquo; 后缀。如图上，qml文件全名为：&amp;ldquo;pp.qml&amp;rdquo;，封面图全名为：&amp;ldquo;pp.qml.svg&amp;rdquo;。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>/deepin-screensaver/modules/pp：此前缀不是必须的，用于放置项目中的其它文件。为了不与其它项目产生冲突，建议使用项目名作为目录名称
资源文件最好以项目名称命名，避免和其它屏保应用冲突。另外，大家可能已经发现了，这三个前缀都有一个共同点，那就是以 &amp;ldquo;/deepin-screensaver/modules&amp;quot;开头，的确，这是一个格式要求，不能随意更改路径。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>主qml文件作为屏保应用的入口，它的根元素一定要设置&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">anchors.fill: parent
&lt;/code>&lt;/pre>&lt;/div>&lt;p>这样才能确保屏保应用充满整个屏幕。在多屏的情况下下，会创建多个窗口示例，可根据屏幕绘制不同的屏保内容。
项目编译其实很简单，只需要使用Qt提供的rcc命令将qrc文件编译为rcc文件即可，使用qmake构建系统时，可以在pro文件中调用以下命令：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">system(rcc --binary $$_PRO_FILE_PWD_/xx.qrc -o $$_PRO_FILE_PWD_/xx.rcc)
&lt;/code>&lt;/pre>&lt;/div>&lt;p>当然，最后不要忘记将 xx.rcc 文件安装到deepin-screensaver所要求的目录。做完这所有的步骤后，回到桌面，在右键菜单中选择“壁纸与屏保”，切换到屏保设置后即可看到新添加的屏保应用。
另外，deepin-screensaver为qml提供了获取当前屏幕截图的接口，只需要为Image项指定特定的路径即可：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">Image &amp;#123;
anchors.fill: parent
source: "image://deepin-screensaver/screen/" + Screen.name
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>由于要获取屏幕名称，上述代码需要 &amp;ldquo;import QtQuick.Window 2.2&amp;rdquo; 使用&lt;/p>
&lt;h2 id="后记">后记
&lt;span>
&lt;a href="https://blog.deepin.org/posts/screensaver/#%e5%90%8e%e8%ae%b0">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>屏保封面图最佳比例为：8:5，推荐使用svg格式，以更好的适应高分屏缩放。&lt;/p>
&lt;p>推荐大家使用Qt Creator作为项目的开发工具，可以方便的编辑 qrc 文件。&lt;/p>
&lt;p>泡泡屏保是一个完整的demo，有任何疑问的地方都可以以其作为参考&lt;/p>
&lt;h2 id="参考">参考
&lt;span>
&lt;a href="https://blog.deepin.org/posts/screensaver/#%e5%8f%82%e8%80%83">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>“泡泡”屏保项目：&lt;a href="https://github.com/zccrs/screensaver-pp">https://github.com/zccrs/screensaver-pp&lt;/a>&lt;/li>
&lt;li>Qt资源文件：&lt;a href="https://doc.qt.io/qt-5/resources.html">https://doc.qt.io/qt-5/resources.html&lt;/a>&lt;/li>
&lt;/ul></description><author>deepin 社区官方博客</author></item><item><title>如何在Deepin上使用LNMP</title><link>https://blog.mkacg.com/2019/02/21/how-to-use-LNMP-on-deepin/</link><pubDate>Thu, 21 Feb 2019 02:11:15 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2019/02/21/how-to-use-LNMP-on-deepin/</guid><description>&lt;p>为了节省读者的时间，我先简述一下阅读这篇文章需要了解的知识。&lt;/p>&lt;p>这篇文章将基于Docker来构建nginx、php和mysql来搭建LNMP环境，和其他教程有所不同的是，需要有一定的Docker基础。&lt;/p>&lt;span id="more">&lt;/span>&lt;p>Docker是一个不错的工具，使我们不需要虚拟机那样的庞然大物就可以轻松的隔离运行的程序，这要感谢Linux的资源分离机制，避免启动一个虚拟机造成了大量资源浪费。&lt;/p>&lt;p>首先需要在Deepin上安装Docker，添加Docker的deb仓库，并安装docker-ce。&lt;/p>&lt;p>创建文件&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo nano /etc/apt/sources.list.d/docker.list&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>写入&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">deb [arch=amd64] https://download.docker.com/linux/debian jessie edge&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>刷新一下仓库就可以安装了。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo apt update &amp;amp;&amp;amp; sudo apt install docker-ce docker-compose&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>安装完成后重启一下系统，准备工作就算完成了一半了。&lt;/p>&lt;p>在家目录创建一个Projects目录，当做我们LNMP的工作目录，创建一个名叫&lt;em>docker-compose.yaml&lt;/em>的文件，这是docker-compose的配置文件，我们通过docker-compose这个工具来管理我们的Docker容器。&lt;/p>&lt;p>所有的镜像均采用最新版本，nginx(1.15.8)，php(7.3.2)，mysql(8.0.15)，如有需要，自行选择不同版本的镜像。&lt;/p>&lt;p>注意PHP7已经不支持mysql扩展，使用内置的MySQLnd。&lt;/p>&lt;p>写入以下配置文件：&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">version: &amp;#x27;3&amp;#x27;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">services:&lt;/span>&lt;br />&lt;span class="line"> nginx:&lt;/span>&lt;br />&lt;span class="line"> # 设置容器名字&lt;/span>&lt;br />&lt;span class="line"> container_name: &amp;quot;nginx&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> # 采用最新的nginx&lt;/span>&lt;br />&lt;span class="line"> image: nginx:latest&lt;/span>&lt;br />&lt;span class="line"> # 绑定80端口&lt;/span>&lt;br />&lt;span class="line"> ports:&lt;/span>&lt;br />&lt;span class="line"> - &amp;quot;80:80&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> # 添加php容器的依赖&lt;/span>&lt;br />&lt;span class="line"> depends_on:&lt;/span>&lt;br />&lt;span class="line"> - &amp;quot;php&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> # 绑定数据目录&lt;/span>&lt;br />&lt;span class="line"> volumes:&lt;/span>&lt;br />&lt;span class="line"> - &amp;quot;./volumes/nginx/conf.d:/etc/nginx/conf.d&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> - &amp;quot;./volumes/html:/usr/share/nginx/html&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> restart: always&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> php:&lt;/span>&lt;br />&lt;span class="line"> # 设置容器名字&lt;/span>&lt;br />&lt;span class="line"> container_name: &amp;quot;php&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> # 采用最新的php&lt;/span>&lt;br />&lt;span class="line"> image: php:fpm&lt;/span>&lt;br />&lt;span class="line"> # 绑定端口&lt;/span>&lt;br />&lt;span class="line"> ports:&lt;/span>&lt;br />&lt;span class="line"> - &amp;quot;9000:9000&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> # 绑定数据目录&lt;/span>&lt;br />&lt;span class="line"> volumes:&lt;/span>&lt;br />&lt;span class="line"> - &amp;quot;./volumes/html:/var/www/html&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> restart: always&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> mysql:&lt;/span>&lt;br />&lt;span class="line"> # 设置容器名字&lt;/span>&lt;br />&lt;span class="line"> container_name: &amp;quot;mysql&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> # 采用最新的mysql&lt;/span>&lt;br />&lt;span class="line"> image: mysql:latest&lt;/span>&lt;br />&lt;span class="line"> # 绑定端口&lt;/span>&lt;br />&lt;span class="line"> ports:&lt;/span>&lt;br />&lt;span class="line"> - &amp;quot;3306:3306&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> # 设置环境变量&lt;/span>&lt;br />&lt;span class="line"> environment:&lt;/span>&lt;br />&lt;span class="line"> - MYSQL_ROOT_PASSWORD=(自己设置密码)&lt;/span>&lt;br />&lt;span class="line"> # 绑定数据目录&lt;/span>&lt;br />&lt;span class="line"> volumes:&lt;/span>&lt;br />&lt;span class="line"> - &amp;quot;./volumes/mysql:/var/lib/mysql&amp;quot;&lt;/span>&lt;br />&lt;span class="line"> restart: always&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>创建nginx的配置文件，编辑 &lt;em>.&amp;#x2f;volumes&amp;#x2f;nginx&amp;#x2f;conf.d&amp;#x2f;nginx.conf&lt;/em> ：&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">server &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> listen 80;&lt;/span>&lt;br />&lt;span class="line"> server_name localhost;&lt;/span>&lt;br />&lt;span class="line"> location / &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> root /usr/share/nginx/html;&lt;/span>&lt;br />&lt;span class="line"> index index.html index.htm index.php;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> error_page 500 502 503 504 /50x.html;&lt;/span>&lt;br />&lt;span class="line"> location = /50x.html &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> root /usr/share/nginx/html;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line"> location ~ \.php$ &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> fastcgi_pass php:9000;&lt;/span>&lt;br />&lt;span class="line"> fastcgi_index index.php;&lt;/span>&lt;br />&lt;span class="line"> fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;&lt;/span>&lt;br />&lt;span class="line"> include fastcgi_params;&lt;/span>&lt;br />&lt;span class="line"> &amp;#125;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>创建php测试文件，编辑 &lt;em>.&amp;#x2f;volumes&amp;#x2f;html&amp;#x2f;index.php&lt;/em> :&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">&amp;lt;?php&lt;/span>&lt;br />&lt;span class="line">phpinfo();&lt;/span>&lt;br />&lt;span class="line">?&amp;gt;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>启动docker，第一次需要拉取一下镜像:&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">docker-compose up --build -d&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>等全部结束以后，就可以访问localhost看到php的信息了。&lt;/p>&lt;p>通过Docker的方法来使用LNMP，不污染宿主机环境，不会再因为各种依赖问题而搞坏系统，这恰恰是新手容易犯的错误，使用Docker，方便你我。&lt;/p></description><author>justforlxz</author></item><item><title>修理 FreeRDP</title><link>https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/</link><pubDate>Mon, 17 Dec 2018 06:00:00 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/</guid><description>&lt;p>头段时间入了一个大坑儿，大概被坑了有一个月之久，出来之后同事还不忘嘲讽一番：”这么个事情就搞了一个月，看吧，你果然是老了“。听了这句话，心里真是百般滋味，但转念一想，”我年轻的时候做事好像也不怎么快“，顿时也就释怀了 🙂&lt;/p>
&lt;p>这个坑就是”给 FreeRDP 的 RAIL 模式添加托盘支持“，当然，跟所有的需求一样，这么具有总结性而又直指根源的需求描述，绝对不是它最原始的模样——我刚接到这个坑的时候，它是这样的：FreeRDP 的 RAIL 模式下，应用的托盘在我们 DDE 下不显示。请注意这里说得是&lt;strong>不显示&lt;/strong>，而不是后来发现的&lt;strong>压根儿没有支持&lt;/strong>！&lt;/p>
&lt;h3 id="freerdp">FreeRDP
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#freerdp">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>说到这，可能有读者还不了解 FreeRDP 和 RAIL，所以先简单介绍一下。&lt;/p>
&lt;p>RDP 其实是一个协议名称，全称 Remote Desktop Protocol（远程桌面协议），是微软公司开发的一套用于远程桌面展示和操作的协议，FreeRDP 就是它在开源世界的实现咯。而 RAIL 的全称是 Remote Application Integrated Locally （远程应用本地集成），其实就是非常类似大家熟悉的虚拟机的”无缝模式“，通过将应用的显示跟本地环境相融合，让用户完全感受不到这个应用其实不在本机运行——就是这么一种技术。&lt;/p>
&lt;p>问题也就出在这，我当时第一反应是这么老的技术实现肯定比较完整了，托盘没有显示出来应该是跟 DDE 的兼容性有点小问题，稍微修一下就完了，三下五除二的事情，所以满口答应了下来……&lt;/p>
&lt;h2 id="经过">经过
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#%e7%bb%8f%e8%bf%87">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>既然答应了，硬着头皮也要顶下去的。何况调 BUG 这种事情——不管是不是我们自己的问题——在深度都是家常便饭。慢慢地，调各种项目的 BUG 竟然成了我的一种乐趣——每次开始接手一个新的项目的时候，我都把自己当成了福尔摩斯或者胡八一，或者也可以是其他全世界最聪明的那类人 ?，在通过代码找寻问题线索的过程中，慢慢成为这个项目世界中的主宰，解开真相……&lt;/p>
&lt;p>额……不好意思，白日梦又发作了一会儿。总之，这次也不例外，而且刚好这次在调问题的过程中有记录几个关键环节，所以打算把中间的过程写成日记性质的记录，看看能不能有更好的阅读效果：&lt;/p>
&lt;h3 id="2018-11-14">2018-11-14
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#2018-11-14">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>从”沈老板“那收到需求，说 FreeRDP 在我们系统上有问题，应用的托盘显示不出来，QQ之类的程序关闭了窗口以后就没办法显示出来了，无法使用。这丫的又拿刘老大来压我……呵呵，想削他。不过看在他快要当爸爸的份上，还是算了。问了下时间要求，大概需要两周左右有初步的结果。不过我自己最近没有什么时间，先把锅丢给了印象中还比较熟悉网络协议的 @Blumia 同学。&lt;/p>
&lt;h3 id="2018-11-15">2018-11-15
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#2018-11-15">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>从 @Blumia 那收到反馈，可能 FreeRDP 没有实现托盘图标这部分的功能，我怕他一个人搞不定，简单翻了翻 FreeRDP 的项目 wiki 和 RDP 的一些介绍，给了他，让他先帮忙找一下需要补充实现部分的代码结构。&lt;/p>
&lt;h3 id="2018-11-16">2018-11-16
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#2018-11-16">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>没时间处理。&lt;/p>
&lt;p>@Blumia 搭了测试环境。&lt;/p>
&lt;p>&lt;strong>中间几天两&lt;strong>&lt;strong>个&lt;/strong>&lt;/strong>人都没有时间处理 FreeRDP&lt;strong>&lt;strong>的&lt;/strong>&lt;/strong>事情。&lt;/strong>&lt;/p>
&lt;h3 id="2018-11-22">2018-11-22
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#2018-11-22">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>留了少部分时间，看了 FreeRDP 的代码，大概找到了托盘图标相关处理应该在的位置。&lt;/p>
&lt;ul>
&lt;li>RAIL 主要接口的实现都在 &lt;code>xf_rail.c&lt;/code> 中。&lt;/li>
&lt;li>托盘图标相关的处理在 &lt;code>xf_rail_no``t``ify_icon_*&lt;/code> 相关的函数，这些函数在 &lt;code>xf_rail_re``gi``s``t``er_``u``pdate_call``b``a``c``ks&lt;/code> 里面被注册到 &lt;code>rdpWind``o``wUpdate&lt;/code> 对象上。&lt;/li>
&lt;li>部署了一份测试服务器的虚拟机。&lt;/li>
&lt;li>在 &lt;code>wf_rail.``c&lt;/code> 中发现一个 &lt;code>PrintRailI``c``onInfo&lt;/code> 函数，放在 &lt;code>xf_``r``ail_notify_i``c``on_common&lt;/code> 中打印了获取到的图标的信息，发现能正常获取一些图标的数据。&lt;/li>
&lt;li>xf 应该是 x11 freerdp 的缩写，而 wf 应该是 windows freerdp 的缩写。
&lt;strong>中间又是几天没有时间处理 FreeRDP 的事情。&lt;/strong>&lt;/li>
&lt;/ul>
&lt;h3 id="2018-11-27">2018-11-27
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#2018-11-27">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>有半天的时间看 FreeRDP 的代码，同时跟 FreeRDP 的邮件列表发了&lt;a href="https://sourceforge.net/p/freerdp/mailman/message/36477842/">邮件&lt;/a>询问相关技术问题，主要是为了验证自己的想法，没有指望有回复或者什么比较大用处的信息，只是希望如果自己想法是错的，有人及时纠正一下。&lt;/p>
&lt;ul>
&lt;li>图标显示的问题不打算优先处理，现在的问题变成如何让服务端知道了本地用户点了托盘图标。&lt;/li>
&lt;li>搜了一下 event 相关的文件，发现 &lt;code>x``f_event.c&lt;/code> ，怀疑 X 相关的事件都是在这里面处理的，这个也不用急着去证明，先看看 client 怎么让 server 感知本地的事件。&lt;/li>
&lt;li>没有头绪，只好看了一下 RAIL 的 &lt;a href="https://msdn.microsoft.com/en-us/library/cc242568/">主要协议&lt;/a>，发现 &lt;code>Cli``e``nt Notify Ev``en``t&lt;/code>。&lt;/li>
&lt;li>怀疑托盘图标在 client 端（本地端）的事件是通过 ClientNotifyEvent 发送给 server 端的。&lt;/li>
&lt;li>ClientNotifyEvent 相关：
&lt;ul>
&lt;li>&lt;code>rail_main.``c&lt;/code> 中的 &lt;code>Vi``r``tualChann``e``lE``n``tryEx&lt;/code> 应该是 RDP 中 RAIL 相关的 channel 处理的函数。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="2018-11-28">2018-11-28
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#2018-11-28">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>上午继续看了 FreeRDP 的代码。&lt;/p>
&lt;ul>
&lt;li>&lt;code>V``i``rtualCha``n``nelEntryEx&lt;/code> 中给 &lt;code>RailClientContext&lt;/code> 设置的哪些成员函数，有些函数（Server开头的）都是需要真正的 client 去实现的，Client 开头的函数（包括 ClientNotifyEvent）都是默认有实现，但是这些 Client 开头的函数都是在哪调用的呢？
找到重要线索：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">/**
* The position of the X window can become out of sync with the RDP window
* if the X window is moved locally by the window manager. In this event
* send an update to the RDP server informing it of the new window position
* and size.
*/
void xf_rail_adjust_position(xfContext* xfc, xfAppWindow* appWindow)
&amp;#123;
RAIL_WINDOW_MOVE_ORDER windowMove;
if (!appWindow-&amp;gt;is_mapped || appWindow-&amp;gt;local_move.state != LMS_NOT_ACTIVE)
return;
/* If current window position disagrees with RDP window position, send update to RDP server */
if (appWindow-&amp;gt;x != appWindow-&amp;gt;windowOffsetX ||
appWindow-&amp;gt;y != appWindow-&amp;gt;windowOffsetY ||
appWindow-&amp;gt;width != appWindow-&amp;gt;windowWidth ||
appWindow-&amp;gt;height != appWindow-&amp;gt;windowHeight)
&amp;#123;
windowMove.windowId = appWindow-&amp;gt;windowId;
/*
* Calculate new size/position for the rail window(new values for windowOffsetX/windowOffsetY/windowWidth/windowHeight) on the server
*/
windowMove.left = appWindow-&amp;gt;x;
windowMove.top = appWindow-&amp;gt;y;
windowMove.right = windowMove.left + appWindow-&amp;gt;width;
windowMove.bottom = windowMove.top + appWindow-&amp;gt;height;
xfc-&amp;gt;rail-&amp;gt;ClientWindowMove(xfc-&amp;gt;rail, &amp;amp;windowMove);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>其中有主动调用 &lt;code>RailClientContext&lt;/code> 的 &lt;code>ClientWindowMove&lt;/code> 函数。这个函数又是 &lt;code>xf_``e``v``en``t.``c&lt;/code> 中 &lt;code>xf_``e``v``en``t_C``o``nfig``u``r``e``Notify&lt;/code> 有调用，再加上这个函数的注释说明，差不多能证明所有的 X事件相关的都是在 &lt;code>xf_event.c&lt;/code> 中处理的，跟之前的猜测一致。&lt;/li>
&lt;li>那样的话如果想发送事件到 server，应该就是在 &lt;code>xf_event.c&lt;/code> 中收到我们自己创建的托盘图标的点击事件后，发送一个 ClientNofityEvent 。
尝试在收到 &lt;code>xf_r``a``il_``no``ti``f``y_``i``con_u``p``d``a``te&lt;/code> 的时候主动调用一次 &lt;code>Cli``ent``NotifyEvent&lt;/code> 看看会发生什么。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">RAIL_NOTIFY_EVENT_ORDER notifyEvent;
notifyEvent.windowId = orderInfo-&amp;gt;windowId;
notifyEvent.notifyIconId = orderInfo-&amp;gt;notifyIconId;
notifyEvent.message = NIN_SELECT;
xfContext* xfc = (xfContext*) context;
xfc-&amp;gt;rail-&amp;gt;ClientNotifyEvent(xfc-&amp;gt;rail, &amp;amp;notifyEvent);
&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>选择 message 为 &lt;code>NIN_SELECT&lt;/code> 是因为根据 &lt;code>r``ail.h&lt;/code> 里面仅有的零星注释，只能推测这个可能是针对托盘的。&lt;/li>
&lt;li>试了下，没有任何反应。尝试换成 NIN_KEYSELECT ，更不行。&lt;/li>
&lt;li>硬着头皮又翻了一下协议，发现 &lt;code>Notificat``i``o``n``I``c``on Info``r``mation&lt;/code> 这段，随便翻了一下，看起来没有有用信息。&lt;/li>
&lt;li>偶发奇想搜了一下 select 关键字想看一下这个到底是什么意思，偶然发现 &lt;code>Cli``en``t Notify E``ve``nt PDU&lt;/code> 这一节（能跟源码 &lt;code>r``ai``l``.h&lt;/code> 里面的一些注释对应上），里面有 WM_LBUTTONDOWN 、 WM_LBUTTONUP 等针对托盘图标的动作定义。&lt;/li>
&lt;li>真是对自己做事情毛毛躁躁的行为无语了，要不是心血来潮，差点就错过这么重要的信息。&lt;/li>
&lt;li>把 message 改成 &lt;code>WM_LBUTTONDOWN&lt;/code> 和 &lt;code>WM_LBUTTONUP&lt;/code> ，满怀期待。&lt;/li>
&lt;li>测试还是无效果……仍旧不死心，怀疑测试程序（@Blumia 同学搞的一个音乐程序）的稳健程度。&lt;/li>
&lt;li>使用 TIM 再试，还是不行，不能唤出主窗口。&lt;/li>
&lt;li>感觉走进了死胡同。&lt;/li>
&lt;/ul>
&lt;h3 id="2018-11-29">2018-11-29
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#2018-11-29">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>继续看 FreeRDP 的问题，主窗口隐藏后不能显示的问题太奇怪了，得找一个简单点的程序，排除复杂影响。&lt;/p>
&lt;ul>
&lt;li>让 @zccrs 写了一个简单的窗口程序，定时隐藏、显示窗口。&lt;/li>
&lt;li>发现程序窗口隐藏后无法再显示出来……&lt;/li>
&lt;li>赶紧给上游报了一个 &lt;a href="https://github.com/FreeRDP/FreeRDP/issues/5078">issu&lt;/a>&lt;a href="https://github.com/FreeRDP/FreeRDP/issues/5078">e&lt;/a> ，希望上游能修复。但是也不能期望上游很快能修复这个问题，所以自己还是尝试看代码……&lt;/li>
&lt;li>上游回复还挺快的，但是对方好像是 FreeRDP 目前的维护者，说自己对 RAIL 这部分协议本身还不是特别熟悉。&lt;/li>
&lt;li>不能依赖的上游不是好上游，继续看代码，发现在窗口的显示隐藏主要是通过 &lt;code>WINDOW_STATE_ORDER&lt;/code> 中的 &lt;code>s``h``o``wState&lt;/code> 控制的，处理的函数是 &lt;code>xf_``r``ail_window_``c``o``m``mon&lt;/code> ，里面调用了 &lt;code>xf_ShowWin``d``ow&lt;/code> 这个函数，但是这个函数在该显示窗口的时候只是调整了一下窗口的最大化、最小化状态，并没有 Map 这个本地窗口。&lt;/li>
&lt;li>加了 &lt;code>XMapW``in``dow(xf``c``-&amp;gt;di``s``pl``a``y,&lt;/code> &lt;code>appWi``n``dow-&amp;gt;ha``n``dl``e``)&lt;/code> 这行，满心期待 bugfix。&lt;/li>
&lt;li>编译代码测试，发现窗口连关闭都不能关闭了……&lt;/li>
&lt;li>xfContext 的 appWindow 是本地窗口的一个抽象表示。&lt;/li>
&lt;/ul>
&lt;h3 id="2018-12-03">2018-12-03
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#2018-12-03">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>觉得这个事情没有什么太大的希望了，不过既然已经知道托盘图标的显示方式和事件的发送，但是没有实际实现，到时候”沈老板“来问，也不好说都是在脑子里，干脆先把之前测通但是没有实现的内容实现一下。&lt;/p>
&lt;ul>
&lt;li>托盘窗口加好了，事件也都加上了。&lt;/li>
&lt;li>眼看着都快要完美了，就差那么一点问题没有解决，实在是不甘心，继续死磕那个问题。&lt;/li>
&lt;li>尝试了各种手段调试，跟整个程序的命令传递，都没有能解决问题。&lt;/li>
&lt;/ul>
&lt;h3 id="2018-12-04">2018-12-04
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#2018-12-04">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>调试了一天，一遍又一遍看窗口事件，一点一点排除事件处理函数，终于发现了上游犯的一个低级错误，我很怀疑当时作者有没有测试一下 🙁&lt;/p>
&lt;p>做了修复，提交了 &lt;a href="https://github.com/FreeRDP/FreeRDP/pull/5097/">PR&lt;/a>，并且顺利合并。&lt;/p>
&lt;p>心情终于舒畅了。&lt;/p>
&lt;p>&lt;strong>中间有事请假一天&lt;/strong>&lt;/p>
&lt;h3 id="2018-12-06">2018-12-06
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#2018-12-06">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>托盘图标也画上了，不过怎么感觉颜色有点偏。&lt;/p>
&lt;p>调了一下颜色的格式（RGBA -&amp;gt; BGRA），图标显示正常了。&lt;/p>
&lt;p>事情终于告一段路了。&lt;/p>
&lt;h2 id="结束">结束
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#%e7%bb%93%e6%9d%9f">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>折腾了这么长时间，事情终于搞定了，这应该是最近一年里面时间拉的最长的 BUG 了。&lt;/p>
&lt;p>实现算是完了，也能使用。但是还有一些细节没有特别完善，已提交提交到上游 &lt;a href="https://github.com/FreeRDP/FreeRDP/pull/5110">一个新&lt;/a>&lt;a href="https://github.com/FreeRDP/FreeRDP/pull/5110">的&lt;/a>&lt;a href="https://github.com/FreeRDP/FreeRDP/pull/5110">PR&lt;/a> ，希望能早日合并造福一方用户。&lt;/p>
&lt;h2 id="感想">感想
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/#%e6%84%9f%e6%83%b3">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;p>感觉我之前对 wine 有偏见，一直比较拒绝使用（或者大量使用）wine 的东西，但是实际上在修复 FreeRDP 的过程中，我竟然觉得这也是一种不错的解决方案……仔细想想，还是 wine 方便一点，至少不需要依赖一个服务端。&lt;/p>
&lt;p>准备入坑 wine 啦 ~(≧▽≦)/~&lt;/p></description><author>deepin 社区官方博客</author></item><item><title>使用swapfile来休眠</title><link>https://blog.mkacg.com/2018/12/12/hibernate-for-swapfile/</link><pubDate>Wed, 12 Dec 2018 03:01:55 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2018/12/12/hibernate-for-swapfile/</guid><description>&lt;p>最近deepin要添加休眠功能，但是之前测试的通过swapfile来休眠失败了，所以对正在使用swap分区的用户提供休眠功能。但是昨天我在askubuntu上看到有人发了在ubuntu下通过swapfile休眠的方案，今天试了一下，效果良好，觉得可以考虑给deepin也加上这样的功能。&lt;/p>&lt;span id="more">&lt;/span>&lt;p>原文链接: &lt;a href="https://askubuntu.com/questions/6769/hibernate-and-resume-from-a-swap-file">Hibernate and resume from a swap file&lt;/a>&lt;/p>&lt;p>具体步骤是通过uswsusp这个包来做的，uswsusp是一组用户空间工具，用于Linux系统上的休眠(挂起到磁盘)和挂起(挂起到RAM或待机)。详细内容可以在ArchWiki上参考。&lt;a href="https://wiki.archlinux.org/index.php/Uswsusp">点这里&lt;/a>&lt;/p>&lt;p>先创建一个和内存同等大小的swapfile，为了确保休眠成功，不能小于内存的容量。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo fallocate -l 16g /swapfile # 我的机子是16G，具体自己修改&lt;/span>&lt;br />&lt;span class="line">sudo chmod 600 /swapfile&lt;/span>&lt;br />&lt;span class="line">sudo mkswap /swapfile&lt;/span>&lt;br />&lt;span class="line">sudo swapon /swapfile&lt;/span>&lt;br />&lt;span class="line">echo &amp;#x27;/swapfile swap swap defaults 0 0&amp;#x27; | sudo tee -a /etc/fstab&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>安装用户空间软休眠(Userspace Software Suspend)包&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo apt install uswsusp&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>创建需要的配置文件，只需要创建文件即可。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo touch /etc/uswsusp.conf&lt;/span>&lt;br />&lt;span class="line">sudo dpkg-reconfigure -pmedium uswsusp&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>这时候终端会提醒是否继续，选择Yes，然后会要求你创建一个密码，设置一个密码继续即可。&lt;/p>&lt;p>此时就可以测试一下功能了，不过我是跳过这个步骤了(比较喜欢作死)。&lt;/p>&lt;p>修改systemd的hibernate服务，使用uswsusp来代替systemd的功能。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo systemctl edit systemd-hibernate.service&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>写入以下内容:&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">[Service]&lt;/span>&lt;br />&lt;span class="line">ExecStart=&lt;/span>&lt;br />&lt;span class="line">ExecStart=/usr/sbin/s2disk&lt;/span>&lt;br />&lt;span class="line">ExecStartPost=/bin/run-parts -a post /lib/systemd/system-sleep&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>这时候可以使用systemd的命令来测试的，我表示工作的非常正常。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">systemctl hibernate&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>执行以后可以看到屏幕上会打印当前保存的进度，然后设备就关机了，此时再开机，等待一会儿以后就看到了背景是我漂亮老婆的锁屏，解锁以后看到工作区还是执行命令前的，一切ok。&lt;/p>&lt;p>参考以下内容:&lt;/p>&lt;blockquote>&lt;p>&lt;a href="https://askubuntu.com/questions/6769/hibernate-and-resume-from-a-swap-file">https://askubuntu.com/questions/6769/hibernate-and-resume-from-a-swap-file&lt;/a>&lt;/p>&lt;/blockquote>&lt;blockquote>&lt;p>&lt;a href="https://wiki.archlinux.org/index.php/Uswsusp">https://wiki.archlinux.org/index.php/Uswsusp&lt;/a>&lt;/p>&lt;/blockquote></description><author>justforlxz</author></item><item><title>在DeepinLinux下使用nVidia CUDA</title><link>https://blog.mkacg.com/2018/06/27/%E5%9C%A8DeepinLinux%E4%B8%8B%E4%BD%BF%E7%94%A8nVidia-CUDA/</link><pubDate>Wed, 27 Jun 2018 19:21:50 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2018/06/27/%E5%9C%A8DeepinLinux%E4%B8%8B%E4%BD%BF%E7%94%A8nVidia-CUDA/</guid><description>&lt;p>CUDA（Compute Unified Device Architecture，统一计算架构）是由NVIDIA所推出的一种集成技术，是该公司对于GPGPU的正式名称。通过这个技术，用户可利用NVIDIA的GeForce 8以后的GPU和较新的Quadro GPU进行计算。亦是首次可以利用GPU作为C-编译器的开发环境。NVIDIA营销的时候，往往将编译器与架构混合推广，造成混乱。实际上，CUDA可以兼容OpenCL或者自家的C-编译器。无论是CUDA C-语言或是OpenCL，指令最终都会被驱动程序转换成PTX代码，交由显示核心计算。&lt;/p>&lt;p>在论坛上看到有些用户希望在deepin下使用CUDA，但是他们采取的做法往往是手动下载nvidia的二进制文件，直接进行安装。&lt;/p>&lt;p>但是这样会破坏一部分的glx链接，导致卸载的时候无法彻底恢复，结果就是系统因为卸载nvidia驱动而废掉，所以我推荐使用包管理器的方式安装nvidia驱动和cuda相关的东西，尽量不要手动修改。&lt;/p>&lt;span id="more">&lt;/span>&lt;p>需要安装的很少，只有五个包，不过会依赖很多nvidia的库，总量还是有一些的。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo apt install nvidia-cuda-toolkit nvidia-profiler nvidia-visual-profiler nvidia-cuda-doc nvidia-cuda-dev&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>nvcc是cuda的编译器，它目前只支持g++5，所以还需要安装g++5。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo apt install g++-5&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>然后，重启一下计算机。&lt;/p>&lt;p>&lt;a href="https://bingliu221.gitbooks.io/learn-cuda-the-simple-way/content/chapter2.html/">这里有个小栗子，可以用来测试cuda是否能够成功编译和运行&lt;/a>&lt;/p>&lt;p>&lt;strong>将以下代码保存为 main.cu&lt;/strong>&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">#include &amp;lt;stdio.h&amp;gt;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">__global__ void vector_add(const int *a, const int *b, int *c) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> *c = *a + *b;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">int main(void) &amp;#123;&lt;/span>&lt;br />&lt;span class="line"> const int a = 2, b = 5;&lt;/span>&lt;br />&lt;span class="line"> int c = 0;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> int *dev_a, *dev_b, *dev_c;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> cudaMalloc((void **)&amp;amp;dev_a, sizeof(int));&lt;/span>&lt;br />&lt;span class="line"> cudaMalloc((void **)&amp;amp;dev_b, sizeof(int));&lt;/span>&lt;br />&lt;span class="line"> cudaMalloc((void **)&amp;amp;dev_c, sizeof(int));&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> cudaMemcpy(dev_a, &amp;amp;a, sizeof(int), cudaMemcpyHostToDevice);&lt;/span>&lt;br />&lt;span class="line"> cudaMemcpy(dev_b, &amp;amp;b, sizeof(int), cudaMemcpyHostToDevice);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> vector_add&amp;lt;&amp;lt;&amp;lt;1, 1&amp;gt;&amp;gt;&amp;gt;(dev_a, dev_b, dev_c);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> cudaMemcpy(&amp;amp;c, dev_c, sizeof(int), cudaMemcpyDeviceToHost);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> printf(&amp;quot;%d + %d = %d, Is that right?\n&amp;quot;, a, b, c);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> cudaFree(dev_a);&lt;/span>&lt;br />&lt;span class="line"> cudaFree(dev_b);&lt;/span>&lt;br />&lt;span class="line"> cudaFree(dev_c);&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line"> return 0;&lt;/span>&lt;br />&lt;span class="line">&amp;#125;&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>编译:&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">nvcc main.cu&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>运行:&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">./a.out&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>如果一切顺利，在编译的时候就不会有报错，不过在我的环境下nvcc会有架构被弃用的警告，本着只要不error就算没事的原则，我们无视这条警告即可。&lt;/p>&lt;p>输出结果:&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">2 + 5 = 0, Is that right?&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure></description><author>justforlxz</author></item><item><title>deepin待机后键盘和触摸板无法使用的解决方法</title><link>https://blog.mkacg.com/2018/06/24/deepin%E5%BE%85%E6%9C%BA%E5%90%8E%E9%94%AE%E7%9B%98%E5%92%8C%E8%A7%A6%E6%91%B8%E6%9D%BF%E6%97%A0%E6%B3%95%E4%BD%BF%E7%94%A8%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/</link><pubDate>Sun, 24 Jun 2018 22:01:22 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2018/06/24/deepin%E5%BE%85%E6%9C%BA%E5%90%8E%E9%94%AE%E7%9B%98%E5%92%8C%E8%A7%A6%E6%91%B8%E6%9D%BF%E6%97%A0%E6%B3%95%E4%BD%BF%E7%94%A8%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/</guid><description>&lt;p>笔记本一直使用的bumblebee来省电，毕竟我也不想笔记本的电只够从一张桌子移动到另一张桌子，但是今天在调待机唤醒后dde-dock崩溃的问题，我需要切换到私有驱动下，因为笔记本使用bumblebee需要使用acpi的参数，否则会见图形就死。&lt;/p>&lt;span id="more">&lt;/span>&lt;p>一切准备就绪以后，我开始调试dde-dock，通过codedump已经知道崩溃在wifi列表为空时访问了first节点，但是当我开始测试修复的代码时，发生了很意外的事情，恢复待机以后键盘和触摸板无法使用了。&lt;/p>&lt;p>虽然之前我也偶尔会用用私有驱动，但是还没遇到过无法键盘和触摸板无法使用的情况。想到论坛好像也有人报了类似的问题，恢复待机以后无wifi和外置键盘无法使用，刚好可以趁这个机会调试一下。&lt;/p>&lt;p>&amp;#x2f;var&amp;#x2f;log&amp;#x2f;Xorg.0.log里看到了大量的synaptics错误，然后该模块被卸载，键盘则是没看到什么信息。&lt;/p>&lt;p>尝试重新modprobe synaptics模块，但是失败了，然后在&amp;#x2f;etc&amp;#x2f;modprobe.d&amp;#x2f;nvidia.conf里看到了几行配置。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line"># These aliases are defined in *all* nvidia modules.&lt;/span>&lt;br />&lt;span class="line"># Duplicating them here sets higher precedence and ensures the selected&lt;/span>&lt;br />&lt;span class="line"># module gets loaded instead of a random first match if more than one&lt;/span>&lt;br />&lt;span class="line"># version is installed. See #798207.&lt;/span>&lt;br />&lt;span class="line">#aliaspci:v000010DEd00000E00sv*sd*bc04sc80i00*nvidia&lt;/span>&lt;br />&lt;span class="line">#aliaspci:v000010DEd00000AA3sv*sd*bc0Bsc40i00*nvidia&lt;/span>&lt;br />&lt;span class="line">#aliaspci:v000010DEd*sv*sd*bc03sc02i00*nvidia&lt;/span>&lt;br />&lt;span class="line">#aliaspci:v000010DEd*sv*sd*bc03sc00i00*nvidia&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>似乎是通配出错了，匹配到了键盘和触摸板，然后就无法使用了。刚好deepin 15.6升级了nvidia驱动，所以是现在才会出这个问题。&lt;/p></description><author>justforlxz</author></item><item><title>Dock插件开发&lt;等待填坑></title><link>https://blog.mkacg.com/2018/05/22/Dock%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91/</link><pubDate>Tue, 22 May 2018 21:22:12 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2018/05/22/Dock%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91/</guid><description>&lt;p>从零构建 dde-dock 的插件&lt;br />本教程将展示一个简单的 dde-dock 插件的开发过程，插件开发者可跟随此步骤为 dde-dock 创造出更多具有丰富功能的插件。&lt;/p>&lt;span id="more">&lt;/span>&lt;p>在本教程中，将创建一个可以实时显示用户家目录(~&amp;#x2f;)使用情况的小工具。&lt;/p>&lt;p>插件的工作原理&lt;br />dde-dock 插件本质是一个按 Qt 插件标准所开发的共享库文件(so)。通过 dde-dock 预定的规范与提供的接口，共同完成 dde-dock 的功能扩展。&lt;/p>&lt;p>准备环境&lt;br />插件的开发环境可以是任意的，只要是符合 Qt 插件规范及 dde-dock 插件规范的共享库文件，都可以被当作 dde-dock 插件载入。下面以 Qt + qmake 为例进行说明：&lt;/p>&lt;p>安装依赖&lt;br />以 Deepin 15.5.1 环境为基础，至少先安装如下的包：&lt;/p>&lt;ul>&lt;li>dde-dock-dev&lt;/li>&lt;li>qt5-qmake&lt;/li>&lt;li>qtbase5-dev-tools&lt;/li>&lt;li>libqt5core5a&lt;/li>&lt;li>libqt5widgets5&lt;/li>&lt;li>pkg-config&lt;/li>&lt;/ul>&lt;p>基本的项目结构&lt;br />创建必需的项目目录与文件&lt;br />插件名称叫做home_monitor，所以创建以下的目录结构：&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">home_monitor&lt;/span>&lt;br />&lt;span class="line">├── home_monitor.json&lt;/span>&lt;br />&lt;span class="line">├── homemonitorplugin.cpp&lt;/span>&lt;br />&lt;span class="line">├── homemonitorplugin.h&lt;/span>&lt;br />&lt;span class="line">└── home_monitor.pro&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure></description><author>justforlxz</author></item><item><title>重构dde-session-ui</title><link>https://blog.mkacg.com/2018/03/11/%E9%87%8D%E6%9E%84dde-session-ui/</link><pubDate>Sun, 11 Mar 2018 03:48:01 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2018/03/11/%E9%87%8D%E6%9E%84dde-session-ui/</guid><description>&lt;p>dde-session-ui里面包含了很多项目，是一个集合，但是其中的代码缺少合理的维护，以至于已经到了必须重构才能继续开发和维护，在支持AD域登录的时候，如果强制加上功能，代码会变得更加糟糕，所以和石博文一块重构了其中非常重要的UserWidget。&lt;/p>&lt;span id="more">&lt;/span>&lt;h2 id="重构前的设计">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#重构前的设计" title="重构前的设计">&lt;/a>重构前的设计&lt;/h2>&lt;p>重构前的dde-lock和lightdm-deepin-greeter是非常混乱的，处理逻辑都混杂在一块，虽然能看出有基本的结构，但是整体并未解耦。&lt;/p>&lt;h2 id="重构后的设计">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#重构后的设计" title="重构后的设计">&lt;/a>重构后的设计&lt;/h2>&lt;ul>&lt;li>基于User类的处理&lt;/li>&lt;li>UserWidget负责提供对用户的处理，暴露出基本的currentUser和LogindUsers。&lt;/li>&lt;li>Lock和Greeter的Manager从UserWidget、SessionWidget中获取用户和用户的会话。&lt;/li>&lt;li>Manager只负责控件的位置和用户的验证。&lt;/li>&lt;li>背景修改为Manager提供模糊的壁纸，FullBackground只提供绘制。&lt;/li>&lt;/ul>&lt;p>重构以后用了大概原代码的1&amp;#x2f;3，启动速度也快了，感觉世界充满了美好… 就是重构历程太辛苦…&lt;/p>&lt;p>本次也发现了很多自身的问题，基础并没有学好，很多地方都可以使用更好的处理方式【就是管不住这手…】&lt;/p></description><author>justforlxz</author></item><item><title>使用DTK开发</title><link>https://blog.mkacg.com/2018/01/12/%E4%BD%BF%E7%94%A8DTK%E5%BC%80%E5%8F%91/</link><pubDate>Fri, 12 Jan 2018 03:05:26 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2018/01/12/%E4%BD%BF%E7%94%A8DTK%E5%BC%80%E5%8F%91/</guid><description>&lt;p>&lt;strong>在阅读本篇文章之前，你需要掌握基本的Qt&amp;#x2f;C++开发知识。&lt;/strong>&lt;/p>&lt;span id="more">&lt;/span>&lt;blockquote>&lt;p>&lt;strong>注意：本篇文章基于Deepin平台，其他平台请自行补充依赖关系。&lt;/strong>&lt;/p>&lt;/blockquote>&lt;p>先安装DTK的依赖关系。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo apt install libdtkwidget2 libdtkcore2&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>新建Qt项目，编辑pro文件，添加项目依赖。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">CONFIG += c++14 link_pkgconfig&lt;/span>&lt;br />&lt;span class="line">PKGCONFIG += dtkwidget&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>DTK目前有两个组件，一个是提供库功能的core，一个是提供控件的widget。&lt;/p>&lt;p>修改main.cpp,删除QApplication的相关内容，改为DApplication。&lt;/p>&lt;blockquote>&lt;p>注意： 使用DTK的组件，需要使用DTK的宏,根据使用的文件来选择对应的宏。&lt;/p>&lt;/blockquote>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">DWIDGET_USE_NAMESPACE&lt;/span>&lt;br />&lt;span class="line">DCORE_USE_NAMESPACE&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>DTK使用了deepin自己的qt插件，需要在DApplication前调用。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">DApplication::loadDXcbPlugin();&lt;/span>&lt;br />&lt;span class="line">DApplication app(argc, argv);&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>DApplication中提供了很多方法来设置程序的各种信息，具体请看头文件的定义。&lt;/p>&lt;p>主窗口由DMainWindow提供，新建类，然后添加DMainWindow的头文件和DTKWIDGET的宏。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">#include &amp;lt;DMainWindow&amp;gt;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">DWIDGET_USE_NAMESPACE&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>然后修改继承关系，改为继承DMainWindow。DMainWindow提供了一些我们封装的方法。目前为止，该程序的界面已经符合Deepin程序的风格了，我们封装了一些其他控件，使其样式符合我们的风格，如果要在其他Qt程序中使用，也是同样的步骤，载入Qt插件，添加对应的头文件和DTK的宏。&lt;/p></description><author>justforlxz</author></item><item><title>开发topbar中的技术问题</title><link>https://blog.mkacg.com/2017/08/23/%E5%BC%80%E5%8F%91topbar%E4%B8%AD%E7%9A%84%E6%8A%80%E6%9C%AF%E9%97%AE%E9%A2%98/</link><pubDate>Wed, 23 Aug 2017 00:54:56 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2017/08/23/%E5%BC%80%E5%8F%91topbar%E4%B8%AD%E7%9A%84%E6%8A%80%E6%9C%AF%E9%97%AE%E9%A2%98/</guid><description>&lt;p>这里记录了开发topbar中遇到的坑和一些问题。&lt;/p>&lt;span id="more">&lt;/span>&lt;p>使用Qt提供的qxcb方法注册阴影为dock类型，反而处于DESKTOP和NORMAL之间。其实当初并不是想设置为DOCK类型的，因为这样阴影也会在窗口上方，我希望的是阴影在普通程序下方，在桌面上方。今天曹哥来讲窗管的一些坑，讲到窗管是如何控制窗口的，我的阴影其实是被Qt注册成_NET_WM_STATE_BELOW了。&lt;br />&lt;a href="https://specifications.freedesktop.org/wm-spec/1.3/ar01s05.html">这里可以看到一些type的介绍&lt;/a>。&lt;/p>&lt;p>在_NET_WM_STATE中一共有这么几个类型：&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">_NET_WM_STATE_MODAL, ATOM&lt;/span>&lt;br />&lt;span class="line">_NET_WM_STATE_STICKY, ATOM&lt;/span>&lt;br />&lt;span class="line">_NET_WM_STATE_MAXIMIZED_VERT, ATOM&lt;/span>&lt;br />&lt;span class="line">_NET_WM_STATE_MAXIMIZED_HORZ, ATOM&lt;/span>&lt;br />&lt;span class="line">_NET_WM_STATE_SHADED, ATOM&lt;/span>&lt;br />&lt;span class="line">_NET_WM_STATE_SKIP_TASKBAR, ATOM&lt;/span>&lt;br />&lt;span class="line">_NET_WM_STATE_SKIP_PAGER, ATOM&lt;/span>&lt;br />&lt;span class="line">_NET_WM_STATE_HIDDEN, ATOM&lt;/span>&lt;br />&lt;span class="line">_NET_WM_STATE_FULLSCREEN, ATOM&lt;/span>&lt;br />&lt;span class="line">_NET_WM_STATE_ABOVE, ATOM&lt;/span>&lt;br />&lt;span class="line">_NET_WM_STATE_BELOW, ATOM&lt;/span>&lt;br />&lt;span class="line">_NET_WM_STATE_DEMANDS_ATTENTION, ATOM&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>如果程序被注册成_NET_WM_STATE_BELOW，则会被放置在DESKTOP之上的一层。不是很清楚Qt是出于什么策略，才把我的阴影注册为这个状态，反而是刚好满足了我的需求。&lt;/p>&lt;p>这要多谢曹哥了，我才终于明白了为什么会这样，以及以后如何正确的设置type。&lt;/p>&lt;img alt="DESKTOP1" src="https://blog.mkacg.com/rss/DeepinScreenshot_%E6%A1%8C%E9%9D%A2_20170823201608.png" />&lt;img alt="DESKTOP2" src="https://blog.mkacg.com/rss/DeepinScreenshot_%E6%A1%8C%E9%9D%A2_20170823201641.png" /></description><author>justforlxz</author></item><item><title>debug了两天，只删了一行代码</title><link>https://blog.mkacg.com/2017/08/16/debug%E4%BA%86%E4%B8%A4%E5%A4%A9%EF%BC%8C%E5%8F%AA%E5%88%A0%E4%BA%86%E4%B8%80%E8%A1%8C%E4%BB%A3%E7%A0%81/</link><pubDate>Wed, 16 Aug 2017 10:25:39 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2017/08/16/debug%E4%BA%86%E4%B8%A4%E5%A4%A9%EF%BC%8C%E5%8F%AA%E5%88%A0%E4%BA%86%E4%B8%80%E8%A1%8C%E4%BB%A3%E7%A0%81/</guid><description>&lt;p>前言： 项目一定要留一些文档！！ 修bug前一定要知道所有的流程！！！&lt;/p>&lt;span id="more">&lt;/span>&lt;p>这两天一直在修一个用户切换的bug，众所周知，deepin的多用户切换一直都不是正常工作的，确切来说是压根没有正常工作，还好用户不是经常切换，不然早就收到一大波的报告了。&lt;/p>&lt;p>dde-session-ui项目中包含了以下软件：&lt;/p>&lt;ul>&lt;li>dde-lock&lt;/li>&lt;li>dde-shutdown&lt;/li>&lt;li>dde-osd&lt;/li>&lt;li>lightdm-deepin-greeter&lt;/li>&lt;li>dde-switchtogreeter&lt;/li>&lt;li>dde-suspend-dialog&lt;/li>&lt;li>dde-warning-dialog&lt;/li>&lt;li>dde-welcome&lt;/li>&lt;li>dde-wm-chooser&lt;/li>&lt;li>dde-lowpower&lt;/li>&lt;li>dde-offline-upgrader&lt;/li>&lt;/ul>&lt;p>大部分项目根据名字就可以知道是做什么的，这是一个软件组的集合。&lt;br />而dde-lock和lightdm-deepin-greeter二者有大量重复的功能和代码，这是它俩的工作性质决定的。&lt;/p>&lt;blockquote>&lt;p>lightdm-deepin-greeter: display-manager启动的实体，登录的界面是它负责的。&lt;/p>&lt;/blockquote>&lt;blockquote>&lt;p>dde-lock： 用户层面的屏幕锁定，基于我们的设计，和lightdm-deepin-greeter是大致相同的布局。&lt;/p>&lt;/blockquote>&lt;p>而且都包含了用户密码的验证，用户的切换，但是二者工作的层面是不同的，为了方便切换，就有了dde-switchtogreeter，用来协调二者的工作，只需要提供用户名就可以切换。&lt;/p>&lt;p>然而，虽然这样的想法是很好的，可是当初并没有人写文档，随着人员的变动，现在公司应该没有一个人是比较完整了解整个的工作流程了，用户切换的bug也就这样被留下来了。&lt;/p>&lt;p>上次修复用户切换的问题，是发现登录以后lightdm-deepin-greeter没有退出，由于不是很清楚linux的登录流程，再加上代码中有不工作的退出代码，当时就改好了退出的问题，这样就引入了第二个问题，而这个问题，就导致了两天三个人在一直查找问题所在。&lt;/p>&lt;p>这次的问题是发现一直切换greeter，会导致Xorg一直在开新的display，这就很奇怪了，正常来说是不会一直创建才对。&lt;/p>&lt;p>最开始以为是dde-switchtogreeter的问题，毕竟切换是它在做，dde-switchtogreeter是单文件的c代码，代码没有任何的说明，真的是为切换而生，我从main函数开始自己走了好几遍的流程，一边看着d-feet的数据来验证，然而只发现了一个小问题，整个代码是没啥问题的。&lt;/p>&lt;p>最后在后端大佬的帮助下，知道了display-manager会自己退掉greeter，不需要自己退，然后我就想起来了以前改的地方，赶紧把退出代码删掉，重新编译，问题得到了解决。&lt;/p>&lt;p>如果我知道display-manager的工作流程，也许这个问题就不会拖两天了。&lt;/p></description><author>justforlxz</author></item><item><title>正常的流程在界面上却是bug</title><link>https://blog.mkacg.com/2017/08/14/%E6%AD%A3%E5%B8%B8%E7%9A%84%E6%B5%81%E7%A8%8B%E5%9C%A8%E7%95%8C%E9%9D%A2%E4%B8%8A%E5%8D%B4%E6%98%AFbug/</link><pubDate>Mon, 14 Aug 2017 23:43:43 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2017/08/14/%E6%AD%A3%E5%B8%B8%E7%9A%84%E6%B5%81%E7%A8%8B%E5%9C%A8%E7%95%8C%E9%9D%A2%E4%B8%8A%E5%8D%B4%E6%98%AFbug/</guid><description>&lt;p>排查了一天，最后终于确认了流程，知道了问题所在，不得不说，dde-session-ui这个项目太需要一个文档了，要把工作流程写的非常详细才可以。&lt;/p>&lt;span id="more">&lt;/span>&lt;p>上午收到了一条新任务，是龙芯上新安装的系统需要输入两次密码才可以登录，没有错误提示。近期并没有什么太大的改动，无非是给龙芯也用上了简单重构过的dde-session-ui，怎么会导致这样的问题。&lt;/p>&lt;p>由于是新安装的系统才会发生，而且是现象一旦发生，就无法重现，这让我头有点大，怎么会有这样的神奇的事情，而且日志中很正常，没有收到message导致密码框被清空。我提交了一个添加了更多日志的，然后重装的龙芯的系统(龙芯重装一次要半个小时)，等重装完了，切换到tty去安装这个包，然后重启lightdm，让我输入密码回车以后，密码消失，我赶紧去看日志，但是日志中并没有我的输出，回车以后肯定会有的一行输出也没有(内心OS：What the fuck is that？)&lt;/p>&lt;p>我又回去看验证的流程，并没有发现有什么不对的地方，而且是近期才有的，我在自己电脑上使用了龙芯的编译参数，打了一个deb包，并没有发生和龙芯一样的情况（这里并不需要，既然是新安装的系统才会发生，在旧系统上是无法重现的）。&lt;/p>&lt;p>再然后我暂时没有管这个，先去修其他bug了。忙完以后，我去问了一下其他大佬，大佬给我提了几条让我去看看，是不是起了两个lightdm-deepin-greeter进程，确认一下使用的二进制是不是你加了log的。（然后我又重装龙芯了），之后确认了是我的二进制，也没有起两个进程。但是ps中有两行输出，我以为是起了两个，就让后端大佬看了一下，后端大佬告诉我说一个是shell的进程，一个是本体，还是只有一个进程存在的。我彻底懵逼了，然后后端大佬告诉我，是不是greeter进程写入什么了，之后的验证中内容已经存在，所以就不会重现了。&lt;/p>&lt;p>其实这个我也想过，但是没考虑太深，greeter并没有操作文件，但是大佬这么一说，我想到有一些dbus的调用，是有写入文件的，然后我把&amp;#x2f;var&amp;#x2f;lib&amp;#x2f;lightdm&amp;#x2f;lightdm-deepin-greeter目录给删除了，完美重现。&lt;/p>&lt;p>我的天啊，排查了快一天，居然是这个目录在新装的系统上没有，所以回车登录以后收到了来自dbus的switchToUser，界面重启导致的内容消失，根本不是收到了Message才被清空的，所以我的log也没有打印出来。&lt;/p>&lt;p>知道了如何重现，可是要怎么修复呢，似乎在greeter上并不能修复，只能去改dde-daemon中LockService，如果文件不存在，就不要发送userChanged的信号。（流程是读取这个文件的信息，和传入的参数进行对比，但是文件是空的，所以被认为不是同一个账户，就发送了信号，也导致了界面上重启，以后无法重现是因为里面已经有内容了）。&lt;/p>&lt;p>就这样，一个流程很正确，但是表现到界面上时就成了一个bug的问题被解决了。写下这篇内容是为了记录我如何解决对我来说很棘手的问题，其实这个问题并不是很困难，但是对整个工作的流程不是很熟悉，导致浪费了大量的时间在非关键点处理，有空要写一些文档了。&lt;/p></description><author>justforlxz</author></item><item><title>在deepin上使用dnsmasq来解决dns解析缓慢</title><link>https://blog.mkacg.com/2017/08/11/%E5%9C%A8deepin%E4%B8%8A%E4%BD%BF%E7%94%A8dnsmasq%E6%9D%A5%E8%A7%A3%E5%86%B3dns%E8%A7%A3%E6%9E%90%E7%BC%93%E6%85%A2/</link><pubDate>Fri, 11 Aug 2017 06:07:26 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2017/08/11/%E5%9C%A8deepin%E4%B8%8A%E4%BD%BF%E7%94%A8dnsmasq%E6%9D%A5%E8%A7%A3%E5%86%B3dns%E8%A7%A3%E6%9E%90%E7%BC%93%E6%85%A2/</guid><description>&lt;p>其实这个问题影响并不是很大，只是稍微的增加一点点访问速度，缓存这东西有利有弊。&lt;/p>&lt;span id="more">&lt;/span>&lt;p>在写完这篇文章以后，我就不用dnsmasq了，现在用的是github上的&lt;a href="https://github.com/chengr28/Pcap_DNSProxy">Pcap_DNSProxy&lt;/a>。用来防止dns污染的。&lt;/p>&lt;p>根据&lt;a href="https://stackoverflow.com/questions/11020027/dns-caching-in-linux">https://stackoverflow.com/questions/11020027/dns-caching-in-linux&lt;/a> 中回答者提供的信息来看，linux发行版是不提供dns解析缓存的，上面提到的nscd也不在deepin的预装列表中，所以我们只能自己动手丰衣足食了。&lt;/p>&lt;p>首先安装口碑较好的dnsmasq，来为我们提供dns缓存。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo apt install dnsmasq&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>如果是deepin最新的2015.4.1版本中安装，安装结束会提醒一个错误，这个错误的解决办法来自&lt;a href="https://stackoverflow.com/questions/16939306/i-cant-restart-my-dnsmasq-service-so-my-fog-server-wont-work">https://stackoverflow.com/questions/16939306/i-cant-restart-my-dnsmasq-service-so-my-fog-server-wont-work&lt;/a>.&lt;br />这个错误似乎是因为&amp;#x2f;usr&amp;#x2f;share&amp;#x2f;dns&amp;#x2f;root.ds文件更新后结构导致的错误。&lt;/p>&lt;p>编辑&amp;#x2f;etc&amp;#x2f;init.d&amp;#x2f;dnsmasq，并找到&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">ROOT_DS=&amp;quot;/usr/share/dns/root.ds&amp;quot;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">if [ -f $ROOT_DS ]; then&lt;/span>&lt;br />&lt;span class="line"> DNSMASQ_OPTS=&amp;quot;$DNSMASQ_OPTS `sed -e s/&amp;quot;. IN DS &amp;quot;/--trust-anchor=.,/ -e s/&amp;quot; &amp;quot;/,/g $ROOT_DS | tr &amp;#x27;\n&amp;#x27; &amp;#x27; &amp;#x27;`&amp;quot; &lt;/span>&lt;br />&lt;span class="line">fi&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>修改为&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">ROOT_DS=&amp;quot;/usr/share/dns/root.ds&amp;quot;&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">if [ -f $ROOT_DS ]; then&lt;/span>&lt;br />&lt;span class="line"> DNSMASQ_OPTS=&amp;quot;$DNSMASQ_OPTS `sed -e s/&amp;quot;.*IN[[:space:]]DS[[:space:]]&amp;quot;/--trust-anchor=.,/ -e s/&amp;quot;[[:space:]]&amp;quot;/,/g $ROOT_DS | tr &amp;#x27;\n&amp;#x27; &amp;#x27; &amp;#x27;`&amp;quot; &lt;/span>&lt;br />&lt;span class="line">fi&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>当错误解决以后，我们手动重启一下dnsmasq的systemd服务。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo systemctl restart dnsmasq&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>deepin的&amp;#x2f;etc&amp;#x2f;resolv.conf来自&amp;#x2f;etc&amp;#x2f;NetworkManager&amp;#x2f;resolv.conf.是一个软连接。我采取的行为是删除这个文件，重新创建。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo rm /etc/resolv.conf&lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;span class="line">sudo vim /etc/resolv.conf&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>然后写入本地地址当做dns地址。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">nameserver 127.0.0.1&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>dnsmasq是一个本地的dns和dhcp服务器，当我们在上面成功启动dnsmasq以后，个人系统中就已经在提供dns服务了，所以本机使用回环地址来表明dns服务器就是本机，所有的dns查询都会发送到本机的dnsmasq中。&lt;/p>&lt;p>如果需要额外添加dns服务器，做法来自&lt;a href="https://wiki.archlinux.org/index.php/Dnsmasq#More_than_three_nameservers">https://wiki.archlinux.org/index.php/Dnsmasq#More_than_three_nameservers&lt;/a>.&lt;/p>&lt;p>创建一个 &amp;#x2f;etc&amp;#x2f;resolv.dnsmasq.conf，写入其他dns服务器的地址。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">nameserver 114.114.114.114&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>然后编辑 &amp;#x2f;etc&amp;#x2f;dnsmasq.conf,找到resolv-file字段，改为&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">resolv-file=/etc/resolv.dnsmasq.conf&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>然后重启dnsmasq。&lt;/p>&lt;p>验证的话通过dig命令。&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">dig blog.mkacg.com&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;p>通过执行两次来判断，Query time在第二次查询是为0 msec。&lt;/p></description><author>justforlxz</author></item><item><title>topbar PPA</title><link>https://blog.mkacg.com/2017/07/20/topbar-PPA/</link><pubDate>Thu, 20 Jul 2017 05:37:51 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2017/07/20/topbar-PPA/</guid><description>&lt;p>自己搭了一个仓库，提供deepin-topbar及相关依赖的包。&lt;/p>&lt;p>I created a repository,provide deepin-topbar and dependencies.&lt;/p>&lt;span id="more">&lt;/span>&lt;p>也许需要安装dirmngr&lt;/p>&lt;p>maybe you need install dirmngr&lt;/p>&lt;h1 id="追加内容到-etc-apt-sources-list">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#追加内容到-etc-apt-sources-list" title="追加内容到&amp;#x2F;etc&amp;#x2F;apt&amp;#x2F;sources.list">&lt;/a>追加内容到&amp;#x2f;etc&amp;#x2f;apt&amp;#x2f;sources.list&lt;/h1>&lt;p>Append content to &amp;#x2f;etc&amp;#x2f;apt&amp;#x2f;sources.list&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">deb [arch=amd64] https://packages.mkacg.com panda main &lt;/span>&lt;br />&lt;span class="line">&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h1 id="导入key">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#导入key" title="导入key">&lt;/a>导入key&lt;/h1>&lt;p>import key&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3BBF73EE77F2FB2A&lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure>&lt;h1 id="刷新列表，进行安装">&lt;a class="headerlink" href="https://blog.mkacg.com/rss/feed.xml#刷新列表，进行安装" title="刷新列表，进行安装">&lt;/a>刷新列表，进行安装&lt;/h1>&lt;p>then, refresh list and install&lt;/p>&lt;figure class="highlight plaintext">&lt;table>&lt;tr>&lt;td class="code">&lt;pre>&lt;span class="line">sudo apt update &amp;amp;&amp;amp; sudo apt install deepin-topbar &lt;/span>&lt;br />&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>&lt;/figure></description><author>justforlxz</author></item><item><title>用Flex和Bison自制“五仁”语言</title><link>https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/</link><pubDate>Wed, 04 May 2016 06:00:00 +0000</pubDate><guid isPermaLink="true">https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/</guid><description>&lt;ul>
&lt;li>“五仁”官方名称：zScript&lt;/li>
&lt;li>变量使用关键字&amp;quot;var&amp;quot;进行声明，变量本身无类型&lt;/li>
&lt;/ul>
&lt;ul>
&lt;li>变量声明可放在源码中任何位置，可在任何位置被调用，例如，var a在源码中最后一行，也可以在第一行使用变量a&lt;/li>
&lt;li>每条语句的结尾必须是';&amp;lsquo;或换行符(后面准备改成强制使用&amp;rsquo;;&amp;lsquo;结尾)&lt;/li>
&lt;li>目前支持的数据类型：int、double、bool、string、list、tuple、object 、undefined&lt;/li>
&lt;li>函数也是object类型&lt;/li>
&lt;li>string的定义为 '&amp;rsquo; 或 &amp;quot;&amp;quot; 中所包裹的内容，支持C和JavaScript风格的字符转义&lt;/li>
&lt;li>支持的运算符： =、+、-、&lt;em>、/、%、&amp;amp;、|、~、^、&amp;amp;&amp;amp;、||、+=、-=、&lt;/em>=、/=、%=、&amp;amp;=、|=、~=、^=、&amp;amp;&amp;amp;=、||=、!、&amp;gt;、&amp;lt;、=、==、===、!=、!==、++、&amp;ndash;&lt;/li>
&lt;li>支持匿名函数，函数闭包，多值返回&lt;/li>
&lt;li>支持的分支结构：if else&lt;/li>
&lt;li>支持的循环结构：goto while for（后面准备加入do while、foreach）&lt;/li>
&lt;li>while for 循环结构中使用break跳出循环，continue立马开始执行下一次循环，break和continue可组合使用
例如：break, break 则可以直接跳出两层循环，组合使用时continue只能放在末尾&lt;/li>
&lt;li>支持switch case语句，可枚举int bool string undefined&lt;/li>
&lt;/ul>
&lt;h2 id="flexlexical-analyzar">Flex(Lexical Analyzar)
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#flexlexical-analyzar">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>Lex是一个产生词法分析器的工具(最早是Eric Emerson Schmidt和Mike Lesk制作）是许多UNIX系统的标准词法分析器（lexical analyzer）产生程序，而且这个工具所作的行为被详列为POSIX标准的一部分。而Flex就是由Vern Paxon实现的Lex&lt;/li>
&lt;li>FLex读进一个代表词法分析器规则的输入字符串流，然后输出C/C++语言的词法分析器源代码。&lt;/li>
&lt;li>wiki：&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">https://&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">en&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">.wik&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">i&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">p&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">e&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">dia.o&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">r&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">g/w&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">i&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">ki/F&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">l&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">ex&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">_&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">%28lex&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">ical&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">_&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">an&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">a&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">ly&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">s&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">er&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">_g&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">e&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">n&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">erat&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">o&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">r&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29">%29&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="flex的文件结构">Flex的文件结构
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#flex%e7%9a%84%e6%96%87%e4%bb%b6%e7%bb%93%e6%9e%84">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;ul>
&lt;li>文件分成三个区块，均以一个只有两个百分比符号（%%）的单行来分隔，如下：&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>定义区域
%%
规则区域
%%
C/C++代码区&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>定义区块是用来定义宏以及导入C写成的头文件所在区块。在这里面也可以写一些C代码，这一些代码会被复制到产生出来的C源代码的开头部分。&lt;/li>
&lt;li>规则区块是最重要的区块；这里将样式与C的陈述（statement）串连在一起。这一些样式都是正则表达式。当lexer看到输入里面有合乎给定的样式时，则会操作相对应的C代码。这就是flex运作的基础。&lt;/li>
&lt;li>C代码区块的内容会原封不动的照搬到产生出来的C源代码里面。&lt;/li>
&lt;/ul>
&lt;h3 id="小例子">小例子
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#%e5%b0%8f%e4%be%8b%e5%ad%90">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">%&amp;#123;
#include
%}
number [0-9]+
%option noyywrap
%% // 第一部分结束
&amp;#123;number} &amp;#123;
printf("number: %s\n", yytext);
}
%% // 第二部分结束
int main()
&amp;#123;
yylex();
return 0;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>%option noyywrap用于指明未定义yywrap函数，此函数用于在文件（或输入）的末尾调用。如果函数的返回值是1，就停止解析。否则等待输入后继续解析
注：正则表达式是使用单个字符串来描述、匹配一系列字符串的规则&lt;/li>
&lt;/ul>
&lt;p>wiki：&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">h&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">ttp&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">s://zh.wiki&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">p&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">e&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">di&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">a.&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">o&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">rg/&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">w&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">iki/%E6%&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">AD&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">%A3%E5%88%99%&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">E&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">8%A1%A8%&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">E&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">8%BE%BE%E5%BC%8F&lt;/a>&lt;/p>
&lt;h2 id="bison">Bison
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#bison">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>Bison是GNU版本的Yacc（Lexical Analyzar），用于做语法分析，它与Yacc兼容&lt;/li>
&lt;li>参考手册：&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">ht&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">t&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">p://www.gn&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">u&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">.o&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">r&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">g&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">/&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">so&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">f&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">twa&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">re&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">/biso&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">n&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">/manual/b&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">i&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">so&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">n&lt;/a>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">.html&lt;/a>
中译版：&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">htt&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">p&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">://bl&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">o&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">g.c&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">s&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">dn.ne&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">t&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">/s&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">i&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">r&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">o&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">u&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">ni&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">2003/&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">ar&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">ticl&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">e&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">/d&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">e&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">tail&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">s&lt;/a>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672">/400672&lt;/a>&lt;/li>
&lt;li>wiki：&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">h&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">t&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">tps://en.&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">wi&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">kipe&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">d&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">ia.&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">o&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">rg/&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">wi&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">k&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">i&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">/GNU_bis&lt;/a>&lt;a href="https://en.wikipedia.org/wiki/GNU_bison">on&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="bison的文件结构">Bison的文件结构
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#bison%e7%9a%84%e6%96%87%e4%bb%b6%e7%bb%93%e6%9e%84">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;ul>
&lt;li>和Flex一样，也分为三个部分&lt;/li>
&lt;li>分别是声明、语法规则和代码段，使用%%分开&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>定义区域
%%
规则区域
%%
C/C++代码区&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>第一部分和Flex一样，用于存放终结符的声明、符号优先级和符号类型等定义&lt;/li>
&lt;li>第二部分用来定义文法规则&lt;/li>
&lt;li>第三部分也是用来存放C/C++代码&lt;/li>
&lt;/ul>
&lt;h4 id="来个小例子">来个小例子
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#%e6%9d%a5%e4%b8%aa%e5%b0%8f%e4%be%8b%e5%ad%90">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">%&amp;#123;
#include
#define YYSTYPE int
void yyerror(char const *);
int yylex(void);
%}
%token NUMBER
%debug
%%
start: &amp;#123; printf("yylval = %d\n", yylval);}
| NUMBER &amp;#123;
printf("NUMBER %d\n", yylval);
}
start NUMBER &amp;#123;
printf("start NUMBER %d\n", yylval);
}
;
%%
void yyerror(char const *msg)
&amp;#123;
printf("error: %s\n", msg);
}
int yylex()
&amp;#123;
printf("&amp;amp;gt;&amp;amp;gt;&amp;amp;gt;");
char str[10] = &amp;#123;};
if(scanf("%s\n", str) == EOF)
return 0;
int number = atoi(str);
printf("%d\n", number);
yylval = number;
return NUMBER;
}
int main()
&amp;#123;
return yyparse();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>%token用于定义一个终结符&lt;/li>
&lt;li>%debug指定bison在生成的代码中加入debug输出（和bison的-t参数一样）&lt;/li>
&lt;li>yyerror在分析过程中有语法错误时被调用&lt;/li>
&lt;li>yylex用于给分析器返回token（在生成的代码中自动被调用）
注：终结符是指文法规则中不能再被分解的最小单位，非终结符则是相反的概念。&lt;/li>
&lt;/ul>
&lt;p>例如上面的文法规则中“start”就是非终结符，NUMBER就是一个终结符。&lt;/p>
&lt;p>wiki：&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">h&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">tt&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">ps://zh.w&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">i&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">k&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">ipe&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">d&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">i&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">a.&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">o&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">rg/wik&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">i&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">/%E7%B5%82%E7%B5%90%&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">E&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">7%AC%&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">A&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">6%&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">E&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">8%88%87%&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">E&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">9%9&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">D&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">%9&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">E&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">%E7%BB%88%E7%B5%90%&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">E&lt;/a>&lt;a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6">7%AC%A6#&lt;/a>&lt;/p>
&lt;h2 id="语法介绍">语法介绍
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#%e8%af%ad%e6%b3%95%e4%bb%8b%e7%bb%8d">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;h3 id="变量的定义">变量的定义
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#%e5%8f%98%e9%87%8f%e7%9a%84%e5%ae%9a%e4%b9%89">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>变量使用关键字“var”进行定义。&lt;/p>
&lt;p>zScript提供了两种形式的变量定义语句。&lt;/p>
&lt;p>1.直接定义的形式，该语句的一般形式为：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">var 标识符;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>该语句的含义是：定义变量名为“标识符”的变量。例如：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">var a;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>该语句的作用是：定义一个名为“a”的变量。
2.定义同时给变量初始化赋值，该语句的一般形式为：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">var 标识符 = 表达式;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>该语句的含义是：定义变量名为“标识符”的变量，且给变量赋值为表达式的结果。例如：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">var a = 1 + 2;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>该语句的作用是：定义一个名为“a”的变量，且赋值为3。
“var”关键字可同时定义多个变量，每个变量之间使用符号“,”分割。&lt;/p>
&lt;h3 id="匿名函数的定义">匿名函数的定义
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#%e5%8c%bf%e5%90%8d%e5%87%bd%e6%95%b0%e7%9a%84%e5%ae%9a%e4%b9%89">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>匿名函数定义的一般形式为：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">(形参列表) &amp;#123;
语句组
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>该语句的含义是：生成一个函数对象，例如：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">(a) &amp;#123;
console.log(a);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>该语句的作用是：定义一个函数，它有一个名为“a”的形参，执行此函数会将a的值打印到屏幕上。
形参列表，可为空、一个、多个，多个形参之间使用“,”分割。&lt;/p>
&lt;h3 id="数组的定义">数组的定义
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#%e6%95%b0%e7%bb%84%e7%9a%84%e5%ae%9a%e4%b9%89">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>数组定义的一般形式为：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">var a = [表达式1, 表达式2, ...];
&lt;/code>&lt;/pre>&lt;/div>&lt;p>该语句的含义是：生成一个数组，数组元素为中括号中表达式的结果。例如：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">var a = [1, 2, 3];
&lt;/code>&lt;/pre>&lt;/div>&lt;p>该语句的作用是：定义一个包含三个元素的数组，元素的值分别为1、2、3，然后赋值给变量a；
数组使用“[”“]”定义，中间内容可以为空、一个表达式、多个表达式，多个表达式之间使用“,”隔开。&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">a = a[0];
&lt;/code>&lt;/pre>&lt;/div>&lt;p>取数组元素语法和C语言一样，都是中括号中写入数组下标。&lt;/p>
&lt;h3 id="选择控制语句">选择控制语句
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#%e9%80%89%e6%8b%a9%e6%8e%a7%e5%88%b6%e8%af%ad%e5%8f%a5">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>一个选择结构，包括一组或若干组操作，每组操作称为一个分支。通过选择控制语句可以实现选择结构。选择控制语句包括if语句、switch语句及起辅助控制作用的break语句。&lt;/p>
&lt;p>If语句用于计算给定的表达式，根据表达式的值是否为假，决定是否执行某一组操作。&lt;/p>
&lt;p>Switch语句首先求解一个表达式，然后根据计算结果的值，从哈希表中查询该从哪一组操作开始执行。&lt;/p>
&lt;p>Break语句用于switch结构中，用于终止当前switch结构的执行。&lt;/p>
&lt;h4 id="if语句">if语句
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#if%e8%af%ad%e5%8f%a5">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>zScript提供了两种形式的if语句。&lt;/p>
&lt;p>1.单if子句的if语句。该if语句的一般形式为：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">if(表达式)
&amp;#123;
语句组
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>该语句的含义是：只有表达式的值为非零值时，才执行其内部的语句组。例如：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">if ( a &amp;amp;gt; b )
&amp;#123;
console.log(“hello”);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>该语句的作用是：当a的值大于b的值时（此时，“a&amp;gt;b”的值为真，为非假值），在屏幕上显示“hello”；否则，不显示“hello”。
2.带else子句的if语句。该if语句的一般形式为：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">if ( 表达式 )
&amp;#123;
语句组1
} else &amp;#123;
语句组2
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>该语句的含义是：当表达式的值为非假时，执行语句组1，而不执行语句组2；否则，即表达式的值为假时，执行语句组2，而不执行语句组1。例如：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">if ( a &amp;amp;gt; b )
&amp;#123;
console.log(“hello1”);
} else &amp;#123;
console.log(“hello2”);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>该语句的作用是：若a的值大于b的值（此时“a&amp;gt;b”为真，为非假值），则在屏幕上显示“hello1”，而不显示“hello2”；否则，即表达式的值为假时，显示“hello2”，而不显示“hello1”。&lt;/p>
&lt;h4 id="switch语句">switch语句
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#switch%e8%af%ad%e5%8f%a5">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>Switch语句与if语句一样，也可以实现分支选择。但if语句是判断一个表达式的值是否为假，决定是否执行某个分支；而switch语句是计算一个表达式的值，根据计算结果，从哈希表查询从哪个分支开始执行代码。Switch语句的一般形式为：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">switch( 表达式 )
&amp;#123;
case 常量1：
语句组1
case 常量2：
语句组2
...
case 常量n：
语句组n
default:
语句组 n + 1
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>switch语句的执行过程：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>1.求解“表达式”的值；&lt;/p>
&lt;/li>
&lt;li>
&lt;p>2.如果“表达式”的值与某个case后面的“常量”的值相同，则从这里开始顺序执行语句。结果switch执行有两种形式：一是遇到break语句为止；二是未遇到break语句，则程序依次执行完所有语句组。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>3.如果“表达式”的值与任何一个case后面的“常量”的值都不相同，当有default子句时，则执行default后面的语句，如果没有default子句，则结束switch。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>其中break的一般形式为&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">break;
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="循环控制语句">循环控制语句
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#%e5%be%aa%e7%8e%af%e6%8e%a7%e5%88%b6%e8%af%ad%e5%8f%a5">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;h4 id="while语句">while语句
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#while%e8%af%ad%e5%8f%a5">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>while语句的一般形式为&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">while( 表达式 )
&amp;#123;
循环体
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>while语句的执行过程：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>1.求解小括号中表达式的值。如果表达式的值为真，转第2步；否则转第3步。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>2.执行循环体，然后转第1步。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>3.执行while语句后面的语句。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>小括号中表达式的值是否为假，决定着循环体是终止还是继续循环。因此，该表达式的值为循环条件。while循环语句的执行特点是，先判断循环条件是否成立，然后决定是否执行循环体。&lt;/p>
&lt;p>当while语句的循环体只包含一条语句时，包含该循环体的“&amp;#123;}”可以省略。&lt;/p>
&lt;h4 id="for语句">for语句
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#for%e8%af%ad%e5%8f%a5">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h4>&lt;p>在两种循环语句中，for语句最为灵活。for语句的一般形式为：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">for (表达式1; 表达式2; 表达式3)
&amp;#123;
循环体
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>for语句的执行过程：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>1.求解表达式1的值。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>2.求解表达式2的值，若其值为假，则结束循环，转第4步；若其值为真，则执行循环体。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>3.求解表达式3。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>4.结束循环。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="对象的定义">对象的定义
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#%e5%af%b9%e8%b1%a1%e7%9a%84%e5%ae%9a%e4%b9%89">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h3>&lt;p>对象定义的一般形式为：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">&amp;#123;
属性名1: 属性的值,
属性名2: 属性的值,
...
属性名n: 属性的值,
属性名n+1: 属性的值
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>例如：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">var object = &amp;#123;
name: "张三",
age: 18
};
&lt;/code>&lt;/pre>&lt;/div>&lt;p>变量object即是一个对象，它包含两个属性。
注：当对一个对象不存在的属性赋值时会将此属性加入到对象中，例如：&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">object.sex = '男';
&lt;/code>&lt;/pre>&lt;/div>&lt;p>对象object中就会多出一个“sex”属性。&lt;/p>
&lt;h2 id="推荐">推荐
&lt;span>
&lt;a href="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/#%e6%8e%a8%e8%8d%90">
&lt;svg height="100%" viewbox="0 0 28 23" width="19" xmlns="http://www.w3.org/2000/svg">&lt;path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" fill="none" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2">&lt;/svg>
&lt;/a>
&lt;/span>
&lt;/h2>&lt;ul>
&lt;li>&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">GNU&lt;/a>&lt;input disabled="" type="checkbox" /> (&lt;a href="https://www.gnu.org/software/bison/manual/bison.html)%5BB%5D(http://www.gnu.org/software/bison/manual/bison.html)%5Bi%5D(http://www.gnu.org/software/bison/manual/bison.html)%5Bso%5D(http://www.gnu.org/software/bison/manual/bison.html)%5Bn%5D(http://www.gnu.org/software/bison/manual/bison.html)%5B">http://www.gnu.org/software/bison/manual/bison.html)[B](http://www.gnu.org/software/bison/manual/bison.html)[i](http://www.gnu.org/software/bison/manual/bison.html)[so](http://www.gnu.org/software/bison/manual/bison.html)[n](http://www.gnu.org/software/bison/manual/bison.html)[&lt;/a>手册](&lt;a href="https://www.gnu.org/software/bison/manual/bison.html">http://www.gnu.org/software/bison/manual/bison.html&lt;/a>)&lt;/li>
&lt;li>&lt;a href="https://blog.csdn.net/sirouni2003/article/details/400672#SEC103">中译版&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/zccrs/zScript">zScript项目地址&lt;/a>&lt;/li>
&lt;/ul></description><author>deepin 社区官方博客</author></item><item><title>My Life</title><link>https://blog.mkacg.com/2016/03/25/my-life/</link><pubDate>Fri, 25 Mar 2016 03:56:08 +0000</pubDate><guid isPermaLink="true">https://blog.mkacg.com/2016/03/25/my-life/</guid><description>&lt;p>这是我用markdown写的第一篇文章(水文)，先来个自我介绍吧，我是小竹，对没错，是小竹，不是竹子，不是竹酱，更不是竹基。&lt;/p>&lt;span id="more">&lt;/span>&lt;p>我玩linux应该有五六年了吧，初二的时候接触的，不过很多年都保持在换各种发行版上，并没有真正意义的玩。上了大学以后，接触的更多了，玩的也更嗨了。&lt;br />现在也用上arch+btrfs+uefi了，各种叼炸天。&lt;br />irc里面也经常学习【看别人装逼。&lt;br />依旧是英语渣渣，数学渣渣，看到win32api，我直接放弃win编程了，我的智商也就玩玩wpf了。&lt;br />下面就贴几张我的日常截图。&lt;/p>&lt;!-- more -->&lt;p>&lt;strong>My Computer info&lt;/strong>&lt;/p>&lt;pre>&lt;code>kirigayaloveyousei@linuserOS: Arch LinuxKernel: x86_64 Linux 4.4.5-1-ARCHackages: 1092Shell: zsh 5.2Resolution: 1366x768WM: Mutter(DeepinGala)WM Theme: AdwaitaGTK Theme: Arc-OSX [GTK2/3]Icon Theme: deepinFont: Noto Sans CJK SC Regular 10CPU: Intel Core i5-4210U CPU @ 2.7GHzRAM: 1980MiB / 3861MiB&lt;/code>&lt;/pre>&lt;img alt="pic1" src="https://blog.mkacg.com/rss/%E6%B7%B1%E5%BA%A6%E6%88%AA%E5%9B%BE20160403224356.png" />&lt;img alt="pic1" src="https://blog.mkacg.com/rss/%E6%B7%B1%E5%BA%A6%E6%88%AA%E5%9B%BE20160403224410.png" />&lt;img alt="pic1" src="https://blog.mkacg.com/rss/%E6%B7%B1%E5%BA%A6%E6%88%AA%E5%9B%BE20160403224524.png" />&lt;img alt="pic1" src="https://blog.mkacg.com/rss/%E6%B7%B1%E5%BA%A6%E6%88%AA%E5%9B%BE20160403224634.png" />&lt;img alt="pic1" src="https://blog.mkacg.com/rss/%E6%B7%B1%E5%BA%A6%E6%88%AA%E5%9B%BE20160403233112.png" />&lt;img alt="pic1" src="https://blog.mkacg.com/rss/%E6%B7%B1%E5%BA%A6%E6%88%AA%E5%9B%BE20160403233229.png" /></description><author>justforlxz</author></item></channel></rss>