Skip to content

Commit cb7ca9b

Browse files
committed
fix: unify screen scaling management with ScreenScale plugin
Refactored the XSettings scaling logic to use the new ScreenScale plugin as the primary source of truth. The XSettingsManager now delegates scale factor updates to the "org.deepin.dde.ScreenScale1" D-Bus service. This change removes redundant scaling calculations and ensures that scaling configurations are synchronized correctly between XSettings and the dedicated scaling service. Added a fallback synchronization logic in onScaleFactorChanged for cases where the ScreenScale service is unavailable. Influence: 1. Verify screen scaling updates via XSettings D-Bus and DConfig keys. 2. Ensure ScreenScale1 service correctly handles SetScaleFactor calls. 3. Confirm Plymouth and GTK cursor size are updated on scale changes. 4. Test backward compatibility for apps relying on existing D-Bus properties. fix: 统一屏幕缩放管理到 ScreenScale 插件 重构了 XSettings 缩放逻辑,将 ScreenScale 插件作为缩放配置的事实来源。 XSettingsManager 现在将缩放因子更新操作委托给 "org.deepin.dde.ScreenScale1" D-Bus 服务。此更改删除了冗余的缩放计算逻 辑,并确保 XSettings 与专用缩放服务之间的配置保持同步。同时,在 ScreenScale 服务不可用时,在 onScaleFactorChanged 中提供了回退同步逻辑。 影响范围: 1. 验证通过 XSettings D-Bus 和 DConfig 键进行的屏幕缩放更新是否生效。 2. 确认 ScreenScale1 服务能正确处理 SetScaleFactor 调用。 3. 确保缩放因子更改时,Plymouth 和 GTK 光标大小能正确更新。 4. 检查与依赖现有 XSettings D-Bus 属性的应用程序的向后兼容性。 pms: BUG-355227 Change-Id: I2363239245451fc5079c39e6b76042d42a87b2a4
1 parent 6cefac6 commit cb7ca9b

11 files changed

Lines changed: 618 additions & 265 deletions

src/plugin-qt/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
# SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
1+
# SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd.
22
#
33
# SPDX-License-Identifier: LGPL-3.0-or-later
44

