Skip to content

Commit a6da112

Browse files
fly602yixinshark
authored andcommitted
feat: add ScreenScale plugin and refactor scale management
1. Add new ScreenScale plugin (org.deepin.dde.ScreenScale1) as the single source of truth for screen scale factors 2. Implement GetScreenScaleInfo and SetScaleFactor DBus methods with automatic recommended scale calculation 3. Refactor XSettings to delegate scale management to ScreenScale service via DBus 4. Remove deprecated ScaleFactorHelper, updateFirefoxDPI, and related legacy code 5. Standardize CMake variable naming from BIN_NAME to PLUGIN_NAME across all plugins 6. Update copyright years from 2025 to 2025-2026 7. Use dtk_add_config_meta_files for DConfig installation Log: Added centralized screen scale management service with automatic recommended scale calculation based on monitor physical dimensions Influence: 1. Test GetScreenScaleInfo with various screen configurations (single monitor, multi-monitor, HiDPI) 2. Verify SetScaleFactor validates input range (1.0-3.0) and persists via DConfig 3. Test scale factor synchronization between ScreenScale and XSettings services 4. Verify ScaleFactorChanged signal propagation across services 5. Test recommended scale calculation accuracy for different monitor sizes 6. Verify backward compatibility with existing scale configurations 7. Test plugin loading/unloading via deepin-service-manager 8. Verify DBus permission policies for ScreenScale1 service feat: 添加屏幕缩放插件并重构缩放管理 1. 新增 ScreenScale 插件 (org.deepin.dde.ScreenScale1) 作为屏幕缩放比例 的唯一事实来源 2. 实现 GetScreenScaleInfo 和 SetScaleFactor DBus 方法,支持自动计算推荐 缩放比例 3. 重构 XSettings 插件,通过 DBus 将缩放管理委托给 ScreenScale 服务 4. 移除已废弃的 ScaleFactorHelper、updateFirefoxDPI 及相关遗留代码 5. 统一各插件 CMake 变量命名,将 BIN_NAME 改为 PLUGIN_NAME 6. 更新版权年份从 2025 到 2025-2026 7. 使用 dtk_add_config_meta_files 安装 DConfig 配置 Log: 新增屏幕缩放管理服务,支持根据显示器物理尺寸自动计算推荐缩放比例 Influence: 1. 测试 GetScreenScaleInfo 接口,覆盖单屏、多屏、HiDPI 等场景 2. 验证 SetScaleFactor 对输入范围 (1.0-3.0) 的校验及 DConfig 持久化 3. 测试 ScreenScale 与 XSettings 服务间的缩放比例同步 4. 验证 ScaleFactorChanged 信号在各服务间的传播 5. 测试不同显示器尺寸下推荐缩放比例计算的准确性 6. 验证与现有缩放配置的向后兼容性 7. 测试通过 deepin-service-manager 加载/卸载插件 8. 验证 ScreenScale1 服务的 DBus 权限策略 PMS: BUG-355227 Change-Id: I17fbc039b207e020b74b5183b78463b97ef9eb3e
1 parent b0c5491 commit a6da112

18 files changed

Lines changed: 665 additions & 372 deletions

