feat: Add DConfig class

DConfig 用于读取程序配置文件,符合 DSG 标准:
https://gitlabwh.uniontech.com/wuhan/se/deepin-specifications/-/issues/3
当配置文件的DBus服务未注册时将直接使用 DConfigFile, 在非 Linux 系统上
使用 QSettings。

Log:
Change-Id: Id4ab32f9f5fc1d870b35203a3cb4d8476d28c839
This commit is contained in:
Ye ShanShan 2021-08-13 16:24:29 +08:00 committed by zccrs
parent 07bb75f6c0
commit b541e07bd5
7 changed files with 351 additions and 1 deletions

1
debian/api.json vendored

File diff suppressed because one or more lines are too long

1
src/DConfig Normal file
View File

@ -0,0 +1 @@
#include "dconfig.h"

View File

@ -0,0 +1,40 @@
<interface name='org.desktopspec.ConfigManager.Manager'>
<property access="read" type="s" name="version"/>
<property access="read" type="as" name="keyList"/>
<property access="read" type="b" name="canRead"/>
<property access="read" type="b" name="canWrite"/>
<property access="read" type="b" name="canOverride"/>
<!--为每个 key 提供一个只读属性,如:
<property access="read" type="v" name="key1"/>
...
<property access="read" type="v" name="key2"/>
-->
<method name='value'>
<arg type='s' name='key' direction='in'/>
<arg type='v' name='value' direction='out'/>
</method>
<method name='setValue'>
<arg type='s' name='key' direction='in'/>
<arg type='v' name='value' direction='in'/>
</method>
<method name='name'>
<arg type='s' name='key' direction='in'/>
<arg type='s' name='language' direction='in'/>
<arg type='s' name='name' direction='out'/>
</method>
<method name='description'>
<arg type='s' name='key' direction='in'/>
<arg type='s' name='language' direction='in'/>
<arg type='s' name='description' direction='out'/>
</method>
<method name='visibility'>
<arg type='s' name='key' direction='in'/>
<arg type='s' name='visibility' direction='out'/>
</method>
<!--采用引用计数的方式,引用为 0 时才真正的销毁-->
<method name='release'>
</method>
<signal name="valueChanged">
<arg name="key" type="s" direction="out"/>'
</signal>
</interface>

View File

@ -0,0 +1,8 @@
<interface name='org.desktopspec.ConfigManager'>
<method name='acquireManager'>
<arg type='s' name='appid' direction='in'/>
<arg type='s' name='name' direction='in'/>
<arg type='s' name='subpath' direction='in'/>
<arg type='o' name='path' direction='out'/>
</method>
</interface>

220
src/dconfig.cpp Normal file
View File

