From 3b5434bc26777a2097634641f4fe45d38f6ce6e9 Mon Sep 17 00:00:00 2001 From: caixiangrong Date: Wed, 24 Dec 2025 13:50:38 +0800 Subject: [PATCH] feat: add automatic screen scaling recommendation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added automatic screen scaling factor recommendation based on monitor physical size and resolution Implemented XCB RANDR support to get monitor size information Added force scaling factor configuration file support Improved build dependencies to include X11 and XCB development libraries Switched from pkg-config to CMake find_package for X11/XCB dependencies The system now calculates optimal scaling factor using monitor diagonal measurements and standard reference values Log: Added automatic screen scaling recommendation feature Influence: 1. Test with multiple monitor setups to verify scaling factor calculation 2. Verify force-scale-factor.ini configuration file functionality 3. Check scaling behavior on different resolution displays 4. Test migration from old scaling settings 5. Verify compatibility with lightdm display manager 6. Test DPI and font scaling consistency feat: 添加自动屏幕缩放推荐功能 新增基于显示器物理尺寸和分辨率的自动屏幕缩放因子推荐功能 实现 XCB RANDR 支持以获取显示器尺寸信息 添加强制缩放因子配置文件支持 改进构建依赖关系,包含 X11 和 XCB 开发库 从 pkg-config 切换到 CMake find_package 来管理 X11/XCB 依赖 系统现在使用显示器对角线测量值和标准参考值计算最佳缩放因子 Log: 新增自动屏幕缩放推荐功能 Influence: 1. 在多显示器设置下测试缩放因子计算功能 2. 验证 force-scale-factor.ini 配置文件功能 3. 检查不同分辨率显示器的缩放行为 4. 测试从旧缩放设置的迁移过程 5. 验证与 lightdm 显示管理器的兼容性 6. 测试 DPI 和字体缩放的一致性 PMS: BUG-343939 --- debian/control | 3 + src/plugin-qt/xsettings/CMakeLists.txt | 12 +- .../xsettings/impl/xsettingsmanager.cpp | 121 ++++++++++++++++++ .../xsettings/impl/xsettingsmanager.h | 3 + .../xsettings/modules/api/xcbutils.cpp | 67 ++++++++++ .../xsettings/modules/api/xcbutils.h | 9 ++ 6 files changed, 209 insertions(+), 6 deletions(-) diff --git a/debian/control b/debian/control index 5fdcdc40..be956330 100644 --- a/debian/control +++ b/debian/control @@ -12,6 +12,9 @@ Build-Depends: debhelper (>= 11), libglib2.0-dev, libdtk6gui-dev, libdtk6core-dev, + libx11-dev, + libxcb-randr0-dev, + libxcb1-dev, Standards-Version: 4.1.3 Homepage: https://github.com/linuxdeepin diff --git a/src/plugin-qt/xsettings/CMakeLists.txt b/src/plugin-qt/xsettings/CMakeLists.txt index ae68a7bf..b8a1cc97 100644 --- a/src/plugin-qt/xsettings/CMakeLists.txt +++ b/src/plugin-qt/xsettings/CMakeLists.txt @@ -14,10 +14,9 @@ file(GLOB_RECURSE SRCS "*.h" "*.cpp") find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core DBus) find_package(Dtk${DTK_VERSION_MAJOR} REQUIRED COMPONENTS Gui Core) -find_package(PkgConfig REQUIRED) - -pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11) -pkg_check_modules(XCB REQUIRED IMPORTED_TARGET xcb) +find_package(X11 REQUIRED) +find_package(XCB REQUIRED) +find_package(XCB REQUIRED COMPONENTS RANDR) add_library(${BIN_NAME} MODULE ${SRCS} @@ -26,8 +25,9 @@ add_library(${BIN_NAME} MODULE target_link_libraries(${BIN_NAME} Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::DBus - PkgConfig::X11 - PkgConfig::XCB + X11::X11 + XCB::XCB + XCB::RANDR Dtk${DTK_VERSION_MAJOR}::Core ) macro(install_user_symlink filepath wantsdir) diff --git a/src/plugin-qt/xsettings/impl/xsettingsmanager.cpp b/src/plugin-qt/xsettings/impl/xsettingsmanager.cpp index 03249840..7648eb5d 100644 --- a/src/plugin-qt/xsettings/impl/xsettingsmanager.cpp +++ b/src/plugin-qt/xsettings/impl/xsettingsmanager.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,10 @@ XSettingsManager::XSettingsManager(QObject *parent) , m_xcbUtils(XcbUtils::getInstance()) { connect(m_settingDconfig, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, &XSettingsManager::handleDConfigChangedCb); + setScreenScaleFactors(getScreenScaleFactors(), false); + adjustScaleFactor(getRecommendedScaleFactor()); setSettings(getSettingsInSchema()); + updateDPI(); updateXResources(); QThreadPool::globalInstance()->start([this]() { @@ -231,6 +235,123 @@ void XSettingsManager::handleDConfigChangedCb(const QString &key) setSettings(xsSetting); } +double toListedScaleFactor(double s) +{ + const double min = 1.0; + const double max = 3.0; + const double step = 0.25; + + if (s <= min) { + return min; + } else if (s >= max) { + return max; + } + + for (double i = min; i <= max; i += step) { + if (i > s) { + double ii = i - step; + double d1 = s - ii; + double d2 = i - s; + + if (d1 >= d2) { + return i; + } else { + return ii; + } + } + } + return max; +} + +// calcRecommendedScaleFactor 计算推荐的缩放比 +double calcRecommendedScaleFactor(double widthPx, double heightPx, double widthMm, double heightMm) +{ + if (widthMm == 0.0 || heightMm == 0.0) { + return 1.0; + } + double lenPx = std::sqrt(std::pow(widthPx, 2) + std::pow(heightPx, 2)); + double lenMm = std::sqrt(std::pow(widthMm, 2) + std::pow(heightMm, 2)); + + double lenPxStd = std::sqrt(std::pow(1920, 2) + std::pow(1080, 2)); // 标准分辨率对角线长度 + double lenMmStd = std::sqrt(std::pow(477, 2) + std::pow(268, 2)); // 标准物理尺寸对角线长度 + + const double a = 0.00158; + double fix = (lenMm - lenMmStd) * (lenPx / lenPxStd) * a; + double scaleFactor = (lenPx / lenMm) / (lenPxStd / lenMmStd) + fix; + + return toListedScaleFactor(scaleFactor); +} + +double XSettingsManager::getRecommendedScaleFactor() +{ + QList monitors = XcbUtils::getInstance().getMonitorSizeInfos(); + if (monitors.isEmpty()) { + return 1.0; + } + // 允许用户通过 force-scale-factor.ini 强制设置全局缩放 + double forceScaleFactor = getForceScaleFactor(); + if (forceScaleFactor > 0.0) { + return forceScaleFactor; + } + double minScaleFactor = 3.0; + for (auto &&monitor : monitors) { + double scaleFactor = calcRecommendedScaleFactor(monitor.width, monitor.height, monitor.mmWidth, monitor.mmHeight); + if (minScaleFactor > scaleFactor) { + minScaleFactor = scaleFactor; + } + } + return minScaleFactor; +} + +// GetForceScaleFactor 允许用户通过 force-scale-factor.ini 强制设置全局缩放 +double XSettingsManager::getForceScaleFactor() +{ + QString fileName = Utils::GetUserConfigDir() + "/deepin/force-scale-factor.ini"; + KeyFile keyFile; + bool bSuccess = keyFile.loadFile(fileName); + if (bSuccess) { + bool ok = false; + double forceScaleFactor = keyFile.getStr("ForceScaleFactor", "scale").toDouble(&ok); + if (ok && forceScaleFactor >= 1.0 && forceScaleFactor <= 3.0) { + return forceScaleFactor; + } + qWarning() << "invalid forceScaleFactor:" << keyFile.getStr("ForceScaleFactor", "scale"); + } + return -1.0; +} + +void XSettingsManager::adjustScaleFactor(double recommendedScaleFactor) +{ + qDebug() << "recommended scale factor:" << recommendedScaleFactor; + double value = m_settingDconfig->value("scale-factor").toDouble(); + if (value <= 0) { + ScaleFactors map; + map.insert("ALL", value); + setScreenScaleFactors(map, false); + // m_restartOSD = true; + } + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + if (!env.value("STARTDDE_MIGRATE_SCALE_FACTOR").isEmpty()) { + double scaleFactor = getScaleFactor(); + if (scaleFactor > 0.0) { + ScaleFactors map; + map.insert("", scaleFactor); + setScreenScaleFactorsForQt(map); + } + cleanUpDdeEnv(); + return; + } + if (!QFile::exists("/etc/lightdm/deepin/qt-theme.ini")) { + // lightdm-deepin-greeter does not have the qt-theme.ini file yet. + double scaleFactor = getScaleFactor(); + if (scaleFactor > 0.0) { + ScaleFactors map; + map.insert("", scaleFactor); + setScreenScaleFactorsForQt(map); + } + } +} + void XSettingsManager::updateDPI() { double scale = 1; diff --git a/src/plugin-qt/xsettings/impl/xsettingsmanager.h b/src/plugin-qt/xsettings/impl/xsettingsmanager.h index c62910f3..9437a2b8 100644 --- a/src/plugin-qt/xsettings/impl/xsettingsmanager.h +++ b/src/plugin-qt/xsettings/impl/xsettingsmanager.h @@ -42,6 +42,9 @@ protected Q_SLOTS: void handleDConfigChangedCb(const QString &key); private: + double getRecommendedScaleFactor(); + double getForceScaleFactor(); + void adjustScaleFactor(double recommendedScaleFactor); void updateDPI(); void updateXResources(); void updateFirefoxDPI(); diff --git a/src/plugin-qt/xsettings/modules/api/xcbutils.cpp b/src/plugin-qt/xsettings/modules/api/xcbutils.cpp index 8213ad10..3cd65bd8 100644 --- a/src/plugin-qt/xsettings/modules/api/xcbutils.cpp +++ b/src/plugin-qt/xsettings/modules/api/xcbutils.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -181,6 +182,72 @@ QString XcbUtils::getXcbAtomName(xcb_atom_t atom) return ""; } +QList XcbUtils::getMonitorSizeInfos() +{ + // 获取XCB版本信息 + const xcb_setup_t *setup = xcb_get_setup(connection); + int majorVersion = setup->protocol_major_version; + int minorVersion = setup->protocol_minor_version; + qInfo() << QString("randr version %1.%2").arg(majorVersion).arg(minorVersion); + if (majorVersion < 1 || (majorVersion == 1 && minorVersion < 2)) { + return {}; + } + xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup); + xcb_window_t root = screen_iter.data->root; + xcb_randr_get_screen_resources_cookie_t cookie = xcb_randr_get_screen_resources(connection, root); + xcb_generic_error_t *error = nullptr; + xcb_randr_get_screen_resources_reply_t *resources = xcb_randr_get_screen_resources_reply(connection, cookie, &error); + if (error) { + qWarning() << "Error getting screen resources: " << error->error_code; + free(error); + return {}; + } + + xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(resources); + int output_count = xcb_randr_get_screen_resources_outputs_length(resources); + QList monitors; + for (int i = 0; i < output_count; i++) { + xcb_randr_output_t output = outputs[i]; + xcb_randr_get_output_info_cookie_t info_cookie = xcb_randr_get_output_info(connection, output, resources->config_timestamp); + + xcb_randr_get_output_info_reply_t *output_info = xcb_randr_get_output_info_reply(connection, info_cookie, nullptr); + + if (!output_info) + continue; + + // 检查输出是否连接 + if (output_info->connection != XCB_RANDR_CONNECTION_CONNECTED) { + free(output_info); + continue; + } + + if (output_info->crtc == XCB_NONE) { + free(output_info); + continue; + } + xcb_randr_get_crtc_info_cookie_t crtc_cookie = xcb_randr_get_crtc_info(connection, output_info->crtc, resources->config_timestamp); + + xcb_randr_get_crtc_info_reply_t *crtc_info = xcb_randr_get_crtc_info_reply(connection, crtc_cookie, nullptr); + + if (crtc_info) { + MonitorSizeInfo monitor; + monitor.width = crtc_info->width; + monitor.height = crtc_info->height; + monitor.mmWidth = output_info->mm_width; + monitor.mmHeight = output_info->mm_height; + + monitors.push_back(monitor); + + free(crtc_info); + } + + free(output_info); + } + + free(resources); + return monitors; +} + // 获取 _XSETTINGS_SETTINGS 属性 QByteArray XcbUtils::getXcbAtomProperty(xcb_atom_t atom) { diff --git a/src/plugin-qt/xsettings/modules/api/xcbutils.h b/src/plugin-qt/xsettings/modules/api/xcbutils.h index ecf1d024..f75c39eb 100644 --- a/src/plugin-qt/xsettings/modules/api/xcbutils.h +++ b/src/plugin-qt/xsettings/modules/api/xcbutils.h @@ -14,6 +14,14 @@ class XcbUtils : public QObject { Q_OBJECT public: + struct MonitorSizeInfo + { + uint16_t width; + uint16_t height; + uint32_t mmWidth; + uint32_t mmHeight; + }; + static XcbUtils &getInstance(); xcb_window_t createWindows(); xcb_atom_t getAtom(const char *name, bool exist = false); @@ -28,6 +36,7 @@ class XcbUtils : public QObject QByteArray getXcbAtomProperty(xcb_atom_t atom); QByteArray xcbPropertyReplyDataToArray(xcb_get_property_reply_t *reply); QString getXcbAtomName(xcb_atom_t atom); + QList getMonitorSizeInfos(); private: XcbUtils(QObject *parent = nullptr);