55
add_subdirectory("thememanager")
66
add_subdirectory("wallpaperslideshow")
7-
add_subdirectory("xsettings")
7+
add_subdirectory("xsettings")
8+
add_subdirectory("screenscale")
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
#
3+
# SPDX-License-Identifier: LGPL-3.0-or-later
4+
5+
set(BIN_NAME "plugin-dde-screenscale")
6+
7+
project(${BIN_NAME})
8+
9+
set(CMAKE_INCLUDE_CURRENT_DIR ON)
10+
set(CMAKE_AUTOMOC ON)
11+
set(CMAKE_AUTORCC ON)
12+
13+
include(GNUInstallDirs)
14+
file(GLOB_RECURSE SRCS "*.h" "*.cpp")
15+
16+
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core DBus Gui)
17+
find_package(Dtk${DTK_VERSION_MAJOR} REQUIRED COMPONENTS Core)
18+
19+
add_library(${BIN_NAME} MODULE
20+
${SRCS}
21+
)
22+
23+
target_link_libraries(${BIN_NAME}
24+
Qt${QT_VERSION_MAJOR}::Core
25+
Qt${QT_VERSION_MAJOR}::DBus
26+
Qt${QT_VERSION_MAJOR}::Gui
27+
Dtk${DTK_VERSION_MAJOR}::Core
28+
)
29+
30+
# Install module
31+
install(TARGETS ${BIN_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}/deepin-service-manager/)
32+
33+
# Install dbus service file
34+
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/dbus/org.deepin.dde.ScreenScale1.service DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/dbus-1/services)
35+
36+
# Install systemd service file
37+
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/org.deepin.dde.ScreenScale1.service
38+
DESTINATION lib/systemd/user/
39+
RENAME org.deepin.dde.ScreenScale1.service
40+
)
41+
42+
# Install plugin config
43+
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/plugin-dde-screenscale.json DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/deepin-service-manager/user)
44+
45+
# Install dconf config meta
46+
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/org.deepin.dde.ScreenScale1.json DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/dsg/configs/org.deepin.dde.daemon)
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: LGPL-3.0-or-later
4+
5+
#include "screenscale.h"
6+
7+
#include <DConfig>
8+
9+
#include <QDebug>
10+
#include <QGuiApplication>
11+
#include <QScreen>
12+
13+
#include <algorithm>
14+
#include <cmath>
15+
16+
namespace {
17+
double toListedScaleFactor(double s, double step = 0.25)
18+
{
19+
const double min = 1.0;
20+
const double max = 3.0;
21+
22+
if (step <= 0)
23+
step = 0.25;
24+
25+
return std::clamp(std::round(s / step) * step, min, max);
26+
}
27+
28+
double calcRecommendedScaleFactor(
29+
double widthPx, double heightPx, double widthMm, double heightMm, double step = 0.25)
30+
{
31+
if (widthMm == 0.0 || heightMm == 0.0) {
32+
return 1.0;
33+
}
34+
double lenPx = std::sqrt(std::pow(widthPx, 2) + std::pow(heightPx, 2));
35+
double lenMm = std::sqrt(std::pow(widthMm, 2) + std::pow(heightMm, 2));
36+
37+
double lenPxStd =
38+
std::sqrt(std::pow(1920, 2) + std::pow(1080, 2)); // 标准分辨率对角线长度 (1080p)
39+
double lenMmStd =
40+
std::sqrt(std::pow(477, 2) + std::pow(268, 2)); // 标准物理尺寸对角线长度 (约21.5英寸显示器)
41+
42+
const double a = 0.00158; // 经验修正系数,用于平滑不同尺寸屏幕带来的缩放偏差
43+
double fix = (lenMm - lenMmStd) * (lenPx / lenPxStd) * a;
44+
double scaleFactor = (lenPx / lenMm) / (lenPxStd / lenMmStd) + fix;
45+
46+
return toListedScaleFactor(scaleFactor, step);
47+
}
48+
49+
double getRecommendedScaleFactor(double step = 0.25)
50+
{
51+
// If no QGuiApplication instance, we cannot get screens, fallback to 1.0
52+
if (!qApp) {
53+
return 1.0;
54+
}
55+
56+
auto screens = QGuiApplication::screens();
57+
if (screens.isEmpty()) {
58+
return 1.0;
59+
}
60+
61+
double minScaleFactor = 3.0;
62+
for (auto &&screen : screens) {
63+
double realWidth = screen->geometry().width() * screen->devicePixelRatio();
64+
double realHeight = screen->geometry().height() * screen->devicePixelRatio();
65+
66+
qDebug() << "Screen:" << realWidth << realHeight << screen->physicalSize().width()
67+
<< screen->physicalSize().height();
68+
69+
double scaleFactor = calcRecommendedScaleFactor(realWidth,
70+
realHeight,
71+
screen->physicalSize().width(),
72+
screen->physicalSize().height(),
73+
step);
74+
if (minScaleFactor > scaleFactor) {
75+
minScaleFactor = scaleFactor;
76+
}
77+
}
78+
79+
return minScaleFactor;
80+
}
81+
} // namespace
82+
83+
ScreenScale::ScreenScale(QObject *parent)
84+
: QObject(parent)
85+
, m_config(DTK_CORE_NAMESPACE::DConfig::create(
86+
"org.deepin.dde.daemon", "org.deepin.dde.ScreenScale1", "", this))
87+
{
88+
if (!m_config) {
89+
qWarning() << "Failed to create DConfig instance for ScreenScale";
90+
return;
91+
}
92+
93+
if (m_config->isValid()) {
94+
double step = getScaleStep();
95+
double currentFactor = m_config->value("scale-factor").toDouble();
96+
if (currentFactor <= 0.0) {
97+
double recommended = getRecommendedScaleFactor(step);
98+
m_config->setValue("scale-factor", recommended);
99+
qDebug() << "Initialized scale-factor to recommended:" << recommended;
100+
}
101+
102+
connect(m_config,
103+
&DTK_CORE_NAMESPACE::DConfig::valueChanged,
104+
this,
105+
[this](const QString &key) {
106+
if (key == "scale-step") {
107+
Q_EMIT AvailableScalesChanged();
108+
Q_EMIT RecommendedScaleChanged();
109+
}
110+
});
111+
}
112+
113+
if (qApp) {
114+
connect(qApp, &QGuiApplication::screenAdded, this, [this]() {
115+
Q_EMIT AvailableScalesChanged();
116+
Q_EMIT RecommendedScaleChanged();
117+
});
118+
connect(qApp, &QGuiApplication::screenRemoved, this, [this]() {
119+
Q_EMIT AvailableScalesChanged();
120+
Q_EMIT RecommendedScaleChanged();
121+
});
122+
}
123+
}
124+
125+
double ScreenScale::GetScaleFactor() const
126+
{
127+
if (m_config && m_config->isValid()) {
128+
double currentFactor = m_config->value("scale-factor").toDouble();
129+
if (currentFactor > 0.0) {
130+
return currentFactor;
131+
}
132+
}
133+
134+
// Calculate recommended scale factor but do not save in const method
135+
return getRecommendedScaleFactor(getScaleStep());
136+
}
137+
138+
double ScreenScale::getMaxScaleLimit() const
139+
{
140+
if (!qApp) {
141+
return 3.0;
142+
}
143+
144+
auto screens = QGuiApplication::screens();
145+
if (screens.isEmpty()) {
146+
return 3.0;
147+
}
148+
149+
double maxScale = 3.0;
150+
for (auto &&screen : screens) {
151+
double realWidth = screen->geometry().width() * screen->devicePixelRatio();
152+
double realHeight = screen->geometry().height() * screen->devicePixelRatio();
153+
154+
double scaleW = realWidth / 1024.0;
155+
double scaleH = realHeight / 768.0;
156+
double limit = std::min(scaleW, scaleH);
157+
158+
if (maxScale > limit) {
159+
maxScale = limit;
160+
}
161+
}
162+
163+
double step = getScaleStep();
164+
return std::clamp(std::floor(maxScale / step) * step, 1.0, 3.0);
165+
}
166+
167+
QList<double> ScreenScale::getAvailableScales() const
168+
{
169+
double maxScaleVal = getMaxScaleLimit();
170+
double step = getScaleStep();
171+
172+
QList<double> range;
173+
// 使用整数循环避免浮点数累加误差
174+
int count = static_cast<int>((maxScaleVal - 1.0 + 0.0001) / step);
175+
for (int i = 0; i <= count; ++i) {
176+
range.append(1.0 + i * step);
177+
}
178+
179+
// 补偿精度,确保最后一个值一定是精确的最大值
180+
if (range.isEmpty() || !qFuzzyCompare(range.last(), maxScaleVal)) {
181+
if (range.isEmpty() || range.last() < maxScaleVal) {
182+
range.append(maxScaleVal);
183+
}
184+
}
185+
186+
return range;
187+
}
188+
189+
double ScreenScale::getRecommendedScale() const
190+
{
191+
return getRecommendedScaleFactor(getScaleStep());
192+
}
193+
194+
double ScreenScale::getScaleStep() const
195+
{
196+
double step = 0.25;
197+
if (m_config && m_config->isValid()) {
198+
step = m_config->value("scale-step", 0.25).toDouble();
199+
}
200+
201+
if (step <= 0)
202+
step = 0.25;
203+
204+
return step;
205+
}
206+
207+
void ScreenScale::SetScaleFactor(double factor)
208+
{
209+
if (!m_config || !m_config->isValid()) {
210+
return;
211+
}
212+
213+
if (std::isnan(factor) || std::isinf(factor)) {
214+
if (calledFromDBus()) {
215+
sendErrorReply("org.deepin.dde.ScreenScale1.Error.InvalidParameter",
216+
"NaN or Infinity is not allowed.");
217+
}
218+
qWarning() << "Invalid scale factor (NaN or Infinity) received, ignoring.";
219+
return;
220+
}
221+
222+
// 校验 factor 是否在可用列表中
223+
auto availableList = getAvailableScales();
224+
bool found = false;
225+
for (double v : availableList) {
226+
if (qFuzzyCompare(v, factor)) {
227+
factor = v; // 修正为列表中的精确值
228+
found = true;
229+
break;
230+
}
231+
}
232+
233+
if (!found) {
234+
if (calledFromDBus()) {
235+
sendErrorReply(
236+
"org.deepin.dde.ScreenScale1.Error.InvalidParameter",
237+
QString("Scale factor %1 is not in the available scales list.").arg(factor));
238+
}
239+
qWarning() << "Requested scale factor" << factor << "is not in the available list.";
240+
return;
241+
}
242+
243+
if (qFuzzyCompare(GetScaleFactor(), factor)) {
244+
return;
245+
}
246+
247+
qDebug() << "SetScaleFactor:" << factor;
248+
m_config->setValue("scale-factor", factor);
249+
250+
if (qFuzzyCompare(m_config->value("scale-factor").toDouble(), factor)) {
251+
Q_EMIT ScaleFactorChanged(factor);
252+
} else {
253+
qWarning() << "Failed to save scale factor to DConfig.";
254+
}
255+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: LGPL-3.0-or-later
4+
5+
#ifndef SCREEN_SCALE_H
6+
#define SCREEN_SCALE_H
7+
8+
#include <DConfig>
9+
10+
#include <QDBusContext>
11+
#include <QList>
12+
#include <QObject>
13+
14+
class ScreenScale : public QObject, protected QDBusContext
15+
{
16+
Q_OBJECT
17+
Q_CLASSINFO("D-Bus Interface", "org.deepin.dde.ScreenScale1")
18+
Q_PROPERTY(double RecommendedScale READ getRecommendedScale NOTIFY RecommendedScaleChanged)
19+
Q_PROPERTY(QList<double> AvailableScales READ getAvailableScales NOTIFY AvailableScalesChanged)
20+
Q_PROPERTY(double ScaleFactor READ GetScaleFactor WRITE SetScaleFactor NOTIFY ScaleFactorChanged)
21+
22+
public:
23+
explicit ScreenScale(QObject *parent = nullptr);
24+
~ScreenScale() override = default;
25+
26+
double getRecommendedScale() const;
27+
QList<double> getAvailableScales() const;
28+
29+
public Q_SLOTS:
30+
double GetScaleFactor() const;
31+
void SetScaleFactor(double factor);
32+
33+
Q_SIGNALS:
34+
void ScaleFactorChanged(double factor);
35+
void AvailableScalesChanged();
36+
void RecommendedScaleChanged();
37+
38+
private:
39+
double getMaxScaleLimit() const;
40+
double getScaleStep() const;
41+
42+
DTK_CORE_NAMESPACE::DConfig *m_config = nullptr;
43+
};
44+
45+
#endif // SCREEN_SCALE_H
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[D-BUS Service]
2+
Name=org.deepin.dde.ScreenScale1
3+
Exec=/usr/bin/false
4+
SystemdService=org.deepin.dde.ScreenScale1.service
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"magic": "dsg.config.meta",
3+
"version": "1.0",
4+
"contents": {
5+
"scale-factor": {
6+
"value": 0.0,
7+
"serial": 0,
8+
"flags": [],
9+
"name": "Global Scale Factor",
10+
"name[zh_CN]": "全局缩放比例",
11+
"description": "Global scale factor for all monitors.",
12+
"permissions": "readwrite",
13+
"visibility": "public"
14+
},
15+
"scale-step": {
16+
"value": 0.25,
17+
"serial": 0,
18+
"flags": [],
19+
"name": "Scale Step",
20+
"name[zh_CN]": "缩放步长",
21+
"description": "Step size for screen scaling.",
22+
"permissions": "readonly",
23+
"visibility": "public"
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)