@ -0,0 +1,220 @@
/*
* Copyright (C) 2021 Uniontech Technology Co., Ltd.
*
* Author: zccrs <zccrs@live.com>
*
* Maintainer: zccrs <zhangjide@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dconfig.h"
#ifndef D_DISABLE_DCONFIG
#include "dconfigfile.h"
#ifndef D_DISABLE_DBUS_CONFIG
#include "configmanager_interface.h"
#include "manager_interface.h"
#endif
#include <unistd.h>
#include <pwd.h>
#else
#include <QSettings>
#endif
#include "dobject_p.h"
#include <QLoggingCategory>
#include <QCoreApplication>
// https://gitlabwh.uniontech.com/wuhan/se/deepin-specifications/-/issues/3
DCORE_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(cfLog)
#define DSG_CONFIG "org.desktopspec.ConfigManager"
#define DSG_CONFIG_MANAGER "org.desktopspec.ConfigManager"
inline static QString getAppId() {
// TODO: 应该使用更可靠的接口获取 appid
return QCoreApplication::applicationName();
}
class Q_DECL_HIDDEN DConfigPrivate : public DObjectPrivate
{
public:
DConfigPrivate(DConfig *qq)
: DObjectPrivate(qq)
{
}
QString name;
QString subpath;
#ifndef D_DISABLE_DCONFIG
inline static QString getUserName() {
uid_t uid = geteuid();
passwd *pw = getpwuid(uid);
return pw ? QString::fromLocal8Bit(pw->pw_name) : QString();
}
#ifndef D_DISABLE_DBUS_CONFIG
DSGConfigManager *config = nullptr;
#endif
QScopedPointer<DConfigFile> configFile;
inline QStringList keyList() const {
#ifndef D_DISABLE_DBUS_CONFIG
if (Q_LIKELY(config)) {
return config->keyList();
}
#endif
return configFile->keyList();
}
inline QVariant value(const QString &key, const QVariant &fallback) const {
#ifndef D_DISABLE_DBUS_CONFIG
if (Q_LIKELY(config)) {
const QDBusVariant &dv = config->value(key);
const QVariant &v = dv.variant();
return v.isValid() ? v : fallback;
}
#endif
return configFile->value(key, fallback);
}
inline void setValue(const QString &key, const QVariant &value) {
#ifndef D_DISABLE_DBUS_CONFIG
if (Q_LIKELY(config)) {
config->setValue(key, QDBusVariant(value));
return;
}
#endif
configFile->setValue(key, value, getUserName(), getAppId());
}
#else
QSettings *settings = nullptr;
inline QStringList keyList() const {
return settings->childKeys();
}
inline QVariant value(const QString &key, const QVariant &fallback) const {
return settings->value(key, fallback);
}
inline void setValue(const QString &key, const QVariant &value) {
settings->setValue(key, value);
}
#endif
D_DECLARE_PUBLIC(DConfig)
};
DConfig::DConfig(const QString &name, const QString &subpath,
QObject *parent)
: QObject(parent)
, DObject(*new DConfigPrivate(this))
{
D_D(DConfig);
d->name = name;
d->subpath = subpath;
}
bool DConfig::load()
{
D_D(DConfig);
const auto &appid = getAppId();
Q_ASSERT(!appid.isEmpty());
qCDebug(cfLog, "Load config of appid=%s name=%s, subpath=%s",
qPrintable(appid), qPrintable(d->name), qPrintable(d->subpath));
#ifndef D_DISABLE_DCONFIG
bool use_dbus_config = false;
#ifndef D_DISABLE_DBUS_CONFIG
if (d->config)
return true;
use_dbus_config = QDBusConnection::systemBus().interface()->isServiceRegistered(DSG_CONFIG);
if (use_dbus_config) {
qCDebug(cfLog, "Try acquire config manager object form DBus");
DSGConfig dsg_config(DSG_CONFIG, "/", QDBusConnection::systemBus());
const QDBusObjectPath dbus_path = dsg_config.acquireManager(appid, d->name, d->subpath);
if (dbus_path.path().isEmpty()) {
qCWarning(cfLog, "Can't acquire config manager");
return false;
} else {
d->config = new DSGConfigManager(DSG_CONFIG_MANAGER, dbus_path.path(),
QDBusConnection::systemBus(), this);
if (!d->config->isValid()) {
delete d->config;
d->config = nullptr;
return false;
} else {
connect(d->config, &DSGConfigManager::valueChanged, this, &DConfig::valueChanged);
use_dbus_config = true;
}
}
}
#endif
if (!use_dbus_config) {
qCDebug(cfLog, "Can't use DBus config service, fallback to DConfigFile mode");
if (d->configFile)
return true;
d->configFile.reset(new DConfigFile(appid, d->name, d->subpath));
}
#else
qCDebug(cfLog, "Fallback to QSettings mode");
if (d->settings)
return true;
d->settings = new QSettings(d->name, QSettings::IniFormat, this);
d->settings->beginGroup(d->subpath);
#endif
return true;
}
QStringList DConfig::keyList() const
{
D_DC(DConfig);
return d->keyList();
}
bool DConfig::isValid() const
{
D_DC(DConfig);
#ifndef D_DISABLE_DCONFIG
if (d->configFile)
return true;
#ifndef D_DISABLE_DBUS_CONFIG
return d->config;
#endif
#else
return d->settings;
#endif
}
QVariant DConfig::value(const QString &key, const QVariant &fallback) const
{
D_DC(DConfig);
return d->value(key, fallback);
}
void DConfig::setValue(const QString &key, const QVariant &value)
{
D_D(DConfig);
d->setValue(key, value);
}
DCORE_END_NAMESPACE

58
src/dconfig.h Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2021 Uniontech Technology Co., Ltd.
*
* Author: zccrs <zccrs@live.com>
*
* Maintainer: zccrs <zhangjide@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DCONFIG_H
#define DCONFIG_H
#include <dtkcore_global.h>
#include <DObject>
#include <QObject>
#include <QVariant>
DCORE_BEGIN_NAMESPACE
class DConfigPrivate;
class LIBDTKCORESHARED_EXPORT DConfig : public QObject, public DObject
{
Q_OBJECT
D_DECLARE_PRIVATE(DConfig)
Q_PROPERTY(QStringList keyList READ keyList FINAL)
public:
explicit DConfig(const QString &name, const QString &subpath = QString(),
QObject *parent = nullptr);
bool load();
QStringList keyList() const;
bool isValid() const;
QVariant value(const QString &key, const QVariant &fallback = QVariant()) const;
void setValue(const QString &key, const QVariant &value);
Q_SIGNALS:
void valueChanged(const QString &key);
};
DCORE_END_NAMESPACE
#endif // DCONFIG_H

View File

@ -11,11 +11,13 @@ QMAKE_CXXFLAGS_RELEASE += -fvisibility=hidden
INCLUDEPATH += $$PWD
HEADERS += $$PWD/dtkcore_global.h \
dconfig.h \
dsysinfo.h \
dsecurestring.h \
ddesktopentry.h
SOURCES += \
dconfig.cpp \
dsysinfo.cpp \
dsecurestring.cpp \
ddesktopentry.cpp
@ -26,6 +28,25 @@ linux: {
SOURCES += \
$$PWD/dconfigfile.cpp
# generic dbus interfaces
isEmpty(DTK_DISABLE_DBUS_CONFIG) {
QT += dbus
config.files = $$PWD/dbus/org.desktopspec.ConfigManager.xml
config.header_flags += -c DSGConfig -N
config.source_flags += -c DSGConfig -N
manager.files = $$PWD/dbus/org.desktopspec.ConfigManager.Manager.xml
manager.header_flags += -c DSGConfigManager -N
manager.source_flags += -c DSGConfigManager -N
DBUS_INTERFACES += config manager
} else {
DEFINES += D_DISABLE_DBUS_CONFIG
}
} else {
DEFINES += D_DISABLE_DCONFIG
}
include($$PWD/base/base.pri)
@ -46,7 +67,8 @@ includes.files += \
$$PWD/DSysInfo \
$$PWD/DSecureString \
$$PWD/DDesktopEntry \
$$PWD/DConfigFile
$$PWD/DConfigFile \
$$PWD/DConfig
INSTALLS += includes target