src/plugin-qt/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
add_subdirectory("thememanager")
66
add_subdirectory("wallpapercache")
77
add_subdirectory("wallpaperslideshow")
8-
add_subdirectory("xsettings")
8+
add_subdirectory("xsettings")
9+
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(PLUGIN_NAME "plugin-dde-screenscale")
6+
7+
project(${PLUGIN_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)
17+
find_package(Dtk${DTK_VERSION_MAJOR} REQUIRED COMPONENTS Core)
18+
find_package(Dtk${DTK_VERSION_MAJOR}DConfig REQUIRED)
19+
20+
add_library(${PLUGIN_NAME} MODULE
21+
${SRCS}
22+
)
23+
24+
target_link_libraries(${PLUGIN_NAME}
25+
Qt${QT_VERSION_MAJOR}::Core
26+
Qt${QT_VERSION_MAJOR}::DBus
27+
Dtk${DTK_VERSION_MAJOR}::Core
28+
)
29+
30+
# Install module
31+
install(TARGETS ${PLUGIN_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}/deepin-service-manager/)
32+
33+
# Install dbus system service file
34+
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/dbus/org.deepin.dde.ScreenScale1.service DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/dbus-1/system-services)
35+
36+
# Install dbus policy config
37+
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/dbus/org.deepin.dde.ScreenScale1.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d)
38+
39+
# Install plugin config to system directory
40+
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/plugin-dde-screenscale.json DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/deepin-service-manager/system)
41+
42+
# Install dconf config meta
43+
dtk_add_config_meta_files(
44+
APPID org.deepin.dde.daemon
45+
FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/org.deepin.dde.ScreenScale1.json
46+
)
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
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 <QJsonArray>
11+
#include <QJsonDocument>
12+
#include <QJsonObject>
13+
14+
#include <algorithm>
15+
#include <cmath>
16+
17+
ScreenScale::ScreenScale(QObject *parent)
18+
: QObject(parent)
19+
{
20+
m_config = DTK_CORE_NAMESPACE::DConfig::create(
21+
"org.deepin.dde.daemon", "org.deepin.dde.ScreenScale1", "", this);
22+
if (!m_config || !m_config->isValid()) {
23+
qWarning() << "DConfig instance for ScreenScale is null or invalid";
24+
m_config = nullptr;
25+
}
26+
}
27+
28+
/*
29+
测试入参示例:
30+
31+
1. 单屏 1920x1080:
32+
[{"widthPx":1920,"heightPx":1080,"widthMm":477,"heightMm":268}]
33+
34+
2. 单屏 4K:
35+
[{"widthPx":3840,"heightPx":2160,"widthMm":597,"heightMm":336}]
36+
37+
3. 笔记本 14英寸:
38+
[{"widthPx":1920,"heightPx":1080,"widthMm":310,"heightMm":174}]
39+
40+
4. 多屏 (笔记本 + 外接显示器):
41+
[{"widthPx":1920,"heightPx":1080,"widthMm":310,"heightMm":174},{"widthPx":2560,"heightPx":1440,"widthMm":597,"heightMm":336}]
42+
*/
43+
QString ScreenScale::GetScreenScaleInfo(const QString &screensJson)
44+
{
45+
qDebug() << "GetScreenScaleInfo input:" << screensJson;
46+
47+
QJsonParseError error;
48+
QJsonDocument doc = QJsonDocument::fromJson(screensJson.toUtf8(), &error);
49+
if (error.error != QJsonParseError::NoError) {
50+
qWarning() << "Failed to parse screens JSON:" << error.errorString();
51+
QString result = R"({"current":1.0,"recommended":1.0,"available":[1.0,1.25]})";
52+
qDebug() << "GetScreenScaleInfo output:" << result;
53+
return result;
54+
}
55+
56+
QJsonArray screens = doc.array();
57+
if (screens.isEmpty()) {
58+
QString result = R"({"current":1.0,"recommended":1.0,"available":[1.0,1.25]})";
59+
qDebug() << "GetScreenScaleInfo output:" << result;
60+
return result;
61+
}
62+
63+
double step = getScaleStep();
64+
65+
// 计算推荐缩放
66+
double recommended = 3.0;
67+
for (const auto &screenVal : screens) {
68+
QJsonObject screen = screenVal.toObject();
69+
double scale = calcRecommendedScale(
70+
screen["widthPx"].toDouble(),
71+
screen["heightPx"].toDouble(),
72+
screen["widthMm"].toDouble(),
73+
screen["heightMm"].toDouble(),
74+
step);
75+
if (scale < recommended) {
76+
recommended = scale;
77+
}
78+
}
79+
80+
// 计算可用缩放列表(确保缩放后有效分辨率不小于 1024x768)
81+
double maxScale = 3.0;
82+
for (const auto &screenVal : screens) {
83+
QJsonObject screen = screenVal.toObject();
84+
double widthPx = screen["widthPx"].toDouble();
85+
double heightPx = screen["heightPx"].toDouble();
86+
87+
double limit = std::min(widthPx / 1024.0, heightPx / 768.0);
88+
if (limit < maxScale) {
89+
maxScale = limit;
90+
}
91+
}
92+
maxScale = std::clamp(std::floor(maxScale / step) * step, 1.0, 3.0);
93+
94+
QJsonArray available;
95+
int count = static_cast<int>((maxScale - 1.0 + 0.0001) / step);
96+
for (int i = 0; i <= count; ++i) {
97+
available.append(1.0 + i * step);
98+
}
99+
100+
// 确定当前缩放值
101+
double current = recommended;
102+
if (m_config && m_config->isValid()) {
103+
double configured = m_config->value("scaleFactor").toDouble();
104+
if (configured > 0.0) {
105+
for (const auto &v : available) {
106+
if (qFuzzyCompare(v.toDouble(), configured)) {
107+
current = configured;
108+
break;
109+
}
110+
}
111+
}
112+
}
113+
114+
QJsonObject result;
115+
result["current"] = current;
116+
result["recommended"] = recommended;
117+
result["available"] = available;
118+
119+
QString output = QJsonDocument(result).toJson(QJsonDocument::Compact);
120+
qDebug() << "GetScreenScaleInfo output:" << output;
121+
return output;
122+
}
123+
124+
void ScreenScale::SetScaleFactor(double factor)
125+
{
126+
qDebug() << "SetScaleFactor input:" << factor;
127+
128+
if (!m_config || !m_config->isValid()) {
129+
return;
130+
}
131+
132+
if (std::isnan(factor) || std::isinf(factor) || factor < 1.0 || factor > 3.0) {
133+
qWarning() << "Invalid scale factor:" << factor;
134+
if (calledFromDBus()) {
135+
sendErrorReply("org.deepin.dde.ScreenScale1.Error.InvalidParameter",
136+
"Invalid scale factor. Must be between 1.0 and 3.0.");
137+
}
138+
return;
139+
}
140+
141+
double current = m_config->value("scaleFactor").toDouble();
142+
if (qFuzzyCompare(current, factor)) {
143+
qDebug() << "Scale factor unchanged:" << factor;
144+
return;
145+
}
146+
147+
m_config->setValue("scaleFactor", factor);
148+
qDebug() << "Scale factor set to:" << factor;
149+
Q_EMIT ScaleFactorChanged(factor);
150+
}
151+
152+
double ScreenScale::getScaleStep() const
153+
{
154+
double step = 0.25;
155+
if (m_config && m_config->isValid()) {
156+
step = m_config->value("scaleStep", 0.25).toDouble();
157+
}
158+
return step > 0 ? step : 0.25;
159+
}
160+
161+
double ScreenScale::calcRecommendedScale(
162+
double widthPx, double heightPx, double widthMm, double heightMm, double step) const
163+
{
164+
if (widthMm <= 0.0 || heightMm <= 0.0) {
165+
return 1.0;
166+
}
167+
168+
double lenPx = std::sqrt(widthPx * widthPx + heightPx * heightPx);
169+
double lenMm = std::sqrt(widthMm * widthMm + heightMm * heightMm);
170+
171+
// 标准 1080p 21.5 英寸显示器
172+
double lenPxStd = std::sqrt(1920.0 * 1920.0 + 1080.0 * 1080.0);
173+
double lenMmStd = std::sqrt(477.0 * 477.0 + 268.0 * 268.0);
174+
175+
double a = 0.00158; // 经验修正系数
176+
double fix = (lenMm - lenMmStd) * (lenPx / lenPxStd) * a;
177+
double scaleFactor = (lenPx / lenMm) / (lenPxStd / lenMmStd) + fix;
178+
179+
// 对齐到 step
180+
return std::clamp(std::round(scaleFactor / step) * step, 1.0, 3.0);
181+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
19+
public:
20+
explicit ScreenScale(QObject *parent = nullptr);
21+
~ScreenScale() override = default;
22+
23+
public Q_SLOTS:
24+
// 获取缩放信息,返回 JSON: {"current":1.5,"recommended":1.5,"available":[1.0,1.25,1.5,1.75,2.0]}
25+
// current: 配置值在可用范围内则返回配置值,否则返回推荐值
26+
// 入参 JSON 格式: [{"widthPx":1920,"heightPx":1080,"widthMm":477,"heightMm":268}, ...]
27+
QString GetScreenScaleInfo(const QString &screensJson);
28+
29+
// 设置缩放值 (1.0 ~ 3.0)
30+
void SetScaleFactor(double factor);
31+
32+
Q_SIGNALS:
33+
void ScaleFactorChanged(double factor);
34+
35+
private:
36+
double getScaleStep() const;
37+
double calcRecommendedScale(
38+
double widthPx, double heightPx, double widthMm, double heightMm, double step) const;
39+
40+
DTK_CORE_NAMESPACE::DConfig *m_config = nullptr;
41+
};
42+
43+
#endif // SCREEN_SCALE_H
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE busconfig PUBLIC
3+
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
4+
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
5+
<busconfig>
6+
<!-- Only deepin-daemon user can own the service -->
7+
<policy user="deepin-daemon">
8+
<allow own="org.deepin.dde.ScreenScale1"/>
9+
<allow send_destination="org.deepin.dde.ScreenScale1"/>
10+
<allow receive_sender="org.deepin.dde.ScreenScale1"/>
11+
</policy>
12+
13+
<!-- Allow all users to call methods -->
14+
<policy context="default">
15+
<allow send_destination="org.deepin.dde.ScreenScale1"/>
16+
<allow receive_sender="org.deepin.dde.ScreenScale1"/>
17+
</policy>
18+
</busconfig>
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+
User=deepin-daemon
4+
Exec=/usr/bin/deepin-service-manager -n org.deepin.dde.ScreenScale1
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"magic": "dsg.config.meta",
3+
"version": "1.0",
4+
"contents": {
5+
"scaleFactor": {
6+
"value": 0.0,
7+
"serial": 0,
8+
"flags": [
9+
"global"
10+
],
11+
"name": "Global Scale Factor",
12+
"name[zh_CN]": "全局缩放比例",
13+
"description": "Global scale factor for all monitors.",
14+
"permissions": "readwrite",
15+
"visibility": "public"
16+
},
17+
"scaleStep": {
18+
"value": 0.25,
19+
"serial": 0,
20+
"flags": [
21+
"global"
22+
],
23+
"name": "Scale Step",
24+
"name[zh_CN]": "缩放步长",
25+
"description": "Step size for screen scaling.",
26+
"permissions": "readonly",
27+
"visibility": "public"
28+
}
29+
}
30+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "org.deepin.dde.ScreenScale1",
3+
"libPath": "libplugin-dde-screenscale.so",
4+
"startType": "OnDemand",
5+
"idleTime": 1,
6+
"pluginType": "qt",
7+
"policy": [
8+
{
9+
"path": "/org/deepin/dde/ScreenScale1"
10+
}
11+
]
12+
}

0 commit comments

Comments
 (0)