From 45ac48581dd51abfb9076927b35154c60b831230 Mon Sep 17 00:00:00 2001 From: caixiangrong Date: Wed, 3 Dec 2025 15:24:35 +0800 Subject: [PATCH] feat: add xsettings plugin implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Added new xsettings plugin module for deepin-service-manager 2. Implemented XSettings protocol support with DConfig integration 3. Added XCB utilities for X11 property management 4. Implemented scale factor management and DPI calculation 5. Added Firefox DPI configuration support 6. Included Plymouth theme scaling support 7. Added comprehensive configuration file handling Log: Added XSettings plugin for system configuration management Influence: 1. Test XSettings property retrieval and modification via D-Bus 2. Verify scale factor changes affect system DPI and cursor size 3. Check Firefox DPI configuration updates 4. Test Plymouth theme scaling during boot 5. Verify XResources updates for cursor theme and DPI settings 6. Test individual screen scaling factors configuration 7. Validate configuration file persistence and error handling feat: 添加 xsettings 插件实现 1. 新增 deepin-service-manager 的 xsettings 插件模块 2. 实现 XSettings 协议支持并与 DConfig 集成 3. 添加 XCB 工具类用于 X11 属性管理 4. 实现缩放因子管理和 DPI 计算 5. 添加 Firefox DPI 配置支持 6. 包含 Plymouth 主题缩放支持 7. 添加完整的配置文件处理功能 Log: 新增系统配置管理的 XSettings 插件 Influence: 1. 测试通过 D-Bus 获取和修改 XSettings 属性 2. 验证缩放因子变化对系统 DPI 和光标大小的影响 3. 检查 Firefox DPI 配置更新 4. 测试启动时 Plymouth 主题缩放效果 5. 验证光标主题和 DPI 设置的 XResources 更新 6. 测试独立屏幕缩放因子配置 7. 验证配置文件持久化和错误处理 PMS: TASK-381269 --- src/plugin-qt/CMakeLists.txt | 3 +- src/plugin-qt/xsettings/CMakeLists.txt | 35 + src/plugin-qt/xsettings/impl/dconfinfos.cpp | 294 ++++++++ src/plugin-qt/xsettings/impl/dconfinfos.h | 56 ++ .../xsettings/impl/scalefactorhelper.cpp | 6 + .../xsettings/impl/scalefactorhelper.h | 26 + src/plugin-qt/xsettings/impl/xsdatainfo.cpp | 284 +++++++ src/plugin-qt/xsettings/impl/xsdatainfo.h | 59 ++ src/plugin-qt/xsettings/impl/xsettings1.cpp | 73 ++ src/plugin-qt/xsettings/impl/xsettings1.h | 43 ++ .../xsettings/impl/xsettingsmanager.cpp | 701 ++++++++++++++++++ .../xsettings/impl/xsettingsmanager.h | 77 ++ .../xsettings/misc/org.deepin.XSettings.json | 352 +++++++++ .../misc/org.deepin.dde.XSettings.service | 4 + .../xsettings/misc/plugin-dde-xsettings.json | 13 + .../xsettings/modules/api/keyfile.cpp | 187 +++++ src/plugin-qt/xsettings/modules/api/keyfile.h | 44 ++ src/plugin-qt/xsettings/modules/api/utils.cpp | 74 ++ src/plugin-qt/xsettings/modules/api/utils.h | 53 ++ .../xsettings/modules/api/xcbutils.cpp | 345 +++++++++ .../xsettings/modules/api/xcbutils.h | 42 ++ .../xsettings/modules/common/common.h | 55 ++ src/plugin-qt/xsettings/plugin.cpp | 32 + .../xsettings/types/arrayOfColor.cpp | 10 + src/plugin-qt/xsettings/types/arrayOfColor.h | 13 + .../xsettings/types/scaleFactors.cpp | 10 + src/plugin-qt/xsettings/types/scaleFactors.h | 14 + 27 files changed, 2904 insertions(+), 1 deletion(-) create mode 100644 src/plugin-qt/xsettings/CMakeLists.txt create mode 100644 src/plugin-qt/xsettings/impl/dconfinfos.cpp create mode 100644 src/plugin-qt/xsettings/impl/dconfinfos.h create mode 100644 src/plugin-qt/xsettings/impl/scalefactorhelper.cpp create mode 100644 src/plugin-qt/xsettings/impl/scalefactorhelper.h create mode 100644 src/plugin-qt/xsettings/impl/xsdatainfo.cpp create mode 100644 src/plugin-qt/xsettings/impl/xsdatainfo.h create mode 100644 src/plugin-qt/xsettings/impl/xsettings1.cpp create mode 100644 src/plugin-qt/xsettings/impl/xsettings1.h create mode 100644 src/plugin-qt/xsettings/impl/xsettingsmanager.cpp create mode 100644 src/plugin-qt/xsettings/impl/xsettingsmanager.h create mode 100644 src/plugin-qt/xsettings/misc/org.deepin.XSettings.json create mode 100644 src/plugin-qt/xsettings/misc/org.deepin.dde.XSettings.service create mode 100644 src/plugin-qt/xsettings/misc/plugin-dde-xsettings.json create mode 100644 src/plugin-qt/xsettings/modules/api/keyfile.cpp create mode 100644 src/plugin-qt/xsettings/modules/api/keyfile.h create mode 100644 src/plugin-qt/xsettings/modules/api/utils.cpp create mode 100644 src/plugin-qt/xsettings/modules/api/utils.h create mode 100644 src/plugin-qt/xsettings/modules/api/xcbutils.cpp create mode 100644 src/plugin-qt/xsettings/modules/api/xcbutils.h create mode 100644 src/plugin-qt/xsettings/modules/common/common.h create mode 100644 src/plugin-qt/xsettings/plugin.cpp create mode 100644 src/plugin-qt/xsettings/types/arrayOfColor.cpp create mode 100644 src/plugin-qt/xsettings/types/arrayOfColor.h create mode 100644 src/plugin-qt/xsettings/types/scaleFactors.cpp create mode 100644 src/plugin-qt/xsettings/types/scaleFactors.h diff --git a/src/plugin-qt/CMakeLists.txt b/src/plugin-qt/CMakeLists.txt index 6d43a8fe..645add62 100644 --- a/src/plugin-qt/CMakeLists.txt +++ b/src/plugin-qt/CMakeLists.txt @@ -4,4 +4,5 @@ add_subdirectory("OOMScoreAdjust") add_subdirectory("thememanager") -add_subdirectory("wallpaperslideshow") \ No newline at end of file +add_subdirectory("wallpaperslideshow") +add_subdirectory("xsettings") \ No newline at end of file diff --git a/src/plugin-qt/xsettings/CMakeLists.txt b/src/plugin-qt/xsettings/CMakeLists.txt new file mode 100644 index 00000000..20ee3fe5 --- /dev/null +++ b/src/plugin-qt/xsettings/CMakeLists.txt @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +# +# SPDX-License-Identifier: LGPL-3.0-or-later +set(BIN_NAME "plugin-dde-xsettings") + +project(${BIN_NAME}) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +include(GNUInstallDirs) +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(XCB REQUIRED IMPORTED_TARGET xcb) + +add_library(${BIN_NAME} MODULE + ${SRCS} +) + +target_link_libraries(${BIN_NAME} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::DBus + PkgConfig::XCB + Dtk${DTK_VERSION_MAJOR}::Core +) + +install(TARGETS ${BIN_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}/deepin-service-manager/) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/plugin-dde-xsettings.json DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/deepin-service-manager/user/) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/org.deepin.XSettings.json DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/dsg/configs/org.deepin.dde.daemon) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/org.deepin.dde.XSettings.service DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/dbus-1/services/) diff --git a/src/plugin-qt/xsettings/impl/dconfinfos.cpp b/src/plugin-qt/xsettings/impl/dconfinfos.cpp new file mode 100644 index 00000000..ef2058c0 --- /dev/null +++ b/src/plugin-qt/xsettings/impl/dconfinfos.cpp @@ -0,0 +1,294 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "dconfinfos.h" + +#include + +DconfInfo::DconfInfo(QString dconfKey, QString xsKey, DconfValueType dconfType, int8_t xsType) + : dconfKey(dconfKey) + , dconfType(dconfType) + , xsKey(xsKey) + , xsType(xsType) +{ +} + +void DconfInfo::setGsToXsFunc(ConverFun func) +{ + convertGsToXs = func; +} + +void DconfInfo::setXsToGsFunc(ConverFun func) +{ + convertXsToGs = func; +} + +XsValue DconfInfo::getValue(const DTK_CORE_NAMESPACE::DConfig &dconf) +{ + XsValue retVal; + if (!dconf.isValid()) { + return retVal; + } + + bool bOk = false; + switch (dconfType) { + case typeOfBool: + case typeOfInt: + int valInt; + valInt = dconf.value(dconfKey).toInt(&bOk); + if (!bOk) { + return retVal; + } + retVal = valInt; + break; + case typeOfString: + retVal = dconf.value(dconfKey).toString(); + break; + case typeOfDoublue: + double valDouble; + valDouble = dconf.value(dconfKey).toDouble(&bOk); + if (!bOk) { + return retVal; + } + retVal = valDouble; + break; + default: + return retVal; + } + + if (convertGsToXs) { + retVal = convertGsToXs(retVal); + } + + return retVal; +} + +bool DconfInfo::setValue(DTK_CORE_NAMESPACE::DConfig &dconf, XsValue &value) +{ + XsValue converValue = value; + int *valInt; + QString *valString; + double *valDouble; + if (convertXsToGs) { + converValue = convertXsToGs(value); + } + + switch (dconfType) { + case typeOfBool: + valInt = std::get_if(&converValue); + if (valInt == nullptr) { + return false; + } + + if (*valInt == 1) { + dconf.setValue(dconfKey, true); + } else { + dconf.setValue(dconfKey, false); + } + break; + case typeOfInt: + valInt = std::get_if(&converValue); + if (valInt == nullptr) { + return false; + } + dconf.setValue(dconfKey, *valInt); + break; + case typeOfString: + valString = std::get_if(&converValue); + if (valString == nullptr) { + return false; + } + + dconf.setValue(dconfKey, *valString); + break; + case typeOfDoublue: + valDouble = std::get_if(&converValue); + if (valDouble == nullptr) { + return false; + } + + dconf.setValue(dconfKey, *valDouble); + break; + } + + return true; +} + +DconfInfo::ConverFun DconfInfo::getGsToXsFunc(ConverFun func) +{ + return convertGsToXs; +} + +DconfInfo::ConverFun DconfInfo::getXsToGsFunc(ConverFun func) +{ + return convertXsToGs; +} + +XsValue DconfInfo::convertStrToDouble(XsValue &value) +{ + QString *tempValue = std::get_if(&value); + if (tempValue == nullptr) { + return XsValue(); + } + + return tempValue->toDouble(); +} + +XsValue DconfInfo::convertDoubleToStr(XsValue &value) +{ + double *tempValue = std::get_if(&value); + if (tempValue == nullptr) { + return XsValue(); + } + return QString::number(*tempValue); +} + +XsValue DconfInfo::convertStrToColor(XsValue &value) +{ + ColorValueInfo valueInfo; + QString *tempValue = std::get_if(&value); + if (tempValue == nullptr) { + return XsValue(); + } + QStringList valueArray = tempValue->split(","); + if (valueArray.length() != 4) { + return valueInfo; + } + + for (int i = 0; i < colorSize; i++) { + valueInfo[i] = (uint16_t)((valueArray[i].toDouble()) / double(UINT16_MAX) * double(UINT8_MAX)); + } + return valueInfo; +} + +XsValue DconfInfo::convertColorToStr(XsValue &value) +{ + ColorValueInfo *tempValue = std::get_if(&value); + if (tempValue == nullptr) { + return XsValue(); + } + + uint16_t arr[4]; + for (int i = 0; i < colorSize; i++) { + arr[i] = (uint16_t)((double)((*tempValue)[i]) / (double)(UINT8_MAX) * (double)(UINT16_MAX)); + } + return QString::asprintf("%d,%d,%d,%d", arr[0], arr[1], arr[2], arr[3]); +} + +QString DconfInfo::getDconfKey() +{ + return dconfKey; +} + +QString DconfInfo::getXsetKey() +{ + return xsKey; +} + +int8_t DconfInfo::getKeyDType() +{ + return dconfType; +} + +int8_t DconfInfo::getKeySType() +{ + if (xsType != HeadTypeInvalid) { + return xsType; + } + + if (dconfType == typeOfString || dconfType == typeOfDoublue) { + return HeadTypeString; + } else { + return HeadTypeInteger; + } +} + +DconfInfos::DconfInfos() + : dconfArray{ QSharedPointer(new DconfInfo("theme-name", "Net/ThemeName", typeOfString)), + QSharedPointer(new DconfInfo("icon-theme-name", "Net/IconThemeName", typeOfString)), + QSharedPointer(new DconfInfo("fallback-icon-theme", "Net/FallbackIconTheme", typeOfString)), + QSharedPointer(new DconfInfo("sound-theme-name", "Net/SoundThemeName", typeOfString)), + QSharedPointer(new DconfInfo("gtk-theme-name", "Gtk/ThemeName", typeOfString)), + QSharedPointer(new DconfInfo("gtk-cursor-theme-name", "Gtk/CursorThemeName", typeOfString)), + QSharedPointer(new DconfInfo("gtk-font-name", "Gtk/FontName", typeOfString)), + QSharedPointer(new DconfInfo("gtk-key-theme-name", "Gtk/KeyThemeName", typeOfString)), + QSharedPointer(new DconfInfo("gtk-color-palette", "Gtk/ColorPalette", typeOfString)), + QSharedPointer(new DconfInfo("gtk-toolbar-style", "Gtk/ToolbarStyle", typeOfString)), + QSharedPointer(new DconfInfo("gtk-toolbar-icon-size", "Gtk/ToolbarIconSize", typeOfString)), + QSharedPointer(new DconfInfo("gtk-color-scheme", "Gtk/ColorScheme", typeOfString)), + QSharedPointer(new DconfInfo("gtk-im-preedit-style", "Gtk/IMPreeditStyle", typeOfString)), + QSharedPointer(new DconfInfo("gtk-im-status-style", "Gtk/IMStatusStyle", typeOfString)), + QSharedPointer(new DconfInfo("gtk-im-module", "Gtk/IMModule", typeOfString)), + QSharedPointer(new DconfInfo("gtk-modules", "Gtk/Modules", typeOfString)), + QSharedPointer(new DconfInfo("gtk-menubar-accel", "Gtk/MenuBarAccel", typeOfString)), + QSharedPointer(new DconfInfo("xft-hintstyle", "Xft/HintStyle", typeOfString)), + QSharedPointer(new DconfInfo("xft-rgba", "Xft/RGBA", typeOfString)), + QSharedPointer(new DconfInfo("cursor-blink-time", "Net/CursorBlinkTime", typeOfInt)), + QSharedPointer(new DconfInfo("gtk-cursor-blink-timeout", "Net/CursorBlinkTimeout", typeOfInt)), + QSharedPointer(new DconfInfo("double-click-time", "Net/DoubleClickTime", typeOfInt)), + QSharedPointer(new DconfInfo("double-click-distance", "Net/DoubleClickDistance", typeOfInt)), + QSharedPointer(new DconfInfo("dnd-drag-threshold", "Net/DndDragThreshold", typeOfInt)), + QSharedPointer(new DconfInfo("gtk-cursor-theme-size", "Gtk/CursorThemeSize", typeOfInt)), + QSharedPointer(new DconfInfo("gtk-timeout-initial", "Gtk/TimeoutInitial", typeOfInt)), + QSharedPointer(new DconfInfo("gtk-timeout-repeat", "Gtk/TimeoutRepeat", typeOfInt)), + QSharedPointer(new DconfInfo("gtk-recent-files-max-age", "Gtk/RecentFilesMaxAge", typeOfInt)), + QSharedPointer(new DconfInfo("xft-dpi", "Xft/DPI", typeOfInt)), + QSharedPointer(new DconfInfo("cursor-blink", "Net/CursorBlink", typeOfBool)), + QSharedPointer(new DconfInfo("enable-event-sounds", "Net/EnableEventSounds", typeOfBool)), + QSharedPointer(new DconfInfo("enable-input-feedback-sounds", "Net/EnableInputFeedbackSounds", typeOfBool)), + QSharedPointer(new DconfInfo("gtk-can-change-accels", "Gtk/CanChangeAccels", typeOfBool)), + QSharedPointer(new DconfInfo("gtk-menu-images", "Gtk/MenuImages", typeOfBool)), + QSharedPointer(new DconfInfo("gtk-button-images", "Gtk/ButtonImages", typeOfBool)), + QSharedPointer(new DconfInfo("gtk-enable-animations", "Gtk/EnableAnimations", typeOfBool)), + QSharedPointer(new DconfInfo("gtk-show-input-method-menu", "Gtk/ShowInputMethodMenu", typeOfBool)), + QSharedPointer(new DconfInfo("gtk-show-unicode-menu", "Gtk/ShowUnicodeMenu", typeOfBool)), + QSharedPointer(new DconfInfo("gtk-auto-mnemonics", "Gtk/AutoMnemonics", typeOfBool)), + QSharedPointer(new DconfInfo("gtk-recent-files-enabled", "Gtk/RecentFilesEnabled", typeOfBool)), + QSharedPointer(new DconfInfo("gtk-shell-shows-app-menu", "Gtk/ShellShowsAppMenu", typeOfBool)), + QSharedPointer(new DconfInfo("xft-antialias", "Xft/Antialias", typeOfBool)), + QSharedPointer(new DconfInfo("xft-hinting", "Xft/Hinting", typeOfBool)), + QSharedPointer(new DconfInfo("qt-font-name", "Qt/FontName", typeOfString)), + QSharedPointer(new DconfInfo("qt-mono-font-name", "Qt/MonoFontName", typeOfString)), + QSharedPointer(new DconfInfo("dtk-window-radius", "DTK/WindowRadius", typeOfInt)), + QSharedPointer(new DconfInfo("dtk-size-mode", "DTK/SizeMode", typeOfInt)), + QSharedPointer(new DconfInfo("qt-scrollbar-policy", "Qt/ScrollBarPolicy", typeOfInt)), + QSharedPointer(new DconfInfo("primary-monitor-name", "Gdk/PrimaryMonitorName", typeOfString)) } + +{ + QSharedPointer qtActiveColor(new DconfInfo("qt-active-color", "Qt/ActiveColor", typeOfString, HeadTypeColor)); + qtActiveColor->setGsToXsFunc(std::bind(&DconfInfo::convertStrToColor, qtActiveColor.get(), std::placeholders::_1)); + qtActiveColor->setXsToGsFunc(std::bind(&DconfInfo::convertColorToStr, qtActiveColor.get(), std::placeholders::_1)); + dconfArray.push_back(qtActiveColor); + + QSharedPointer qtDarkActiveColor(new DconfInfo("qt-dark-active-color", "Qt/DarkActiveColor", typeOfString, HeadTypeColor)); + qtDarkActiveColor->setGsToXsFunc(std::bind(&DconfInfo::convertStrToColor, qtActiveColor.get(), std::placeholders::_1)); + qtDarkActiveColor->setXsToGsFunc(std::bind(&DconfInfo::convertColorToStr, qtActiveColor.get(), std::placeholders::_1)); + dconfArray.push_back(qtDarkActiveColor); + + QSharedPointer qtFontPoint(new DconfInfo("qt-font-point-size", "Qt/FontPointSize", typeOfDoublue)); + qtFontPoint->setGsToXsFunc(std::bind(&DconfInfo::convertDoubleToStr, qtFontPoint.get(), std::placeholders::_1)); + qtFontPoint->setXsToGsFunc(std::bind(&DconfInfo::convertStrToDouble, qtFontPoint.get(), std::placeholders::_1)); + dconfArray.push_back(qtFontPoint); +} + +QSharedPointer DconfInfos::getByDconfKey(const QString &dconfKey) +{ + for (auto item : dconfArray) { + if (item->getDconfKey() == dconfKey) { + return item; + } + } + + return nullptr; +} + +QSharedPointer DconfInfos::getByXSKey(const QString &xsettingKey) +{ + for (auto item : dconfArray) { + if (item->getXsetKey() == xsettingKey) { + return item; + } + } + + return nullptr; +} diff --git a/src/plugin-qt/xsettings/impl/dconfinfos.h b/src/plugin-qt/xsettings/impl/dconfinfos.h new file mode 100644 index 00000000..a4b057a6 --- /dev/null +++ b/src/plugin-qt/xsettings/impl/dconfinfos.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef DCONFINFOS_H +#define DCONFINFOS_H +#include "modules/common/common.h" + +#include + +#include + +class DconfInfo : public QObject +{ + Q_OBJECT + typedef std::function ConverFun; + +public: + DconfInfo(QString dconfKey, QString xsKey, DconfValueType dconfType, int8_t xsType = HeadTypeInvalid); + void setGsToXsFunc(ConverFun func); + void setXsToGsFunc(ConverFun func); + XsValue getValue(const DTK_CORE_NAMESPACE::DConfig &dconf); + bool setValue(DTK_CORE_NAMESPACE::DConfig &dconf, XsValue &value); + ConverFun getGsToXsFunc(ConverFun func); + ConverFun getXsToGsFunc(ConverFun func); + XsValue convertStrToDouble(XsValue &value); + XsValue convertDoubleToStr(XsValue &value); + XsValue convertStrToColor(XsValue &value); + XsValue convertColorToStr(XsValue &value); + QString getDconfKey(); + QString getXsetKey(); + int8_t getKeySType(); + int8_t getKeyDType(); + +private: + QString dconfKey; + DconfValueType dconfType; + QString xsKey; + int8_t xsType; + ConverFun convertGsToXs; + ConverFun convertXsToGs; +}; + +class DconfInfos : public QObject +{ + Q_OBJECT + +public: + DconfInfos(); + QSharedPointer getByDconfKey(const QString &dconfKey); + QSharedPointer getByXSKey(const QString &xsettingKey); + +private: + QVector> dconfArray; +}; + +#endif // DCONFINFOS_H diff --git a/src/plugin-qt/xsettings/impl/scalefactorhelper.cpp b/src/plugin-qt/xsettings/impl/scalefactorhelper.cpp new file mode 100644 index 00000000..98fdb867 --- /dev/null +++ b/src/plugin-qt/xsettings/impl/scalefactorhelper.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "scalefactorhelper.h" + +ScaleFactorHelper::ScaleFactorHelper() { } diff --git a/src/plugin-qt/xsettings/impl/scalefactorhelper.h b/src/plugin-qt/xsettings/impl/scalefactorhelper.h new file mode 100644 index 00000000..94407c2e --- /dev/null +++ b/src/plugin-qt/xsettings/impl/scalefactorhelper.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef SCALEFACTORHELPER_H +#define SCALEFACTORHELPER_H + +#include +#include +#include +#include + +#include + +class ScaleFactorHelper : public QObject +{ + typedef std::function)> CHANGECALLBACK; + +public: + ScaleFactorHelper(); + +private: + QSharedPointer sysDisplay; + CHANGECALLBACK callback; +}; + +#endif // SCALEFACTORHELPER_H diff --git a/src/plugin-qt/xsettings/impl/xsdatainfo.cpp b/src/plugin-qt/xsettings/impl/xsdatainfo.cpp new file mode 100644 index 00000000..74e3e32c --- /dev/null +++ b/src/plugin-qt/xsettings/impl/xsdatainfo.cpp @@ -0,0 +1,284 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "xsdatainfo.h" + +#include "modules/api/utils.h" + +#include + +XSItemInfo::XSItemInfo(QByteArray &datas) +{ + unMarshalXSItemInfoData(datas); +} + +XSItemInfo::XSItemInfo(const QString &prop, const XsValue &xsValue) +{ + if (std::get_if(&xsValue) != nullptr) { + initXSItemInfoInteger(prop, std::get(xsValue)); + } else if (std::get_if(&xsValue) != nullptr) { + initXSItemInfoString(prop, std::get(xsValue)); + } else if (std::get_if(&xsValue) != nullptr) { + initXSItemInfoColor(prop, std::get(xsValue)); + } else { + qDebug() << ""; + } +} + +void XSItemInfo::unMarshalXSItemInfoData(QByteArray &datas) +{ + + readXSItemHeader(datas); + + int tempInt = 0; + QString tempString = 0; + ColorValueInfo tempColor; + switch (head.type) { + case HeadTypeInteger: + Utils::readInteger(datas, tempInt); + value = tempInt; + break; + case HeadTypeString: + int length; + Utils::readInteger(datas, length); + Utils::readString(datas, tempString, length); + Utils::readSkip(datas, Utils::getPad(length)); + value = tempString; + break; + case HeadTypeColor: + for (int i = 0; i < colorSize; i++) { + Utils::readInteger(datas, tempColor[i]); + } + value = tempColor; + break; + default: + // todo + + ; + } +} + +QString xsValueToString(XsValue &value, uint type) +{ + QString retStr; + int *iVal; + QString *strVal; + ColorValueInfo *color; + switch (type) { + case HeadTypeInteger: + iVal = std::get_if(&value); + if (iVal == nullptr) { + break; + } + retStr = QString::number(*iVal); + break; + case HeadTypeString: + strVal = std::get_if(&value); + if (strVal == nullptr) { + return "nullptr"; + } + retStr = *strVal; + break; + case HeadTypeColor: + color = std::get_if(&value); + if (color == nullptr) { + return "nullptr"; + } + retStr = QString::asprintf("%d,%d,%d,%d", (*color)[0], (*color)[1], (*color)[2], (*color)[3]); + break; + default: + retStr = "unknown"; + break; + } + return retStr; +} + +bool XSItemInfo::marshalXSItemInfoData(QByteArray &datas) +{ + bool ret = false; + int length = 0; + QString *str = nullptr; + QByteArray strArray; + writeXSItemHeader(datas); + switch (head.type) { + case HeadTypeInteger: + if (std::get_if(&value) == nullptr) { + break; + } + Utils::writeInteger(datas, std::get(value)); + ret = true; + break; + case HeadTypeString: + str = std::get_if(&value); + if (str == nullptr) { + break; + } + strArray = str->toUtf8(); + length = strArray.length(); + Utils::writeInteger(datas, length); + + Utils::writeString(datas, strArray); + Utils::writeSkip(datas, Utils::getPad(length)); + ret = true; + break; + case HeadTypeColor: + ColorValueInfo *colorValue; + colorValue = std::get_if(&value); + if (colorValue == nullptr) { + break; + } + for (int i = 0; i < colorSize; i++) { + Utils::writeInteger(datas, (*colorValue)[i]); + } + ret = true; + break; + default: + // todo + ; + } + return ret; +} + +void XSItemInfo::readXSItemHeader(QByteArray &datas) +{ + Utils::readInteger(datas, head.type); + Utils::readSkip(datas, 1); + Utils::readInteger(datas, head.length); + Utils::readString(datas, head.name, static_cast(head.length)); + Utils::readSkip(datas, Utils::getPad(static_cast(head.length))); + Utils::readInteger(datas, head.lastChangeSerial); +} + +void XSItemInfo::writeXSItemHeader(QByteArray &datas) +{ + Utils::writeInteger(datas, head.type); + Utils::writeSkip(datas, 1); + Utils::writeInteger(datas, head.length); + Utils::writeString(datas, head.name.toUtf8()); + Utils::writeSkip(datas, Utils::getPad(static_cast(head.length))); + Utils::writeInteger(datas, head.lastChangeSerial); +} + +QString XSItemInfo::getHeadName() +{ + return head.name; +} + +XsValue XSItemInfo::getValue() +{ + return value; +} + +void XSItemInfo::modifyProperty(const XsSetting &setting) +{ + head.lastChangeSerial++; + value = setting.value; +} + +void XSItemInfo::initXSItemInfoInteger(const QString &prop, int value) +{ + head.name = prop; + head.length = prop.toUtf8().length(); + head.lastChangeSerial = 1; + head.type = HeadTypeInteger; + + this->value = value; +} + +void XSItemInfo::initXSItemInfoString(const QString &prop, QString value) +{ + head.name = prop; + head.length = prop.toUtf8().length(); + head.lastChangeSerial = 1; + head.type = HeadTypeString; + + this->value = value; +} + +void XSItemInfo::initXSItemInfoColor(const QString &prop, ColorValueInfo value) +{ + head.name = prop; + head.length = prop.toUtf8().length(); + head.lastChangeSerial = 1; + head.type = HeadTypeColor; + + this->value = value; +} + +XSDataInfo::XSDataInfo(QByteArray &datas) +{ + unMarshalSettingData(datas); +} + +void XSDataInfo::unMarshalSettingData(QByteArray &datas) +{ + if (datas.isEmpty()) { + byteOrder = 0; + serial = 0; + numSettings = 0; + return; + } + + Utils::readInteger(datas, byteOrder); + Utils::readSkip(datas, 3); + Utils::readInteger(datas, serial); + Utils::readInteger(datas, numSettings); + for (uint32_t i = 0; i < numSettings; i++) { + QSharedPointer info(new XSItemInfo(datas)); + items.push_back(info); + } +} + +QByteArray XSDataInfo::marshalSettingData() +{ + QByteArray array; + + Utils::writeInteger(array, byteOrder); + Utils::writeSkip(array, 3); + Utils::writeInteger(array, serial); + Utils::writeInteger(array, numSettings); + for (auto item : items) { + if (!item->marshalXSItemInfoData(array)) { + qWarning() << "marshal xsetting info failed"; + return {}; + } + } + return array; +} + +QString XSDataInfo::listProps() +{ + QStringList content; + + for (int i = 0; i < items.length(); i++) { + content.append("\"" + items[i]->getHeadName() + "\""); + } + + return "[" + content.join(",") + "]"; +} + +QSharedPointer XSDataInfo::getPropItem(QString prop) +{ + for (auto item : items) { + if (item->getHeadName() == prop) { + return item; + } + } + + return QSharedPointer(); +} + +void XSDataInfo::inserItem(QSharedPointer itemInfo) +{ + items.push_back(itemInfo); +} + +void XSDataInfo::increaseSerial() +{ + serial++; +} + +void XSDataInfo::increaseNumSettings() +{ + numSettings++; +} diff --git a/src/plugin-qt/xsettings/impl/xsdatainfo.h b/src/plugin-qt/xsettings/impl/xsdatainfo.h new file mode 100644 index 00000000..f2ca0197 --- /dev/null +++ b/src/plugin-qt/xsettings/impl/xsdatainfo.h @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef XSDATAINFO_H +#define XSDATAINFO_H + +#include "modules/common/common.h" + +#include +#include +#include + +class XSItemInfo : public QObject +{ + Q_OBJECT +public: + XSItemInfo(QByteArray &datas); + XSItemInfo(const QString &prop, const XsValue &xsValue); + void unMarshalXSItemInfoData(QByteArray &datas); + bool marshalXSItemInfoData(QByteArray &datas); + void readXSItemHeader(QByteArray &datas); + void writeXSItemHeader(QByteArray &datas); + QString getHeadName(); + XsValue getValue(); + void modifyProperty(const XsSetting &setting); + +private: + void initXSItemInfoInteger(const QString &prop, int value); + void initXSItemInfoString(const QString &prop, QString value); + void initXSItemInfoColor(const QString &prop, ColorValueInfo value); + +private: + ItemHeader head; + XsValue value; +}; + +class XSDataInfo : public QObject +{ + Q_OBJECT + +public: + explicit XSDataInfo(QByteArray &datas); + void unMarshalSettingData(QByteArray &datas); + QByteArray marshalSettingData(); + QString listProps(); + QSharedPointer getPropItem(QString prop); + void inserItem(QSharedPointer itemInfo); + void increaseSerial(); + void increaseNumSettings(); + +private: + uint8_t byteOrder; + uint32_t serial; + uint32_t numSettings; + QVector> items; +}; + +QString xsValueToString(XsValue &value, uint type); +#endif // XSDATAINFO_H diff --git a/src/plugin-qt/xsettings/impl/xsettings1.cpp b/src/plugin-qt/xsettings/impl/xsettings1.cpp new file mode 100644 index 00000000..15608028 --- /dev/null +++ b/src/plugin-qt/xsettings/impl/xsettings1.cpp @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "xsettings1.h" + +XSettings1::XSettings1(QObject *parent) + : QObject(parent) + , xSettingsmanger(new XSettingsManager(this)) +{ + registerScaleFactorsMetaType(); + registerArrayOfColorMetaType(); + connect(xSettingsmanger.get(), &XSettingsManager::SetScaleFactorStarted, this, &XSettings1::SetScaleFactorStarted); + connect(xSettingsmanger.get(), &XSettingsManager::SetScaleFactorDone, this, &XSettings1::SetScaleFactorDone); +} + +XSettings1::~XSettings1() { } + +QList XSettings1::GetColor(const QString &prop) +{ + return xSettingsmanger->getColor(prop); +} + +qint32 XSettings1::GetInteger(const QString &prop) +{ + return xSettingsmanger->getInteger(prop); +} + +double XSettings1::GetScaleFactor() +{ + return xSettingsmanger->getScaleFactor(); +} + +ScaleFactors XSettings1::GetScreenScaleFactors() +{ + return xSettingsmanger->getScreenScaleFactors(); +} + +QString XSettings1::GetString(const QString &prop) +{ + return xSettingsmanger->getString(prop); +} + +QString XSettings1::ListProps() +{ + return xSettingsmanger->listProps(); +} + +void XSettings1::SetColor(const QString &prop, const QList &v) +{ + xSettingsmanger->setColor(prop, v); +} + +void XSettings1::SetInteger(const QString &prop, const qint32 &v) +{ + xSettingsmanger->setInteger(prop, v); +} + +void XSettings1::SetScaleFactor(const double &scale) +{ + ScaleFactors factors; + factors["ALL"] = scale; + xSettingsmanger->setScreenScaleFactors(factors, true); +} + +void XSettings1::SetScreenScaleFactors(const ScaleFactors &factors) +{ + xSettingsmanger->setScreenScaleFactors(factors, true); +} + +void XSettings1::SetString(const QString &prop, const QString &v) +{ + xSettingsmanger->setString(prop, v); +} diff --git a/src/plugin-qt/xsettings/impl/xsettings1.h b/src/plugin-qt/xsettings/impl/xsettings1.h new file mode 100644 index 00000000..9d822c91 --- /dev/null +++ b/src/plugin-qt/xsettings/impl/xsettings1.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef XSETTINGS_H +#define XSETTINGS_H + +#include "types/arrayOfColor.h" +#include "types/scaleFactors.h" +#include "xsettingsmanager.h" + +#include +#include + +class XSettings1 : public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.deepin.dde.XSettings1") +public: + XSettings1(QObject *parent = nullptr); + ~XSettings1(); + +public Q_SLOTS: // METHODS + ArrayOfColor GetColor(const QString &prop); + qint32 GetInteger(const QString &prop); + double GetScaleFactor(); + ScaleFactors GetScreenScaleFactors(); + QString GetString(const QString &prop); + QString ListProps(); + void SetColor(const QString &prop, const ArrayOfColor &v); + void SetInteger(const QString &prop, const qint32 &v); + void SetScaleFactor(const double &scale); + void SetScreenScaleFactors(const ScaleFactors &factors); + void SetString(const QString &prop, const QString &v); + +Q_SIGNALS: // SIGNALS + void SetScaleFactorDone(); + void SetScaleFactorStarted(); + +private: + QSharedPointer xSettingsmanger; +}; + +#endif diff --git a/src/plugin-qt/xsettings/impl/xsettingsmanager.cpp b/src/plugin-qt/xsettings/impl/xsettingsmanager.cpp new file mode 100644 index 00000000..86349e10 --- /dev/null +++ b/src/plugin-qt/xsettings/impl/xsettingsmanager.cpp @@ -0,0 +1,701 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "xsettingsmanager.h" + +#include "modules/api/utils.h" +#include "xsdatainfo.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +const static int DPI_FALLBACK = 96; +const static int BASE_CURSORSIZE = 24; +const static QString PLYMOUTH_CONFIGFILE = "/etc/plymouth/plymouthd.conf"; + +XSettingsManager::XSettingsManager(QObject *parent) + : QObject(parent) + , m_settingDconfig(DTK_CORE_NAMESPACE::DConfig::create("org.deepin.dde.daemon", "org.deepin.XSettings")) + , m_greeterInterface(new QDBusInterface("org.deepin.dde.Greeter1", "/org/deepin/dde/Greeter1", "org.deepin.dde.Greeter1", QDBusConnection::systemBus())) + , m_sysDaemonInterface(new QDBusInterface("com.deepin.daemon.Daemon", "/com/deepin/daemon/Daemon", "com.deepin.daemon.Daemon", QDBusConnection::systemBus())) + , m_xcbUtils(XcbUtils::getInstance()) +{ + connect(m_settingDconfig, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, &XSettingsManager::handleDConfigChangedCb); + setSettings(getSettingsInSchema()); + updateDPI(); + updateXResources(); + QThreadPool::globalInstance()->start([this]() { + updateFirefoxDPI(); + }); +} + +ArrayOfColor XSettingsManager::getColor(const QString &prop) +{ + ArrayOfColor arrayOfColor; + XsValue value = getSettingValue(prop); + + if (std::get_if(&value) == nullptr) { + return arrayOfColor; + } + ColorValueInfo color = std::get(value); + for (int i = 0; i < colorSize; i++) { + arrayOfColor.push_back(color[i]); + } + return arrayOfColor; +} + +int XSettingsManager::getInteger(const QString &prop) +{ + XsValue value = getSettingValue(prop); + + if (std::get_if(&value) == nullptr) { + return 0; + } + + return std::get(value); +} + +double XSettingsManager::getScaleFactor() +{ + if (m_settingDconfig->isValid()) { + return m_settingDconfig->value(dcKeyScaleFactor).toDouble(); + } + + return 0; +} + +ScaleFactors XSettingsManager::getScreenScaleFactors() +{ + if (m_settingDconfig->isValid()) { + QString factors = m_settingDconfig->value(cKeyIndividualScaling).toString(); + return parseScreenFactors(factors); + } + + return ScaleFactors(); +} + +QString XSettingsManager::getString(const QString &prop) +{ + XsValue value = getSettingValue(prop); + + if (std::get_if(&value) == nullptr) { + return QString(); + } + + return std::get(value); +} + +QByteArray XSettingsManager::getSettingPropValue() +{ + xcb_atom_t atom = m_xcbUtils.getAtom("_XSETTINGS_SETTINGS"); + if (atom == XCB_NONE) { + qWarning() << "can not get _XSETTINGS_SETTINGS Atom"; + return QByteArray(); + } + return m_xcbUtils.getXcbAtomProperty(atom); +} + +void XSettingsManager::emitSignalSetScaleFactor(bool done, bool emitSignal) +{ + if (!emitSignal) { + return; + } + if (done) { + Q_EMIT SetScaleFactorDone(); + } else { + Q_EMIT SetScaleFactorStarted(); + } +} + +QString XSettingsManager::listProps() +{ + QByteArray datas = getSettingPropValue(); + + XSDataInfo xSDataInfo(datas); + + return xSDataInfo.listProps(); +} + +void XSettingsManager::setColor(const QString &prop, const ArrayOfColor &v) +{ + if (v.size() != 4) { + return; + } + ColorValueInfo color; + for (int i = 0; i < colorSize; i++) { + color[i] = v[i]; + } + + XsSetting xsValue; + xsValue.prop = prop; + xsValue.type = HeadTypeColor; + xsValue.value = color; + + QVector xsSetting{ xsValue }; + setSettings(xsSetting); + setGSettingsByXProp(prop, color); +} + +void XSettingsManager::setInteger(const QString &prop, const int &v) +{ + XsSetting xsValue; + xsValue.prop = prop; + xsValue.type = HeadTypeInteger; + xsValue.value = v; + + QVector xsSetting{ xsValue }; + setSettings(xsSetting); + + setGSettingsByXProp(prop, v); +} + +void XSettingsManager::setScreenScaleFactors(const ScaleFactors &factors, bool emitSignal) +{ + if (factors.isEmpty()) { + return; + } + + for (auto iter : factors.keys()) { + if (factors[iter] < 0) { + return; + } + } + + // err := m.dsfHelper.SetScaleFactors(factors) + + double singleFactor = 0; + if (factors.size() == 1) { + singleFactor = factors.first(); + } else { + if (factors.count("All") == 1) { + singleFactor = factors["All"]; + } else { + singleFactor = 1; + } + } + setSingleScaleFactor(singleFactor, emitSignal); + + QString factorsJoined = joinScreenScaleFactors(factors); + if (m_settingDconfig->isValid()) { + m_settingDconfig->setValue(cKeyIndividualScaling, factorsJoined); + } + + setScreenScaleFactorsForQt(factors); +} + +void XSettingsManager::setString(const QString &prop, const QString &v) +{ + XsSetting xsValue; + xsValue.prop = prop; + xsValue.type = HeadTypeString; + xsValue.value = v; + + QVector xsSetting{ xsValue }; + setSettings(xsSetting); + + setGSettingsByXProp(prop, v); // 设置dconfig +} + +void XSettingsManager::handleDConfigChangedCb(const QString &key) +{ + static const QStringList excludedkeys = { "xft-dpi", "scale-factor", "gtk-cursor-theme-size", "window-scale" }; + if (excludedkeys.contains(key)) { + return; + } + if (key == "gtk-cursor-theme-name") { + updateXResources(); + } + QSharedPointer dconfInfo = m_dconfInfos.getByDconfKey(key); + if (dconfInfo.isNull()) { + return; + } + XsValue value = dconfInfo->getValue(*m_settingDconfig); + XsSetting xsValue; + xsValue.prop = dconfInfo->getXsetKey(); + xsValue.type = dconfInfo->getKeySType(); + xsValue.value = value; + QVector xsSetting{ xsValue }; + setSettings(xsSetting); +} + +void XSettingsManager::updateDPI() +{ + double scale = 1; + QVector xsSettngVec; + int scaledDpi = static_cast((DPI_FALLBACK * 1024) * scale); + int cursorSize; + int windowScale; + + if (m_settingDconfig->isValid()) { + bool bOk = false; + double tempScaleFactor = m_settingDconfig->value(dcKeyScaleFactor).toDouble(&bOk); + if (bOk) { + if (tempScaleFactor <= 0) { + scale = 1; + } else { + scale = tempScaleFactor; + } + } + + bOk = false; + int tempXftDpi = m_settingDconfig->value(dcKeyXftDpi).toInt(&bOk); + if (bOk) { + if (tempXftDpi != scaledDpi) { + m_settingDconfig->setValue(dcKeyXftDpi, scale); + XsSetting setting; + setting.prop = dcKeyXftDpi; + setting.value = scaledDpi; + setting.type = HeadTypeInteger; + + xsSettngVec.push_back(setting); + } + } + + bOk = false; + windowScale = m_settingDconfig->value(dcKeyWindowScale).toInt(&bOk); + if (bOk) { + if (windowScale > 1) { + scaledDpi = static_cast(DPI_FALLBACK * 1024); + } + } + + bOk = false; + int tempGtkCursorThemeSize = m_settingDconfig->value(dcKeyGtkCursorThemeSize).toInt(&bOk); + if (bOk) { + cursorSize = tempGtkCursorThemeSize; + } + } + + int windowScalingFactor = getInteger("Gdk/WindowScalingFactor"); + if (windowScalingFactor != windowScale) { + XsSetting sWindowScalingFactor; + sWindowScalingFactor.prop = "Gdk/WindowScalingFactor"; + sWindowScalingFactor.value = windowScale; + sWindowScalingFactor.type = HeadTypeInteger; + + xsSettngVec.push_back(sWindowScalingFactor); + + XsSetting sDpi; + sDpi.prop = "Gdk/UnscaledDPI"; + sDpi.value = scaledDpi; + sDpi.type = HeadTypeInteger; + + xsSettngVec.push_back(sDpi); + + XsSetting sCursorThemeSize; + sCursorThemeSize.prop = "Gtk/CursorThemeSize"; + sCursorThemeSize.value = cursorSize; + sCursorThemeSize.type = HeadTypeInteger; + + xsSettngVec.push_back(sCursorThemeSize); + } + + if (!xsSettngVec.isEmpty()) { + setSettings(xsSettngVec); + updateXResources(); + } +} + +void XSettingsManager::updateXResources() +{ + QVector> xresourceInfos; + if (m_settingDconfig->isValid()) { + xresourceInfos.push_back(qMakePair(QString("Xcursor.theme"), m_settingDconfig->value("gtk-cursor-theme-name").toString())); + xresourceInfos.push_back(qMakePair(QString("Xcursor.size"), QString::number(m_settingDconfig->value(dcKeyGtkCursorThemeSize).toInt()))); + + double scaleFactor = m_settingDconfig->value(dcKeyScaleFactor).toDouble(); + int xftDpi = static_cast(DPI_FALLBACK * scaleFactor); + xresourceInfos.push_back(qMakePair(QString("Xft.dpi"), QString::number(xftDpi))); + } + + m_xcbUtils.updateXResources(xresourceInfos); +} + +QStringList getFirefoxConfigs(const QString &path) +{ + QStringList configs; + QString config; + QDir dir(path); + QDirIterator it(path, QStringList() << "prefs.js", QDir::Files, QDirIterator::Subdirectories); + while (it.hasNext()) { + QString config = it.next(); + configs.append(config); + } + + if (!configs.empty()) { + qWarning() << "found firefox config file: " << configs; + } + return configs; +} + +const QString ffKeyPixels = R"(user_pref("layout.css.devPixelsPerPx",)"; + +bool setFirefoxDPI(double value, QString src, QString dest) +{ + QFile file(src); + if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { + qWarning() << "failed to open file:" << file.errorString(); + return false; + } + QTextStream in(&file); + QStringList lines; + while (!in.atEnd()) { + lines << in.readLine(); // 读取一行 + } + + QString target = QString("%1 \"%2\");").arg(ffKeyPixels).arg(QString::number(value, 'f', 2)); + bool found = false; + for (int i = 0; i < lines.size(); ++i) { + QString line = lines[i].trimmed(); + if (line.isEmpty() || line.startsWith("#")) { + continue; // 跳过空行和注释行 + } + + if (!line.contains(ffKeyPixels)) { + continue; // 不包含 ffKeyPixels 的行 + } + + if (line == target) { + return true; // 找到目标行,无需修改 + } + + QString tmp = QString("%1, \"%2\");").arg(ffKeyPixels.split(",")[0]).arg(QString::number(value, 'f', 2)); + lines[i] = tmp; // 更新行内容 + found = true; + break; // 找到并更新后退出 + } + if (!found) { + if (value == -1) { + return false; + } + QString tmp = lines[lines.length() - 1]; + lines[lines.length() - 1] = target; + lines.append(tmp); + } + file.seek(0); + file.resize(0); + QTextStream out(&file); + for (const QString &line : lines) { + out << line << "\n"; + } + file.close(); + return true; +} + +void XSettingsManager::updateFirefoxDPI() +{ + QString homeDir = Utils::getUserHomeDir(); + if (homeDir == "") { + return; + } + QDir dir(homeDir); + QString ffDir = dir.filePath(".mozilla/firefox"); + double scale = getScaleFactor(); + if (scale <= 0) { + scale = -1; + } + auto configs = getFirefoxConfigs(ffDir); + if (configs.empty()) { + return; + } + for (auto config : configs) { + if (!setFirefoxDPI(scale, config, config)) { + qWarning() << "failed to set firefox dpi"; + } + } +} + +XsValue XSettingsManager::getSettingValue(QString prop) +{ + QByteArray datas = getSettingPropValue(); + XSDataInfo xsInfo(datas); + + QSharedPointer item = xsInfo.getPropItem(prop); + if (item.isNull()) { + qDebug() << "get item null:" << prop; + return XsValue(); + } + return item->getValue(); +} + +void XSettingsManager::setSettings(QVector settings) +{ + QByteArray datas = getSettingPropValue(); + XSDataInfo xsInfo(datas); + xsInfo.increaseSerial(); + + for (auto xsettingItem : settings) { + QSharedPointer xsItem = xsInfo.getPropItem(xsettingItem.prop); + if (xsItem) { + qDebug() << "setSettings modify:" << xsItem->getHeadName(); + xsItem->modifyProperty(xsettingItem); + continue; + } + + QSharedPointer xSItemInfo(new XSItemInfo(xsettingItem.prop, xsettingItem.value)); + xsInfo.inserItem(xSItemInfo); + xsInfo.increaseNumSettings(); + } + + QByteArray value = xsInfo.marshalSettingData(); + m_xcbUtils.changeSettingProp(value); +} + +QVector XSettingsManager::getSettingsInSchema() +{ + QVector xsSettingVec; + if (!m_settingDconfig->isValid()) { + return xsSettingVec; + } + + QStringList keys = m_settingDconfig->keyList(); + for (auto key : keys) { + QSharedPointer dconfInfo = m_dconfInfos.getByDconfKey(key); + if (dconfInfo.isNull()) { + qWarning() << "dconfigInfo is nullptr:" << key; + continue; + } + + XsValue value = dconfInfo->getValue(*m_settingDconfig); + if (!Utils::hasXsValue(value)) { + qWarning() << "unknown Xs type:" << dconfInfo->getDconfKey() << dconfInfo->getKeySType(); + continue; + } + + XsSetting xsStting; + xsStting.type = dconfInfo->getKeySType(); + xsStting.prop = dconfInfo->getXsetKey(); + xsStting.value = value; + xsSettingVec.push_back(xsStting); + } + return xsSettingVec; +} + +ScaleFactors XSettingsManager::parseScreenFactors(QString screenFactors) +{ + ScaleFactors factorMap; + + QStringList factors = screenFactors.split(";"); + for (auto factor : factors) { + QStringList kv = factor.split("="); + if (kv.size() != 2) { + continue; + } + bool bOk = false; + double value = kv[1].toDouble(&bOk); + if (bOk) { + factorMap[kv[0]] = value; + } + } + + return factorMap; +} + +void XSettingsManager::setGSettingsByXProp(const QString &prop, XsValue value) +{ + QSharedPointer info = m_dconfInfos.getByXSKey(prop); + if (info.isNull()) { + return; + } + info->setValue(*m_settingDconfig, value); +} + +void XSettingsManager::setSingleScaleFactor(double scale, bool emitSignal) +{ + int cursorSize = static_cast(BASE_CURSORSIZE * scale); + int windowScale = qFloor((scale + 0.3) * 10) / 10; + if (windowScale < 1) { + windowScale = 1; + } + + if (m_settingDconfig->isValid()) { + m_settingDconfig->setValue(dcKeyScaleFactor, scale); + + int oldWindowScale = m_settingDconfig->value(dcKeyWindowScale).toInt(); + if (windowScale != oldWindowScale) { + m_settingDconfig->setValue(dcKeyWindowScale, windowScale); + } + + m_settingDconfig->setValue(dcKeyGtkCursorThemeSize, cursorSize); + } + + setScaleFactorForPlymouth(windowScale, emitSignal); +} + +void XSettingsManager::setScaleFactorForPlymouth(int factor, bool emitSignal) +{ + if (factor > 2) { + factor = 2; + } + + QString theme = getPlymouthTheme(PLYMOUTH_CONFIGFILE); + int currentFactor = getPlymouthThemeScaleFactor(theme); + if (currentFactor == factor) { + emitSignalSetScaleFactor(true, emitSignal); + return; + } + + m_sysDaemonInterface->call("ScalePlymouth", static_cast(factor)); + emitSignalSetScaleFactor(true, emitSignal); +} + +QString XSettingsManager::getPlymouthTheme(QString file) +{ + KeyFile keyFile; + bool bSuccess = keyFile.loadFile(file); + if (!bSuccess) { + return ""; + } + + return keyFile.getStr("Daemon", "Theme"); +} + +int XSettingsManager::getPlymouthThemeScaleFactor(QString theme) +{ + if (theme == "deepin-logo" || theme == "deepin-ssd-logo" || theme == "uos-ssd-logo") { + return 1; + } else if (theme == "deepin-hidpi-logo" || theme == "deepin-hidpi-ssd-logo" || theme == "uos-hidpi-ssd-logo") { + return 2; + } else { + return 0; + } +} + +QString XSettingsManager::joinScreenScaleFactors(const ScaleFactors &factors) +{ + QString value; + + for (auto key : factors.keys()) { + value = QString::asprintf("%s=%.2f;", key.toStdString().c_str(), factors[key]); + } + + return value; +} + +void XSettingsManager::setScreenScaleFactorsForQt(const ScaleFactors &factors) +{ + if (factors.isEmpty()) { + return; + } + + QString fileName = Utils::GetUserConfigDir() + "/deepin/qt-theme.ini"; + KeyFile keyFile; + if (!keyFile.loadFile(fileName)) { + qWarning() << "failed to load qt-theme.ini:"; + } + QString value; + if (factors.size() == 1) { + value = QString::number(factors.first()); + } else { + value = joinScreenScaleFactors(factors); + } + keyFile.setKey(qtThemeSection, qtThemeKeyScreenScaleFactors, value); + keyFile.deleteKey(qtThemeSection, qtThemeKeyScaleFactor); + keyFile.setKey(qtThemeSection, qtThemeKeyScaleLogicalDpi, "-1,-1"); + + // 当文件路径不存在时创建 + QFile qfile(fileName); + if (!qfile.exists()) { + QDir dir(fileName.left(fileName.lastIndexOf("/"))); + dir.mkpath(fileName.left(fileName.lastIndexOf("/"))); + qInfo() << "mkpath" << fileName; + } + + qfile.open(QIODevice::WriteOnly); + qfile.close(); + // keyFile.print(); + keyFile.saveToFile(fileName); + + updateGreeterQtTheme(keyFile); + + cleanUpDdeEnv(); +} + +void XSettingsManager::updateGreeterQtTheme(KeyFile &keyFile) +{ + QFile file("/tmp/startdde-qt-theme-"); + if (!file.open(QIODevice::ReadWrite)) { + qWarning() << "open /tmp/startdde-qt-theme- failed"; + return; + } + keyFile.setKey(qtThemeSection, qtThemeKeyScaleLogicalDpi, "96,96"); + keyFile.saveToFile(file.fileName()); + if (!file.seek(0)) { + qWarning() << "file reset failed"; + return; + } + QDBusUnixFileDescriptor dbusFd(file.handle()); + QDBusMessage reply = m_greeterInterface->call("UpdateGreeterQtTheme", QVariant::fromValue(dbusFd)); + if (reply.type() == QDBusMessage::ErrorMessage) { + qWarning() << "update greeter qt-theme failed:" << reply.errorMessage(); + } + file.close(); + file.remove(); +} + +void XSettingsManager::cleanUpDdeEnv() +{ + bool bNeedSave = false; + QMap envMap = loadDDEUserEnv(); + const QStringList keyEnvs{ "QT_SCALE_FACTOR", "QT_SCREEN_SCALE_FACTORS", "QT_AUTO_SCREEN_SCALE_FACTOR", "QT_FONT_DPI", "DEEPIN_WINE_SCALE" }; + + for (auto env : keyEnvs) { + if (envMap.find(env) != envMap.end()) { + bNeedSave = true; + envMap.erase(envMap.find(env)); + } + } + + if (bNeedSave) { + saveDDEUserEnv(envMap); + } +} + +QMap XSettingsManager::loadDDEUserEnv() +{ + QMap result; + + QString envFile = Utils::getUserHomeDir() + "/.dde_env"; + QFile file(envFile); + if (!file.open(QIODevice::ReadOnly)) { + return result; + } + + while (!file.atEnd()) { + QString line = file.readLine(); + if (line.front() == '#') { + continue; + } + QStringList match = line.split(' '); + if (match.length() == 3) { + result[match[1]] = match[2]; + } + } + + return result; +} + +void XSettingsManager::saveDDEUserEnv(const QMap &userEnvs) +{ + QString envFile = Utils::getUserHomeDir() + "/.dde_env"; + QFile file(envFile); + if (!file.open(QFile::WriteOnly | QFile::Truncate)) { + return; + } + + QString text = QString::asprintf("# DDE user env file, bash script\n"); + for (auto key : userEnvs.keys()) { + text += QString::asprintf("export %s=%s;\n", key.toStdString().c_str(), userEnvs[key].toStdString().c_str()); + } + + file.write(text.toLatin1(), text.length()); + file.close(); +} diff --git a/src/plugin-qt/xsettings/impl/xsettingsmanager.h b/src/plugin-qt/xsettings/impl/xsettingsmanager.h new file mode 100644 index 00000000..c62910f3 --- /dev/null +++ b/src/plugin-qt/xsettings/impl/xsettingsmanager.h @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef XSETTINGSMANAGER_H +#define XSETTINGSMANAGER_H + +#include "dconfinfos.h" +#include "modules/api/keyfile.h" +#include "modules/api/xcbutils.h" +#include "modules/common/common.h" +#include "types/arrayOfColor.h" +#include "types/scaleFactors.h" + +#include + +#include +#include +#include +#include + +class XSettingsManager : public QObject +{ + Q_OBJECT +public: + XSettingsManager(QObject *parent = nullptr); + ArrayOfColor getColor(const QString &prop); + int getInteger(const QString &prop); + double getScaleFactor(); + ScaleFactors getScreenScaleFactors(); + QString getString(const QString &prop); + QString listProps(); + void setColor(const QString &prop, const ArrayOfColor &v); + void setInteger(const QString &prop, const int &v); + void setScreenScaleFactors(const ScaleFactors &factors, bool emitSignal); + void setString(const QString &prop, const QString &v); + +Q_SIGNALS: // SIGNALS + void SetScaleFactorDone(); + void SetScaleFactorStarted(); + +protected Q_SLOTS: + void handleDConfigChangedCb(const QString &key); + +private: + void updateDPI(); + void updateXResources(); + void updateFirefoxDPI(); + XsValue getSettingValue(QString prop); + void setSettings(QVector settings); + QVector getSettingsInSchema(); + ScaleFactors parseScreenFactors(QString screenFactors); + void setGSettingsByXProp(const QString &prop, XsValue value); + void setSingleScaleFactor(double scale, bool emitSignal); + void setScaleFactorForPlymouth(int factor, bool emitSignal); + QString getPlymouthTheme(QString file); + int getPlymouthThemeScaleFactor(QString theme); + QString joinScreenScaleFactors(const ScaleFactors &factors); + void setScreenScaleFactorsForQt(const ScaleFactors &factors); + void updateGreeterQtTheme(KeyFile &keyFile); + void cleanUpDdeEnv(); + QMap loadDDEUserEnv(); + void saveDDEUserEnv(const QMap &userEnvs); + QByteArray getSettingPropValue(); + void emitSignalSetScaleFactor(bool done, bool emitSignal); + +private: + DTK_CORE_NAMESPACE::DConfig *m_settingDconfig; + QSharedPointer m_greeterInterface; + QSharedPointer m_sysDaemonInterface; + // QVector m_plymouthScalingTasks; // + // bool m_plymouthScaling; // + // bool m_restartOSD; // + XcbUtils &m_xcbUtils; + DconfInfos m_dconfInfos; +}; + +#endif // XSETTINGSMANAGER_H diff --git a/src/plugin-qt/xsettings/misc/org.deepin.XSettings.json b/src/plugin-qt/xsettings/misc/org.deepin.XSettings.json new file mode 100644 index 00000000..46f8569a --- /dev/null +++ b/src/plugin-qt/xsettings/misc/org.deepin.XSettings.json @@ -0,0 +1,352 @@ +{ + "magic": "dsg.config.meta", + "version": "1.0", + "contents": { + "theme-name": { + "value": "deepin", + "serial": 0, + "flags": [], + "name": "System theme", + "name[zh_CN]": "系统主题", + "description": "Theme to use for gtk+ and qt.", + "permissions": "readwrite", + "visibility": "private" + }, + "icon-theme-name": { + "value": "bloom", + "serial": 0, + "flags": [], + "name": "Icon theme", + "name[zh_CN]": "图标主题", + "description": "Icon theme to use for the panel, nautilus etc", + "permissions": "readwrite", + "visibility": "private" + }, + "icon-theme-fallback": { + "value": "default", + "serial": 0, + "flags": [], + "name": "Fallback icon theme", + "description": "Icon theme", + "permissions": "readwrite", + "visibility": "private" + }, + "sound-theme-name": { + "value": "deepin", + "serial": 0, + "flags": [], + "name": "System sound theme", + "name[zh_CN]": "系统音效", + "description": "Set the system sound theme", + "permissions": "readwrite", + "visibility": "private" + }, + "gtk-theme-name": { + "value": "deepin", + "serial": 0, + "flags": [], + "name": "Gtk+ theme", + "name[zh_CN]": "Gtk 主题", + "description": "Basename of the default theme used by gtk+.", + "permissions": "readwrite", + "visibility": "private" + }, + "gtk-cursor-theme-name": { + "value": "bloom", + "serial": 0, + "flags": [], + "name": "Cursor theme", + "name[zh_CN]": "光标主题", + "description": "Cursor theme name. Used only by Xservers that support the Xcursor extension.", + "permissions": "readwrite", + "visibility": "private" + }, + "gtk-font-name": { + "value": "sans-serif 10.5", + "serial": 0, + "flags": [], + "name": "Default font", + "name[zh_CN]": "默认字体", + "description": "Name of the default font used by gtk+.", + "permissions": "readwrite", + "visibility": "private" + }, + "gtk-key-theme-name": { + "value": "Default", + "serial": 0, + "flags": [], + "name": "Gtk+ Keybinding Theme", + "name[zh_CN]": "热键", + "description": "Basename of the default keybinding theme used by gtk+.", + "permissions": "readwrite", + "visibility": "private" + }, + "xft-hintstyle": { + "value": "hintfull", + "serial": 0, + "flags": [], + "name": "Xft hintstyle", + "description": "What degree of hinting to use; hintnone, hintslight, hintmedium, or hintfull.", + "permissions": "readwrite", + "visibility": "private" + }, + "xft-rgba": { + "value": "rgb", + "serial": 0, + "flags": [], + "name": "Xft RGBA", + "description": "Type of subpixel antialiasing; none, rgb, bgr, vrgb, vbgr.", + "permissions": "readwrite", + "visibility": "private" + }, + "cursor-blink-time": { + "value": 1200, + "serial": 0, + "flags": [], + "name": "Cursor Blink Time", + "name[zh_CN]": "光标闪烁间隔", + "description": "Length of the cursor blink cycle, in milliseconds.", + "permissions": "readwrite", + "visibility": "private" + }, + "gtk-cursor-blink-timeout": { + "value": 10, + "serial": 0, + "flags": [], + "name": "Cursor Blink Timeout", + "description": "Time after which the cursor stops blinking, in seconds.", + "permissions": "readwrite", + "visibility": "private" + }, + "double-click-time": { + "value": 250, + "serial": 0, + "flags": [], + "name": "Double click time", + "name[zh_CN]": "双击响应时间", + "description": "Maximum time allowed between two clicks for them to be considered a double click (in milliseconds).", + "permissions": "readwrite", + "visibility": "private" + }, + "double-click-distance": { + "value": 5, + "serial": 0, + "flags": [], + "name": "Double click distance", + "description": "Maximum distance allowed between two clicks for them to be considered a double click (in pixels).", + "permissions": "readwrite", + "visibility": "private" + }, + "dnd-drag-threshold": { + "value": 8, + "serial": 0, + "flags": [], + "name": "Dnd drag threshold", + "description": "A palette of named colors for use in themes.", + "permissions": "readwrite", + "visibility": "private" + }, + "gtk-cursor-theme-size": { + "value": 24, + "serial": 0, + "flags": [], + "name": "Timeout before click repeat", + "description": "Timeout in milliseconds before a click starts repeating (onspinner buttons for example).", + "permissions": "readwrite", + "visibility": "private" + }, + "gtk-recent-files-max-age": { + "value": 30, + "serial": 0, + "flags": [], + "name": "Gtk Recent Files Max Age", + "description": "The maximum age, in days, of the items inside the recently used resources list.\n Items older than this setting will be excised from the list.\n If set to 0, the list will always be empty; if set to -1, no item will be removed.", + "permissions": "readwrite", + "visibility": "private" + }, + "xft-dpi": { + "value": -1, + "serial": 0, + "flags": [], + "name": "Xft DPI", + "description": "Resolution for Xft, in 1024 * dots/inch. -1 to use default value.\n Do not modify it manually.", + "permissions": "readwrite", + "visibility": "private" + }, + "cursor-blink": { + "value": true, + "serial": 0, + "flags": [], + "name": "Cursor Blink", + "description": "Whether the cursor should blink.", + "permissions": "readwrite", + "visibility": "private" + }, + "enable-event-sounds": { + "value": true, + "serial": 0, + "flags": [], + "name": "Enable sound event", + "description": "Whether to play any event sounds at all.", + "permissions": "readwrite", + "visibility": "private" + }, + "enable-input-feedback-sounds": { + "value": true, + "serial": 0, + "flags": [], + "name": "Enable input feedback sounds", + "description": "Whether to play event sounds as feedback to user input.", + "permissions": "readwrite", + "visibility": "private" + }, + "gtk-recent-files-enabled": { + "value": true, + "serial": 0, + "flags": [], + "name": "Recent files enabled", + "description": "Whether GTK+ should keep track of items inside the recently used resources list.", + "permissions": "readwrite", + "visibility": "private" + }, + "xft-antialias": { + "value": true, + "serial": 0, + "flags": [], + "name": "Xft antialias", + "description": "Whether to antialias Xft fonts; 0=no, 1=yes, -1=default.", + "permissions": "readwrite", + "visibility": "private" + }, + "xft-hinting": { + "value": true, + "serial": 0, + "flags": [], + "name": "Xft hinting", + "description": "Whether to hint Xft fonts; 0=no, 1=yes, -1=default.", + "permissions": "readwrite", + "visibility": "private" + }, + "scale-factor": { + "value": 0, + "serial": 0, + "flags": [], + "name": "Text Scale Factor", + "description": "Text scale for Gdk.", + "permissions": "readwrite", + "visibility": "private" + }, + "window-scale": { + "value": 1, + "serial": 0, + "flags": [], + "name": "Gdk Window Scale Factor", + "description": "Window scale for Gdk.", + "permissions": "readwrite", + "visibility": "private" + }, + "individual-scaling": { + "value": "", + "serial": 0, + "flags": [ + "global" + ], + "name[zh_CN]": "屏幕缩放", + "name": "Scaling factors for individual screen", + "description": "Semicolon-separated list of name=value pairs, as used in QT_SCREEN_SCALE_FACTORS.", + "description[zh_CN]": "屏幕缩放,指定不同屏幕的缩放比例", + "permissions": "readwrite", + "visibility": "private" + }, + "qt-active-color": { + "value": "0,33153,65535,65535", + "serial": 0, + "flags": [], + "name": "Qt Active Color", + "description": "Qt Active Color", + "permissions": "readwrite", + "visibility": "private" + }, + "qt-dark-active-color": { + "value": "0,33153,65535,65535", + "serial": 0, + "flags": [], + "name": "Qt Dark Active Color", + "description": "Qt Active Color In Dark Theme", + "permissions": "readwrite", + "visibility": "private" + }, + "qt-scrollbar-policy": { + "value": 0, + "serial": 0, + "flags": [], + "name": "Qt Scrollbar Policy", + "description": "Qt Scrollbar Policy", + "permissions": "readwrite", + "visibility": "private" + }, + "dtk-size-mode": { + "value": 0, + "serial": 0, + "flags": [], + "name": "Dtk Size Mode", + "description": "Dtk Size Mode", + "permissions": "readwrite", + "visibility": "private" + }, + "qt-font-name": { + "value": "", + "serial": 0, + "flags": [], + "name": "Qt Font Name", + "description": "Qt Font Name", + "permissions": "readwrite", + "visibility": "private" + }, + "qt-mono-font-name": { + "value": "", + "serial": 0, + "flags": [], + "name": "Qt Mono Font Name", + "description": "Qt Mono Font Name", + "permissions": "readwrite", + "visibility": "private" + }, + "qt-font-point-size": { + "value": 10.5, + "serial": 0, + "flags": [], + "name": "Qt Font Point Size", + "description": "Qt Font Point Size", + "permissions": "readwrite", + "visibility": "private" + }, + "dtk-window-radius": { + "value": 8, + "serial": 0, + "flags": [], + "name": "Dtk Window Radius", + "description": "Dtk Window Radius", + "permissions": "readwrite", + "visibility": "private" + }, + "primary-monitor-name": { + "value": "eDP-1", + "serial": 0, + "flags": [], + "name": "Primary Monitor Name", + "description": "Primary Monitor Name", + "permissions": "readwrite", + "visibility": "private" + }, + "primary-monitor-rect": { + "value": "0-0-0-0", + "serial": 0, + "flags": [], + "name": "Primary Monitor Rect", + "description": "x,y,width,height of screen geometry", + "permissions": "readwrite", + "visibility": "private" + } + } +} \ No newline at end of file diff --git a/src/plugin-qt/xsettings/misc/org.deepin.dde.XSettings.service b/src/plugin-qt/xsettings/misc/org.deepin.dde.XSettings.service new file mode 100644 index 00000000..c035c607 --- /dev/null +++ b/src/plugin-qt/xsettings/misc/org.deepin.dde.XSettings.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=org.deepin.dde.XSettings1 +Exec=/usr/bin/deepin-service-manager -n org.deepin.dde.XSettings1 +SystemdService=deepin-service-plugin@org.deepin.dde.XSettings1.service diff --git a/src/plugin-qt/xsettings/misc/plugin-dde-xsettings.json b/src/plugin-qt/xsettings/misc/plugin-dde-xsettings.json new file mode 100644 index 00000000..6be826c6 --- /dev/null +++ b/src/plugin-qt/xsettings/misc/plugin-dde-xsettings.json @@ -0,0 +1,13 @@ +{ + "name": "org.deepin.dde.XSettings1", + "libPath": "libplugin-dde-xsettings.so", + "group": "org.deepin.dde.XSettings1", + "policyStartType": "OnDemand", + "pluginType": "qt", + "startDelay": 0, + "policy": [ + { + "path": "/org/deepin/dde/XSettings1" + } + ] +} diff --git a/src/plugin-qt/xsettings/modules/api/keyfile.cpp b/src/plugin-qt/xsettings/modules/api/keyfile.cpp new file mode 100644 index 00000000..e8e676cd --- /dev/null +++ b/src/plugin-qt/xsettings/modules/api/keyfile.cpp @@ -0,0 +1,187 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "keyfile.h" + +#include +#include + +KeyFile::KeyFile(char separtor) + : modified(false) + , listSeparator(separtor) +{ +} + +KeyFile::~KeyFile() +{ + if (fp.isOpen()) { + fp.close(); + } +} + +bool KeyFile::getBool(const QString §ion, const QString &key, bool defaultValue) +{ + if (mainKeyMap.find(section) == mainKeyMap.end()) + return false; + + QString valueStr = mainKeyMap[section][key]; + bool value = defaultValue; + if (valueStr == "true") + value = true; + else if (valueStr == "false") + value = false; + + return value; +} + +QString KeyFile::getStr(const QString §ion, const QString &key, QString defaultValue) +{ + if (mainKeyMap.find(section) == mainKeyMap.end()) + return defaultValue; + + QString valueStr = mainKeyMap[section][key]; + if (valueStr.isEmpty()) + valueStr = defaultValue; + + return valueStr; +} + +bool KeyFile::containKey(const QString §ion, const QString &key) +{ + if (mainKeyMap.find(section) == mainKeyMap.end()) + return false; + + return mainKeyMap[section].find(key) != mainKeyMap[section].end(); +} + +QStringList KeyFile::getStrList(const QString §ion, const QString &key) +{ + QString value = getStr(section, key); + + return value.split(listSeparator); +} + +// 修改keyfile内容 +void KeyFile::setKey(const QString §ion, const QString &key, const QString &value) +{ + if (mainKeyMap.find(section) == mainKeyMap.end()) { + mainKeyMap.insert(section, KeyMap()); + } + + mainKeyMap[section].insert(key, value); +} + +bool KeyFile::deleteKey(const QString §ion, const QString &key) +{ + if (mainKeyMap.find(section) == mainKeyMap.end()) { + return false; + } + mainKeyMap[section].remove(key); + return false; +} + +// 写入文件 +bool KeyFile::saveToFile(const QString &filePath) +{ + qDebug() << filePath; + QFile file(filePath); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + return false; + } + + for (const auto &im : mainKeyMap.toStdMap()) { + const auto &keyMap = im.second; + QString section = "[" + im.first + "]\n"; + qDebug() << section; + file.write(section.toLatin1()); + for (const auto &ik : keyMap.toStdMap()) { + QString kv = ik.first + "=" + ik.second + "\n"; + qDebug() << kv; + file.write(kv.toLatin1()); + } + } + + file.close(); + return true; +} + +bool KeyFile::loadFile(const QString &filePath) +{ + mainKeyMap.clear(); + if (fp.isOpen()) { + fp.close(); + } + + QString lastSection; + fp.setFileName(filePath); + + if (!fp.open(QIODevice::ReadOnly)) { + return false; + } + + QString line; + while (!fp.atEnd()) { + line = fp.readLine(); + // 移除行首空行 + line.replace(QRegularExpression("^ +"), ""); + if (line.front() == '#') { + continue; + } + + line.replace(QRegularExpression("\\t$"), ""); + line.replace(QRegularExpression("\\r$"), ""); + line.replace(QRegularExpression("\\n$"), ""); + + int lPos = line.indexOf('['); + int rPos = line.indexOf(']'); + if (lPos != -1 && rPos != -1 && rPos > lPos && lPos == 0 && rPos + 1 == line.size()) { + // 主键 + QString section = line.mid(lPos + 1, line.size() - 2); + mainKeyMap.insert(section, KeyMap()); + lastSection = section; + } else { + int index = line.indexOf('='); + if (index == -1) { + continue; + } + + // 文件格式错误 + if (lastSection.isEmpty()) { + return false; + } + + // 子键 + QString key = line.mid(0, index); + QString value = line.mid(index + 1, line.length() - index - 1); + if (mainKeyMap.count(lastSection) == 1) { + mainKeyMap[lastSection][key] = value; + } + } + } + fp.close(); + + return true; +} + +QStringList KeyFile::getMainKeys() +{ + QStringList mainKeys; + for (const auto &iter : mainKeyMap.toStdMap()) + mainKeys.push_back(iter.first); + + return mainKeys; +} + +void KeyFile::print() +{ + qDebug() << "sectionMap: "; + for (auto sectionMap : mainKeyMap.toStdMap()) { + + KeyMap keyMap = sectionMap.second; + qDebug() << sectionMap.first; + for (auto iter : keyMap.toStdMap()) { + qDebug() << iter.first << "=" << iter.second; + } + } + qDebug() << "sectionMap: end"; +} diff --git a/src/plugin-qt/xsettings/modules/api/keyfile.h b/src/plugin-qt/xsettings/modules/api/keyfile.h new file mode 100644 index 00000000..e179968a --- /dev/null +++ b/src/plugin-qt/xsettings/modules/api/keyfile.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef KEYFILE_H +#define KEYFILE_H + +#include +#include +#include + +typedef QMap KeyMap; +typedef QMap MainKeyMap; + +// 解析ini、desktop文件类 +class KeyFile +{ +public: + explicit KeyFile(char separtor = ';'); + ~KeyFile(); + + bool getBool(const QString §ion, const QString &key, bool defaultValue = false); + + QString getStr(const QString §ion, const QString &key, QString defaultValue = ""); + bool containKey(const QString §ion, const QString &key); + QStringList getStrList(const QString §ion, const QString &key); + + void setKey(const QString §ion, const QString &key, const QString &value); + bool deleteKey(const QString §ion, const QString &key); + bool saveToFile(const QString &filePath); + bool loadFile(const QString &filePath); + QStringList getMainKeys(); + + // for test + void print(); + +private: + MainKeyMap mainKeyMap; // section -> key : value + QString filePath; + QFile fp; + bool modified; + char listSeparator; +}; + +#endif // KEYFILE_H diff --git a/src/plugin-qt/xsettings/modules/api/utils.cpp b/src/plugin-qt/xsettings/modules/api/utils.cpp new file mode 100644 index 00000000..cb4d77ca --- /dev/null +++ b/src/plugin-qt/xsettings/modules/api/utils.cpp @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "utils.h" + +#include +#include + +#include +#include + +Utils::Utils() { } + +bool Utils::readString(QByteArray &array, QString &value, int length) +{ + if (array.length() < length) { + return false; + } + + value = array.left(length); + + array.remove(0, length); + + return true; +} + +bool Utils::readSkip(QByteArray &array, int length) +{ + if (array.length() < length) { + return false; + } + + array.remove(0, length); + + return true; +} + +bool Utils::writeString(QByteArray &array, const QByteArray &value) +{ + array.append(value); + return true; +} + +bool Utils::writeSkip(QByteArray &array, int length) +{ + for (int i = 0; i < length; i++) { + array.push_back('\0'); + } + return true; +} + +int Utils::getPad(int e) +{ + return (4 - (e % 4)) % 4; +} + +QString Utils::GetUserConfigDir() +{ + return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); +} + +QString Utils::getUserHomeDir() +{ + return QStandardPaths::writableLocation(QStandardPaths::HomeLocation); +} + +bool Utils::hasXsValue(const XsValue &value) +{ + if (!std::get_if(&value) && !std::get_if(&value) && !std::get_if(&value) && !std::get_if(&value)) { + return false; + } + + return true; +} diff --git a/src/plugin-qt/xsettings/modules/api/utils.h b/src/plugin-qt/xsettings/modules/api/utils.h new file mode 100644 index 00000000..3528a6e9 --- /dev/null +++ b/src/plugin-qt/xsettings/modules/api/utils.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef UTILS_H +#define UTILS_H + +#include "../common/common.h" + +#include +#include +#include +#include + +class Utils +{ +public: + Utils(); + + template + static bool readInteger(QByteArray &array, Value &value) + { + int size = sizeof(Value); + if (array.size() < size) { + return false; + } + // 使用小端格式 + value = qFromLittleEndian(array.data()); + array = array.remove(0, size); + + return true; + } + + template + static bool writeInteger(QByteArray &array, const Value &value) + { + int size = sizeof(Value); + char tmp[size]; + qToLittleEndian(value, tmp); + array.append(tmp, size); + return true; + } + + static bool readString(QByteArray &array, QString &value, int length); + static bool readSkip(QByteArray &array, int length); + static bool writeString(QByteArray &array, const QByteArray &value); + static bool writeSkip(QByteArray &array, int length); + static int getPad(int e); + static QString GetUserConfigDir(); + static QString getUserHomeDir(); + static bool hasXsValue(const XsValue &value); +}; + +#endif // UTILS_H diff --git a/src/plugin-qt/xsettings/modules/api/xcbutils.cpp b/src/plugin-qt/xsettings/modules/api/xcbutils.cpp new file mode 100644 index 00000000..8213ad10 --- /dev/null +++ b/src/plugin-qt/xsettings/modules/api/xcbutils.cpp @@ -0,0 +1,345 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "xcbutils.h" + +#include +#include +#include +#include + +#include +#include +#include + +bool xcb_debug = 1; + +XcbUtils &XcbUtils::getInstance() +{ + static XcbUtils xcbUtils; + return xcbUtils; +} + +XcbUtils::XcbUtils(QObject *parent) + : QObject(parent) +{ + connection = xcb_connect(nullptr, nullptr); + if (!connection) { + qWarning() << "xcb connect failed"; + return; + } + window = createWindows(); + if (window == XCB_WINDOW_NONE) { + qWarning() << "xcb invalid window"; + return; + } + if (!isSelectionOwned("_XSETTINGS_S0")) { + qWarning() << "owned _XSETTINGS_S0 failed"; + return; + } +} + +xcb_window_t XcbUtils::createWindows() +{ + xcb_void_cookie_t cookie; + xcb_generic_error_t *error; + /* Get the first screen */ + const xcb_setup_t *setup = xcb_get_setup(connection); + xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup); + xcb_screen_t *screen = iter.data; + + /* Create the window */ + xcb_window_t window = xcb_generate_id(connection); + if (window != XCB_WINDOW_NONE) { + qDebug() << "xcb generate window:" << window; + } + xcb_create_window(connection, /* Connection */ + XCB_COPY_FROM_PARENT, /* depth (same as root)*/ + window, /* window Id */ + screen->root, /* parent window */ + 0, + 0, /* x, y */ + 1, + 1, /* width, height */ + 0, /* border_width */ + XCB_WINDOW_CLASS_INPUT_ONLY, /* class */ + screen->root_visual, /* visual */ + 0, + nullptr); /* masks, not used yet */ + + xcb_flush(connection); + xcb_atom_t atom = getAtom("_XSETTINGS_S0"); + if (atom == XCB_ATOM_NONE) { + qWarning() << "invalid atom"; + return XCB_WINDOW_NONE; + } + + changeWindowPid(window); + + cookie = xcb_set_selection_owner_checked(connection, window, atom, XCB_CURRENT_TIME); + // 检查错误 + error = xcb_request_check(connection, cookie); + if (error) { + qWarning() << "xcb set selection owner failed: " << error->error_code; + return XCB_WINDOW_NONE; + } + return window; +} + +xcb_atom_t XcbUtils::getAtom(const char *name, bool exist) +{ + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connection, 0, strlen(name), name); + + xcb_generic_error_t *err = nullptr; + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, cookie, &err); + if (!reply) { + if (err != nullptr) { + fprintf(stderr, "get atom failed, errcode:%d\n", err->error_code); + } else { + fprintf(stderr, "get atom failed, unknown err.\n"); + } + return 0; + } + + xcb_atom_t atom = reply->atom; + free(reply); + if (xcb_debug) { + qDebug() << "xcb get atom [" << name << "]: " << atom; + } + return atom; +} + +bool XcbUtils::changeWindowPid(xcb_window_t window) +{ + pid_t pid = getpid(); + + xcb_atom_t atom = getAtom("_NET_WM_PID"); + xcb_atom_t cardinal_atom = getAtom("CARDINAL"); + QByteArray pidArray; + pidArray.push_back(char(pid)); + pidArray.push_back(char(pid >> 8)); + pidArray.push_back(char(pid >> 16)); + pidArray.push_back(char(pid >> 24)); + int32_t data[4]; + data[0] = int32_t(0xff & pid); + data[1] = int32_t(0xff & (pid >> 8)); + data[2] = int32_t(0xff & (pid >> 16)); + data[3] = int32_t(0xff & (pid >> 24)); + + // xcb_void_cookie_t cookie = xcb_change_property_checked(connection,XCB_PROP_MODE_REPLACE,window,atom,XCB_ATOM_CARDINAL,32, + // 4, pidArray.data()); + xcb_void_cookie_t cookie = xcb_change_property_checked(connection, XCB_PROP_MODE_REPLACE, window, atom, cardinal_atom, 32, 1, &pid); + // 检查错误 + xcb_generic_error_t *error = xcb_request_check(connection, cookie); + if (error) { + qWarning() << "xcb change property failed:" << error->error_code; + return false; + } + + return true; +} + +bool XcbUtils::isSelectionOwned(QString prop) +{ + xcb_atom_t atom = getAtom(prop.toLatin1().data()); + + xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(connection, atom); + + xcb_generic_error_t *err = nullptr; + xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(connection, cookie, &err); + if (err != nullptr || reply == nullptr) { + return false; + } + if (reply->owner == 0 || reply->owner != window) { + return false; + } + + return true; +} + +QByteArray XcbUtils::xcbPropertyReplyDataToArray(xcb_get_property_reply_t *reply) +{ + QByteArray arr; + int length = xcb_get_property_value_length(reply); + uint8_t *prop_data = (uint8_t *)xcb_get_property_value(reply); + for (int i = 0; i < length; i++) { + arr.push_back(prop_data[i]); + } + return arr; +} + +QString XcbUtils::getXcbAtomName(xcb_atom_t atom) +{ + xcb_get_atom_name_cookie_t cookie; + xcb_get_atom_name_reply_t *reply; + + cookie = xcb_get_atom_name(connection, atom); + reply = xcb_get_atom_name_reply(connection, cookie, NULL); + if (reply) { + return xcb_get_atom_name_name(reply); + } + return ""; +} + +// 获取 _XSETTINGS_SETTINGS 属性 +QByteArray XcbUtils::getXcbAtomProperty(xcb_atom_t atom) +{ + bool more = true; + int offset = 0; + xcb_get_property_cookie_t cookie; + xcb_get_property_reply_t *reply; + xcb_generic_error_t *error = nullptr; + QByteArray settings; + int i = 0; + while (more) { + i++; + cookie = xcb_get_property(connection, + 0, // 删除属性为 0 + window, // 根窗口 + atom, // 要获取的 Atom + XCB_ATOM_NONE, + offset / 4, // 从 0 偏移量开始 + UINT32_MAX // 获取尽可能多的数据 + ); + reply = xcb_get_property_reply(connection, cookie, &error); + // 在窗口无效时,应当认为此native settings未初始化完成 + if (error && error->error_code == 3) { + return settings; + } + + // 查询这个原子类型的名称 + xcb_get_atom_name_cookie_t type_name_cookie = xcb_get_atom_name(connection, reply->type); + xcb_get_atom_name_reply_t *type_name_reply = xcb_get_atom_name_reply(connection, type_name_cookie, NULL); + + if (type_name_reply) { + int name_len = xcb_get_atom_name_name_length(type_name_reply); + char *name = xcb_get_atom_name_name(type_name_reply); + qWarning("属性类型名称: %.*s", name_len, name); + free(type_name_reply); + } + void *value = xcb_get_property_value(reply); + int value_len = xcb_get_property_value_length(reply); + qWarning("属性值(字符串): %d |%s\n", value_len, (char *)value); + + const auto property_value_length = xcb_get_property_value_length(reply); + settings.append(static_cast(xcb_get_property_value(reply)), property_value_length); + QStringList hex; + for (int i = 0; i < property_value_length; ++i) { + hex.append(QString::number((uint8_t)settings[i])); + // hex<<(uint8_t)array.at(i); + } + offset += property_value_length; + more = reply->bytes_after != 0; + free(reply); + } + return settings; +} + +bool XcbUtils::changeSettingProp(QByteArray data) +{ + xcb_void_cookie_t cookie; + xcb_generic_error_t *err; + xcb_atom_t atom = getAtom("_XSETTINGS_SETTINGS"); + if (atom == 0) { + return false; + } + cookie = xcb_change_property_checked(connection, PropModeReplace, window, atom, atom, 8, data.length(), data); + err = xcb_request_check(connection, cookie); + if (err) { + qWarning() << "xcb change setting prop failed," << err->error_code; + return false; + } + return true; +} + +void XcbUtils::updateXResources(QVector> xresourceInfos) +{ + QString datas = getXResources(); + + if (datas.isEmpty()) { + xresourceInfos.push_back(qMakePair(QString("*customization"), QString("-color"))); + } else { + QVector> infos = XcbUtils::unmarshalXResources(datas); + + for (auto info : infos) { + bool bFound = false; + for (auto xresourceInfo : xresourceInfos) { + if (info.first == xresourceInfo.first) { + bFound = true; + break; + } + } + + if (!bFound) { + xresourceInfos.push_back(info); + } + } + } + + datas = XcbUtils::marshalXResources(xresourceInfos); + + XcbUtils::setXResources(const_cast(datas.toStdString().c_str()), datas.length()); +} + +char *XcbUtils::getXResources() +{ + Display *dpy = XOpenDisplay(nullptr); + if (dpy == nullptr) { + return nullptr; + } + + char *res = XResourceManagerString(dpy); + if (res == nullptr) { + XCloseDisplay(dpy); + return nullptr; + } + + char *ret = strdup(res); + XCloseDisplay(dpy); + return ret; +} + +int XcbUtils::setXResources(char *data, unsigned long length) +{ + Display *dpy = XOpenDisplay(nullptr); + if (dpy == nullptr) { + return -1; + } + + XChangeProperty(dpy, DefaultRootWindow(dpy), XA_RESOURCE_MANAGER, XA_STRING, 8, PropModeReplace, reinterpret_cast(data), length); + + XCloseDisplay(dpy); + return 0; +} + +QVector> XcbUtils::unmarshalXResources(const QString &datas) +{ + QVector> infos; + + QStringList lines = datas.split("\n"); + for (auto line : lines) { + if (line.isEmpty()) { + continue; + } + + QStringList arr = line.split(":\t"); + if (arr.size() != 2) { + continue; + } + + infos.push_back(qMakePair(arr[0], arr[1])); + } + + return infos; +} + +QString XcbUtils::marshalXResources(const QVector> &infos) +{ + QString data; + + for (auto &info : infos) { + data += info.first + ":\t" + info.second + "\n"; + } + + return data; +} diff --git a/src/plugin-qt/xsettings/modules/api/xcbutils.h b/src/plugin-qt/xsettings/modules/api/xcbutils.h new file mode 100644 index 00000000..ecf1d024 --- /dev/null +++ b/src/plugin-qt/xsettings/modules/api/xcbutils.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef XCBUTILS_H +#define XCBUTILS_H + +#include + +#include +#include +#include + +class XcbUtils : public QObject +{ + Q_OBJECT +public: + static XcbUtils &getInstance(); + xcb_window_t createWindows(); + xcb_atom_t getAtom(const char *name, bool exist = false); + bool changeWindowPid(xcb_window_t window); + bool isSelectionOwned(QString prop); + bool changeSettingProp(QByteArray data); + void updateXResources(QVector> xresourceInfos); + char *getXResources(); + int setXResources(char *data, unsigned long length); + QVector> unmarshalXResources(const QString &datas); + QString marshalXResources(const QVector> &infos); + QByteArray getXcbAtomProperty(xcb_atom_t atom); + QByteArray xcbPropertyReplyDataToArray(xcb_get_property_reply_t *reply); + QString getXcbAtomName(xcb_atom_t atom); + +private: + XcbUtils(QObject *parent = nullptr); + XcbUtils(const XcbUtils &) = delete; + XcbUtils &operator=(const XcbUtils &) = delete; + +private: + xcb_connection_t *connection; + xcb_window_t window; +}; + +#endif // XCBUTILS_H diff --git a/src/plugin-qt/xsettings/modules/common/common.h b/src/plugin-qt/xsettings/modules/common/common.h new file mode 100644 index 00000000..3aafddc5 --- /dev/null +++ b/src/plugin-qt/xsettings/modules/common/common.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef COMMON_H +#define COMMON_H +#include + +#include + +#define dcKeyScaleFactor "scale-factor" +#define dcKeyWindowScale "window-scale" +#define dcKeyXftDpi "xft-dpi" +#define dcKeyGtkCursorThemeSize "gtk-cursor-theme-size" +#define cKeyIndividualScaling "individual-scaling" +#define qtThemeSection "Theme" +#define qtThemeKeyScreenScaleFactors "ScreenScaleFactors" +#define qtThemeKeyScaleFactor "ScaleFactor" +#define qtThemeKeyScaleLogicalDpi "ScaleLogicalDpi" + +#define WRAPSCHEMA "com.deepin.wrap.gnome.desktop.interface" + +struct ItemHeader +{ + uint8_t type; + uint16_t length; + QString name; + uint32_t lastChangeSerial; +}; + +// struct ColorValueInfo{ +// uint16_t red; +// uint16_t green; +// uint16_t blue; +// uint16_t alpha; +// }; + +constexpr size_t colorSize = 4; +using ColorValueInfo = std::array; +using XsValue = std::variant; + +enum DconfValueType { typeOfBool = 1, typeOfInt, typeOfString, typeOfDoublue }; + +struct XsSetting +{ + int8_t type; + QString prop; + XsValue value; +}; + +const int8_t HeadTypeInvalid = -1; +const int8_t HeadTypeInteger = 0; +const int8_t HeadTypeString = 1; +const int8_t HeadTypeColor = 2; + +#endif // COMMON_H diff --git a/src/plugin-qt/xsettings/plugin.cpp b/src/plugin-qt/xsettings/plugin.cpp new file mode 100644 index 00000000..5d053924 --- /dev/null +++ b/src/plugin-qt/xsettings/plugin.cpp @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "impl/xsettings1.h" + +#include + +static XSettings1 *xSettings = nullptr; + +extern "C" int DSMRegister(const char *name, void *data) +{ + xSettings = new XSettings1(); + + QDBusConnection::RegisterOptions opts = QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals | QDBusConnection::ExportAllProperties; + QString path = name; + path = QString("/%1").arg(path.replace(".", "/")); + auto connection = reinterpret_cast(data); + connection->registerObject(path, xSettings, opts); + return 0; +} + +// 该函数用于资源释放 +// 非常驻插件必须实现该函数,以防内存泄漏 +extern "C" int DSMUnRegister(const char *name, void *data) +{ + (void)name; + (void)data; + xSettings->deleteLater(); + xSettings = nullptr; + return 0; +} diff --git a/src/plugin-qt/xsettings/types/arrayOfColor.cpp b/src/plugin-qt/xsettings/types/arrayOfColor.cpp new file mode 100644 index 00000000..7bdceecf --- /dev/null +++ b/src/plugin-qt/xsettings/types/arrayOfColor.cpp @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "arrayOfColor.h" + +void registerArrayOfColorMetaType() +{ + qRegisterMetaType("ArrayOfColor"); + qDBusRegisterMetaType(); +} diff --git a/src/plugin-qt/xsettings/types/arrayOfColor.h b/src/plugin-qt/xsettings/types/arrayOfColor.h new file mode 100644 index 00000000..2f50153d --- /dev/null +++ b/src/plugin-qt/xsettings/types/arrayOfColor.h @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef ARRAYOFCOLOR_H +#define ARRAYOFCOLOR_H + +#include + +typedef QList ArrayOfColor; + +void registerArrayOfColorMetaType(); + +#endif // ARRAYOFCOLOR_H diff --git a/src/plugin-qt/xsettings/types/scaleFactors.cpp b/src/plugin-qt/xsettings/types/scaleFactors.cpp new file mode 100644 index 00000000..3e278d34 --- /dev/null +++ b/src/plugin-qt/xsettings/types/scaleFactors.cpp @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "scaleFactors.h" + +void registerScaleFactorsMetaType() +{ + qRegisterMetaType("ScaleFactors"); + qDBusRegisterMetaType(); +} diff --git a/src/plugin-qt/xsettings/types/scaleFactors.h b/src/plugin-qt/xsettings/types/scaleFactors.h new file mode 100644 index 00000000..50af7c58 --- /dev/null +++ b/src/plugin-qt/xsettings/types/scaleFactors.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef SCALEFACTORS_H +#define SCALEFACTORS_H + +#include +#include + +typedef QMap ScaleFactors; + +void registerScaleFactorsMetaType(); + +#endif // SCALEFACTORS_H