Группа :: Система/Библиотеки
Пакет: kf5-kconfig
Главная Изменения Спек Патчи Sources Загрузить Gear Bugs and FR Repocop
Патч: alt-kconfig-notify-via-dbus.patch
Скачать
Скачать
From a50d999d0741ec2ce57d513c949e39283b4561b7 Mon Sep 17 00:00:00 2001
From: Aleksei Nikiforov <darktemplar@basealt.ru>
Date: Fri, 14 Aug 2020 15:28:00 +0300
Subject: [PATCH] KConfig: notify on every configuration file change via DBUS
Also add utilities for listening to the broadcasted changes
and replaying changes.
---
kconfig/src/CMakeLists.txt | 3 +
kconfig/src/core/CMakeLists.txt | 3 +
kconfig/src/core/kconfig.cpp | 26 +++
kconfig/src/core/kconfig.h | 4 +
kconfig/src/core/kconfig_p.h | 1 +
kconfig/src/core/kconfigbase.cpp | 198 ++++++++++++++++
kconfig/src/core/kconfigbase.h | 23 ++
kconfig/src/core/kconfiggroup.cpp | 139 +++++++++++-
kconfig/src/core/kconfiggroup.h | 2 +
kconfig/src/core/kconfigserialization.cpp | 136 +++++++++++
kconfig/src/core/kconfigserialization.h | 24 ++
kconfig/src/kconf_watcher/CMakeLists.txt | 15 ++
kconfig/src/kconf_watcher/kconf_apply.cpp | 240 ++++++++++++++++++++
kconfig/src/kconf_watcher/kconf_watcher.cpp | 135 +++++++++++
kconfig/src/kconf_watcher/kconf_watcher_p.h | 18 ++
15 files changed, 965 insertions(+), 2 deletions(-)
create mode 100644 kconfig/src/core/kconfigserialization.cpp
create mode 100644 kconfig/src/core/kconfigserialization.h
create mode 100644 kconfig/src/kconf_watcher/CMakeLists.txt
create mode 100644 kconfig/src/kconf_watcher/kconf_apply.cpp
create mode 100644 kconfig/src/kconf_watcher/kconf_watcher.cpp
create mode 100644 kconfig/src/kconf_watcher/kconf_watcher_p.h
diff --git a/kconfig/src/CMakeLists.txt b/kconfig/src/CMakeLists.txt
index 151c324..2f211df 100644
--- a/kconfig/src/CMakeLists.txt
+++ b/kconfig/src/CMakeLists.txt
@@ -4,6 +4,9 @@ if(TARGET Qt5::Gui)
endif()
add_subdirectory(kconfig_compiler)
add_subdirectory(kconf_update)
+if(KCONFIG_USE_DBUS)
+ add_subdirectory(kconf_watcher)
+endif()
add_subdirectory(kreadconfig)
ecm_qt_install_logging_categories(
diff --git a/kconfig/src/core/CMakeLists.txt b/kconfig/src/core/CMakeLists.txt
index eb16d03..c80a400 100644
--- a/kconfig/src/core/CMakeLists.txt
+++ b/kconfig/src/core/CMakeLists.txt
@@ -11,6 +11,7 @@ set(libkconfigcore_SRCS
kauthorized.cpp
kemailsettings.cpp
kconfigwatcher.cpp
+ kconfigserialization.cpp
)
ecm_qt_declare_logging_category(libkconfigcore_SRCS
@@ -65,6 +66,7 @@ ecm_generate_headers(KConfigCore_HEADERS
KEMailSettings
ConversionCheck
KConfigWatcher
+ KConfigSerialization
REQUIRED_HEADERS KConfigCore_HEADERS
)
diff --git a/kconfig/src/core/kconfig.cpp b/kconfig/src/core/kconfig.cpp
index 7f53847..f4250e0 100644
--- a/kconfig/src/core/kconfig.cpp
+++ b/kconfig/src/core/kconfig.cpp
@@ -60,6 +60,7 @@ KConfigPrivate::KConfigPrivate(KConfig::OpenFlags flags, QStandardPaths::Standar
, bFileImmutable(false)
, bForceGlobal(false)
, bSuppressGlobal(false)
+ , bUpdateNotificationEnabled(true)
, configState(KConfigBase::NoAccess)
{
const bool isTestMode = QStandardPaths::isTestModeEnabled();
@@ -886,6 +887,19 @@ KEntryMap::EntryOptions convertToOptions(KConfig::WriteConfigFlags flags)
}
void KConfig::deleteGroupImpl(const QByteArray &aGroup, WriteConfigFlags flags)
+{
+ if (isUpdateNotificationEnabled())
+ {
+ sendNotification(nullptr, QByteArray(), QStringList(),
+ QStringList(QString::fromUtf8(aGroup)),
+ name(), locationType(), flags,
+ EntryNotificationType::DeleteGroup);
+ }
+
+ deleteGroupImplWithoutNotification(aGroup, flags);
+}
+
+void KConfig::deleteGroupImplWithoutNotification(const QByteArray &aGroup, WriteConfigFlags flags)
{
Q_D(KConfig);
KEntryMap::EntryOptions options = convertToOptions(flags) | KEntryMap::EntryDeleted;
@@ -1002,6 +1016,18 @@ QStandardPaths::StandardLocation KConfig::locationType() const
return d->resourceType;
}
+bool KConfig::isUpdateNotificationEnabledImpl() const
+{
+ Q_D(const KConfig);
+ return d->bUpdateNotificationEnabled;
+}
+
+void KConfig::setUpdateNotificationEnabledImpl(bool new_state)
+{
+ Q_D(KConfig);
+ d->bUpdateNotificationEnabled = new_state;
+}
+
void KConfig::virtual_hook(int /*id*/, void * /*data*/)
{
/* nothing */
diff --git a/kconfig/src/core/kconfig.h b/kconfig/src/core/kconfig.h
index 792a9a6..7f6638d 100644
--- a/kconfig/src/core/kconfig.h
+++ b/kconfig/src/core/kconfig.h
@@ -370,6 +370,8 @@ protected:
const KConfigGroup groupImpl(const QByteArray &b) const override;
void deleteGroupImpl(const QByteArray &group, WriteConfigFlags flags = Normal) override;
bool isGroupImmutableImpl(const QByteArray &aGroup) const override;
+ bool isUpdateNotificationEnabledImpl() const override;
+ void setUpdateNotificationEnabledImpl(bool new_state) override;
friend class KConfigGroup;
friend class KConfigGroupPrivate;
@@ -384,6 +386,8 @@ protected:
KConfig(KConfigPrivate &d);
+ void deleteGroupImplWithoutNotification(const QByteArray &group, WriteConfigFlags flags = Normal);
+
private:
friend class KConfigTest;
diff --git a/kconfig/src/core/kconfig_p.h b/kconfig/src/core/kconfig_p.h
index b201df1..b013775 100644
--- a/kconfig/src/core/kconfig_p.h
+++ b/kconfig/src/core/kconfig_p.h
@@ -65,6 +65,7 @@ private:
bool bFileImmutable : 1;
bool bForceGlobal : 1;
bool bSuppressGlobal : 1;
+ bool bUpdateNotificationEnabled : 1;
static bool mappingsRegistered;
diff --git a/kconfig/src/core/kconfigbase.cpp b/kconfig/src/core/kconfigbase.cpp
index 9eee642..169c2a8 100644
--- a/kconfig/src/core/kconfigbase.cpp
+++ b/kconfig/src/core/kconfigbase.cpp
@@ -9,10 +9,27 @@
#include "kconfigbase.h"
+#include "config-kconfig.h"
#include "kconfiggroup.h"
#include <QString>
+#if KCONFIG_USE_DBUS
+#include <QDBusConnection>
+#include <QDBusMessage>
+#include <QStringList>
+#include <QVector>
+#include <QDebug>
+#include <QMutex>
+#include <QMutexLocker>
+#include <unistd.h>
+#include <pwd.h>
+#include "kconfig.h"
+#include "kconfigserialization.h"
+#endif // KCONFIG_USE_DBUS
+
+bool KConfigBase::s_notification_warn_once = true;
+
bool KConfigBase::hasGroup(const QString &group) const
{
return hasGroupImpl(group.toUtf8());
@@ -88,6 +105,16 @@ bool KConfigBase::isGroupImmutable(const char *aGroup) const
return isGroupImmutableImpl(QByteArray(aGroup));
}
+bool KConfigBase::isUpdateNotificationEnabled() const
+{
+ return isUpdateNotificationEnabledImpl();
+}
+
+void KConfigBase::setUpdateNotificationEnabled(bool new_state)
+{
+ setUpdateNotificationEnabledImpl(new_state);
+}
+
KConfigBase::~KConfigBase()
{
}
@@ -99,3 +126,174 @@ KConfigBase::KConfigBase()
void KConfigBase::virtual_hook(int, void *)
{
}
+
+void KConfigBase::sendNotification(const char *key, const QByteArray &value,
+ const QStringList &entry_paths, const QStringList &groups,
+ const QString &filename, QStandardPaths::StandardLocation location_type,
+ WriteConfigFlags flags, EntryNotificationType notification_type)
+{
+#if KCONFIG_USE_DBUS
+ {
+ static QMutex s_globalsInitMutex;
+ static bool s_globalsInitialized = false;
+ static bool s_notificationsGloballyEnabled;
+
+ { // lock
+ QMutexLocker globalsInitMutexLock(&s_globalsInitMutex);
+
+ if (!s_globalsInitialized)
+ {
+ KConfig globals_config(QStringLiteral("kdeglobals"), KConfig::NoGlobals);
+ KConfigGroup alt_group = globals_config.group(QStringLiteral("ALT"));
+ s_notificationsGloballyEnabled = alt_group.readEntry(QStringLiteral("EnableDbusNotifications"), false);
+ s_globalsInitialized = true;
+ }
+ } // unlock
+
+ if (!s_notificationsGloballyEnabled)
+ {
+ return;
+ }
+ }
+
+ if (!QDBusConnection::sessionBus().isConnected())
+ {
+ if (s_notification_warn_once)
+ {
+ qDebug() << QStringLiteral(
+ "Cannot connect to the D-Bus session bus.\n"
+ "Please check your system settings and try again.\n");
+ s_notification_warn_once = false;
+ }
+
+ return;
+ }
+
+ QStringList message_values_list;
+
+ message_values_list << QStringLiteral("version=1");
+
+ switch (notification_type)
+ {
+ case EntryNotificationType::Entry:
+ message_values_list << QStringLiteral("event=write_entry");
+ break;
+
+ case EntryNotificationType::PathEntry:
+ message_values_list << QStringLiteral("event=write_path_entry");
+ break;
+
+ case EntryNotificationType::PathArrayEntry:
+ message_values_list << QStringLiteral("event=write_path_array_entry");
+ break;
+
+ case EntryNotificationType::DeleteEntry:
+ message_values_list << QStringLiteral("event=delete_entry");
+ break;
+
+ case EntryNotificationType::DeleteGroup:
+ message_values_list << QStringLiteral("event=delete_group");
+ break;
+ }
+
+ {
+ qint64 initlen = sysconf(_SC_GETPW_R_SIZE_MAX);
+
+ struct passwd result;
+ struct passwd *resultp;
+
+ QVector<char> buffer;
+ buffer.resize((initlen == -1) ? 1024 : initlen);
+
+ uid_t uid = geteuid();
+
+ int e;
+
+ while ((e = getpwuid_r(uid, &result, buffer.data(), buffer.size(), &resultp)) == ERANGE)
+ {
+ buffer.resize(buffer.size() * 2);
+ }
+
+ if (e != 0)
+ {
+ qDebug() << QStringLiteral("Failed to serialize config entry: getpwuid_r function failed");
+ return;
+ }
+
+ if (resultp != nullptr)
+ {
+ message_values_list << QStringLiteral("username=%1").arg(QString::fromLocal8Bit(resultp->pw_name));
+ }
+
+ message_values_list << QStringLiteral("uid=%1").arg(uid);
+ }
+
+ message_values_list << QStringLiteral("filename=%1").arg(filename);
+
+ {
+ bool ok = false;
+ QString location_type_string = serializeStandardLocation(location_type, ok);
+ if (!ok)
+ {
+ qDebug() << QStringLiteral("Failed to serialize config entry: failed to serialize QStandardPaths::StandardLocation enum");
+ return;
+ }
+ message_values_list << QStringLiteral("location_type=%1").arg(location_type_string);
+ }
+
+ {
+ bool ok = false;
+ QString flags_string = serializeWriteConfigFlags(flags, ok);
+ if (!ok)
+ {
+ qDebug() << QStringLiteral("Failed to serialize config entry: failed to serialize KConfigBase::WriteConfigFlags");
+ return;
+ }
+
+ message_values_list << QStringLiteral("flags=%1").arg(flags_string);
+ }
+
+ message_values_list << QStringLiteral("group_name=%1").arg(groups.join(QLatin1Char('|')));
+
+ switch (notification_type)
+ {
+ case EntryNotificationType::Entry:
+ case EntryNotificationType::PathEntry:
+ case EntryNotificationType::PathArrayEntry:
+ case EntryNotificationType::DeleteEntry:
+ message_values_list << QStringLiteral("entry_name=%1").arg(QString::fromLocal8Bit(key));
+ break;
+
+ default:
+ break;
+ }
+
+ switch (notification_type)
+ {
+ case EntryNotificationType::Entry:
+ case EntryNotificationType::PathEntry:
+ message_values_list << QStringLiteral("entry_value=%1").arg(QString::fromLocal8Bit(value.isNull() ? QByteArray("") : value));
+ break;
+
+ default:
+ break;
+ }
+
+ if (notification_type == EntryNotificationType::PathArrayEntry)
+ {
+ int idx = 1;
+ auto iter = entry_paths.begin();
+ for (; iter != entry_paths.end(); ++iter, ++idx)
+ {
+ message_values_list << QStringLiteral("entry_path_%1=%2").arg(idx).arg(*iter);
+ }
+ }
+
+ QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/"),
+ QStringLiteral("org.alt.kde.kconfig.extra.notify"),
+ QStringLiteral("ConfigChanged"));
+
+ message << message_values_list;
+ QDBusConnection::sessionBus().send(message);
+#endif
+}
diff --git a/kconfig/src/core/kconfigbase.h b/kconfig/src/core/kconfigbase.h
index 79af885..54642d4 100644
--- a/kconfig/src/core/kconfigbase.h
+++ b/kconfig/src/core/kconfigbase.h
@@ -15,6 +15,7 @@
#include <QStringList>
#include <QtGlobal>
+#include <QStandardPaths>
class KConfigGroup;
class KConfigBasePrivate;
@@ -218,6 +219,9 @@ public:
*/
bool isGroupImmutable(const char *group) const;
+ bool isUpdateNotificationEnabled() const;
+ void setUpdateNotificationEnabled(bool new_state);
+
protected:
KConfigBase();
@@ -232,10 +236,29 @@ protected:
/// @param group name of group, encoded in UTF-8
virtual bool isGroupImmutableImpl(const QByteArray &group) const = 0;
+ virtual bool isUpdateNotificationEnabledImpl() const = 0;
+ virtual void setUpdateNotificationEnabledImpl(bool new_state) = 0;
+
/** Virtual hook, used to add new "virtual" functions while maintaining
* binary compatibility. Unused in this class.
*/
virtual void virtual_hook(int id, void *data);
+
+ enum class EntryNotificationType
+ {
+ Entry,
+ PathEntry,
+ PathArrayEntry,
+ DeleteEntry,
+ DeleteGroup,
+ };
+
+ void sendNotification(const char *pKey, const QByteArray &value,
+ const QStringList &entry_paths, const QStringList &groups,
+ const QString &filename, QStandardPaths::StandardLocation location_type,
+ WriteConfigFlags pFlags, EntryNotificationType notification_type);
+
+ static bool s_notification_warn_once;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KConfigBase::WriteConfigFlags)
diff --git a/kconfig/src/core/kconfiggroup.cpp b/kconfig/src/core/kconfiggroup.cpp
--- a/kconfig/src/core/kconfiggroup.cpp
+++ b/kconfig/src/core/kconfiggroup.cpp
@@ -16,6 +16,7 @@
#include "kconfigdata_p.h"
#include "ksharedconfig.h"
+#include <QStringList>
#include <QDate>
#include <QDir>
#include <QFile>
@@ -40,6 +41,7 @@ public:
, mName(name)
, bImmutable(isImmutable)
, bConst(isConst)
+ , bUpdateNotificationEnabled(owner->isUpdateNotificationEnabled())
{
}
@@ -49,6 +51,7 @@ public:
, mName(name)
, bImmutable(name.isEmpty() ? owner->isImmutable() : owner->isGroupImmutable(name))
, bConst(false)
+ , bUpdateNotificationEnabled(owner->isUpdateNotificationEnabled())
{
}
@@ -58,6 +61,7 @@ public:
, mName(name)
, bImmutable(isImmutable)
, bConst(isConst)
+ , bUpdateNotificationEnabled(parent->isUpdateNotificationEnabled())
{
if (!parent->d->mName.isEmpty()) {
mParent = parent->d;
@@ -70,6 +74,7 @@ public:
, mName(name)
, bImmutable(isImmutable)
, bConst(other->bConst)
+ , bUpdateNotificationEnabled(other->bUpdateNotificationEnabled)
{
if (!other->mName.isEmpty()) {
mParent = const_cast<KConfigGroupPrivate *>(other);
@@ -84,6 +89,7 @@ public:
/* bitfield */
const bool bImmutable : 1; // is this group immutable?
const bool bConst : 1; // is this group read-only?
+ bool bUpdateNotificationEnabled: 1;
QByteArray fullName() const
{
@@ -109,6 +115,24 @@ public:
return fullName() + '\x1d' + aGroup;
}
+ QStringList fullNameList() const
+ {
+ if (!mParent) {
+ return QStringList(QString::fromUtf8(name()));
+ }
+ return mParent->fullNameList(mName);
+ }
+
+ QStringList fullNameList(const QByteArray &aGroup) const
+ {
+ if (mName.isEmpty()) {
+ return QStringList(QString::fromUtf8(aGroup));
+ }
+ QStringList result = fullNameList();
+ result.append(QString::fromUtf8(aGroup));
+ return result;
+ }
+
static QExplicitlySharedDataPointer<KConfigGroupPrivate> create(KConfigBase *master, const QByteArray &name, bool isImmutable, bool isConst)
{
QExplicitlySharedDataPointer<KConfigGroupPrivate> data;
@@ -470,6 +494,7 @@ static inline bool writeEntryGui(KConfig
KConfigGroup::KConfigGroup(KConfigBase *master, const QString &_group)
: d(KConfigGroupPrivate::create(master, _group.toUtf8(), master->isGroupImmutable(_group), false))
{
+
}
KConfigGroup::KConfigGroup(KConfigBase *master, const char *_group)
@@ -559,7 +584,23 @@ void KConfigGroup::deleteGroup(WriteConf
Q_ASSERT_X(isValid(), "KConfigGroup::deleteGroup", "accessing an invalid group");
Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteGroup", "deleting a read-only group");
- config()->deleteGroup(d->fullName(), flags);
+ if (isUpdateNotificationEnabled())
+ {
+ KConfig *parent_config = config();
+ if (parent_config != nullptr)
+ {
+ sendNotification(nullptr, QByteArray(""), QStringList(),
+ d->fullNameList(),
+ parent_config->name(), parent_config->locationType(), flags,
+ EntryNotificationType::DeleteGroup);
+ }
+ else
+ {
+ qDebug() << QStringLiteral("Failed to send notification: no parent KConfig is found");
+ }
+ }
+
+ config()->deleteGroupImplWithoutNotification(d->fullName(), flags);
}
#if KCONFIGCORE_BUILD_DEPRECATED_SINCE(5, 0)
@@ -859,6 +900,22 @@ void KConfigGroup::writeEntry(const char
Q_ASSERT_X(isValid(), "KConfigGroup::writeEntry", "accessing an invalid group");
Q_ASSERT_X(!d->bConst, "KConfigGroup::writeEntry", "writing to a read-only group");
+ if (isUpdateNotificationEnabled())
+ {
+ KConfig *parent_config = config();
+ if (parent_config != nullptr)
+ {
+ sendNotification(key, value.isNull() ? QByteArray("") : value, QStringList(),
+ d->fullNameList(),
+ parent_config->name(), parent_config->locationType(), flags,
+ EntryNotificationType::Entry);
+ }
+ else
+ {
+ qDebug() << QStringLiteral("Failed to send notification: no parent KConfig is found");
+ }
+ }
+
config()->d_func()->putData(d->fullName(), key, value.isNull() ? QByteArray("") : value, flags);
}
@@ -1085,6 +1142,22 @@ void KConfigGroup::writePathEntry(const
Q_ASSERT_X(isValid(), "KConfigGroup::writePathEntry", "accessing an invalid group");
Q_ASSERT_X(!d->bConst, "KConfigGroup::writePathEntry", "writing to a read-only group");
+ if (isUpdateNotificationEnabled())
+ {
+ KConfig *parent_config = config();
+ if (parent_config != nullptr)
+ {
+ sendNotification(pKey, path.toLocal8Bit(), QStringList(),
+ d->fullNameList(),
+ parent_config->name(), parent_config->locationType(), pFlags,
+ EntryNotificationType::PathEntry);
+ }
+ else
+ {
+ qDebug() << QStringLiteral("Failed to send notification: no parent KConfig is found");
+ }
+ }
+
config()->d_func()->putData(d->fullName(), pKey, translatePath(path).toUtf8(), pFlags, true);
}
@@ -1104,6 +1177,22 @@ void KConfigGroup::writePathEntry(const
list << translatePath(path).toUtf8();
}
+ if (isUpdateNotificationEnabled())
+ {
+ KConfig *parent_config = config();
+ if (parent_config != nullptr)
+ {
+ sendNotification(pKey, QByteArray(), value,
+ d->fullNameList(),
+ parent_config->name(), parent_config->locationType(), pFlags,
+ EntryNotificationType::PathArrayEntry);
+ }
+ else
+ {
+ qDebug() << QStringLiteral("Failed to send notification: no parent KConfig is found");
+ }
+ }
+
config()->d_func()->putData(d->fullName(), pKey, KConfigGroupPrivate::serializeList(list), pFlags, true);
}
@@ -1112,6 +1201,22 @@ void KConfigGroup::deleteEntry(const cha
Q_ASSERT_X(isValid(), "KConfigGroup::deleteEntry", "accessing an invalid group");
Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteEntry", "deleting from a read-only group");
+ if (isUpdateNotificationEnabled())
+ {
+ KConfig *parent_config = config();
+ if (parent_config != nullptr)
+ {
+ sendNotification(key, QByteArray(), QStringList(),
+ d->fullNameList(),
+ parent_config->name(), parent_config->locationType(), flags,
+ EntryNotificationType::DeleteEntry);
+ }
+ else
+ {
+ qDebug() << QStringLiteral("Failed to send notification: no parent KConfig is found");
+ }
+ }
+
config()->d_func()->putData(d->fullName(), key, QByteArray(), flags);
}
@@ -1221,7 +1326,23 @@ void KConfigGroup::deleteGroupImpl(const
Q_ASSERT_X(isValid(), "KConfigGroup::deleteGroupImpl", "accessing an invalid group");
Q_ASSERT_X(!d->bConst, "KConfigGroup::deleteGroupImpl", "deleting from a read-only group");
- config()->deleteGroup(d->fullName(b), flags);
+ if (isUpdateNotificationEnabled())
+ {
+ KConfig *parent_config = config();
+ if (parent_config != nullptr)
+ {
+ sendNotification(nullptr, QByteArray(), QStringList(),
+ d->fullNameList(b),
+ parent_config->name(), parent_config->locationType(), flags,
+ EntryNotificationType::DeleteGroup);
+ }
+ else
+ {
+ qDebug() << QStringLiteral("Failed to send notification: no parent KConfig is found");
+ }
+ }
+
+ config()->deleteGroupImplWithoutNotification(d->fullName(b), flags);
}
bool KConfigGroup::isGroupImmutableImpl(const QByteArray &b) const
@@ -1235,6 +1356,20 @@ bool KConfigGroup::isGroupImmutableImpl(
return config()->isGroupImmutable(d->fullName(b));
}
+bool KConfigGroup::isUpdateNotificationEnabledImpl() const
+{
+ Q_ASSERT_X(isValid(), "KConfigGroup::isUpdateNotificationEnabledImpl", "accessing an invalid group");
+
+ return d->bUpdateNotificationEnabled;
+}
+
+void KConfigGroup::setUpdateNotificationEnabledImpl(bool new_state)
+{
+ Q_ASSERT_X(isValid(), "KConfigGroup::setUpdateNotificationEnabledImpl", "accessing an invalid group");
+
+ d->bUpdateNotificationEnabled = new_state;
+}
+
void KConfigGroup::copyTo(KConfigBase *other, WriteConfigFlags pFlags) const
{
Q_ASSERT_X(isValid(), "KConfigGroup::copyTo", "accessing an invalid group");
diff --git a/kconfig/src/core/kconfiggroup.h b/kconfig/src/core/kconfiggroup.h
index 5c125dd..000cbdb 100644
--- a/kconfig/src/core/kconfiggroup.h
+++ b/kconfig/src/core/kconfiggroup.h
@@ -705,6 +705,8 @@ protected:
const KConfigGroup groupImpl(const QByteArray &b) const override;
void deleteGroupImpl(const QByteArray &group, WriteConfigFlags flags) override;
bool isGroupImmutableImpl(const QByteArray &aGroup) const override;
+ bool isUpdateNotificationEnabledImpl() const override;
+ void setUpdateNotificationEnabledImpl(bool new_state) override;
private:
QExplicitlySharedDataPointer<KConfigGroupPrivate> d;
diff --git a/kconfig/src/core/kconfigserialization.cpp b/kconfig/src/core/kconfigserialization.cpp
new file mode 100644
index 0000000..299ebf7
--- /dev/null
+++ b/kconfig/src/core/kconfigserialization.cpp
@@ -0,0 +1,136 @@
+/*
+ This file is part of the KDE libraries
+ SPDX-FileCopyrightText: 2020 Aleksei Nikiforov <darktemplar@basealt.ru>
+
+ SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+#include "kconfigserialization.h"
+
+#include <QChar>
+#include <QStringList>
+#include <QMap>
+#include <QMutex>
+#include <QMutexLocker>
+
+static const QMap<QStandardPaths::StandardLocation, QString> s_standardLocationEnumValues =
+{
+ { QStandardPaths::DesktopLocation, QStringLiteral("DesktopLocation") },
+ { QStandardPaths::DocumentsLocation, QStringLiteral("DocumentsLocation") },
+ { QStandardPaths::FontsLocation, QStringLiteral("FontsLocation") },
+ { QStandardPaths::ApplicationsLocation, QStringLiteral("ApplicationsLocation") },
+ { QStandardPaths::MusicLocation, QStringLiteral("MusicLocation") },
+ { QStandardPaths::MoviesLocation, QStringLiteral("MoviesLocation") },
+ { QStandardPaths::PicturesLocation, QStringLiteral("PicturesLocation") },
+ { QStandardPaths::TempLocation, QStringLiteral("TempLocation") },
+ { QStandardPaths::HomeLocation, QStringLiteral("HomeLocation") },
+ { QStandardPaths::AppLocalDataLocation, QStringLiteral("AppLocalDataLocation") },
+ { QStandardPaths::CacheLocation, QStringLiteral("CacheLocation") },
+ { QStandardPaths::GenericDataLocation, QStringLiteral("GenericDataLocation") },
+ { QStandardPaths::RuntimeLocation, QStringLiteral("RuntimeLocation") },
+ { QStandardPaths::ConfigLocation, QStringLiteral("ConfigLocation") },
+ { QStandardPaths::DownloadLocation, QStringLiteral("DownloadLocation") },
+ { QStandardPaths::GenericCacheLocation, QStringLiteral("GenericCacheLocation") },
+ { QStandardPaths::GenericConfigLocation, QStringLiteral("GenericConfigLocation") },
+ { QStandardPaths::AppDataLocation, QStringLiteral("AppDataLocation") },
+ { QStandardPaths::AppConfigLocation, QStringLiteral("AppConfigLocation") },
+};
+
+static const QMap<QString, KConfigBase::WriteConfigFlag> s_writeConfigFlagsEnumValues =
+{
+ { QStringLiteral("Persistent"), KConfigBase::Persistent },
+ { QStringLiteral("Global"), KConfigBase::Global },
+ { QStringLiteral("Localized"), KConfigBase::Localized },
+ { QStringLiteral("Notify"), KConfigBase::Notify },
+ { QStringLiteral("Normal"), KConfigBase::Normal },
+};
+
+QString serializeStandardLocation(QStandardPaths::StandardLocation value, bool &ok)
+{
+ auto iter = s_standardLocationEnumValues.find(value);
+ if (iter != s_standardLocationEnumValues.end())
+ {
+ ok = true;
+ return iter.value();
+ }
+ else
+ {
+ ok = false;
+ return QString();
+ }
+}
+
+QStandardPaths::StandardLocation deserializeStandardLocation(const QString &value, bool &ok)
+{
+ static QMap<QString, QStandardPaths::StandardLocation> s_standardLocationEnumDeserializationMap;
+ static QMutex s_standardLocationEnumDeserializationMapMutex;
+
+ { // lock
+ QMutexLocker mapLock(&s_standardLocationEnumDeserializationMapMutex);
+
+ if (s_standardLocationEnumDeserializationMap.isEmpty())
+ {
+ for (auto iter = s_standardLocationEnumValues.begin(); iter != s_standardLocationEnumValues.end(); ++iter)
+ {
+ s_standardLocationEnumDeserializationMap[iter.value()] = iter.key();
+ }
+ }
+ } // unlock
+
+ auto iter = s_standardLocationEnumDeserializationMap.find(value);
+ if (iter != s_standardLocationEnumDeserializationMap.end())
+ {
+ ok = true;
+ return iter.value();
+ }
+ else
+ {
+ ok = false;
+ return QStandardPaths::StandardLocation();
+ }
+}
+
+QString serializeWriteConfigFlags(const KConfigBase::WriteConfigFlags &value, bool &ok)
+{
+ QStringList result;
+ KConfigBase::WriteConfigFlags reconstructed_value;
+
+ for (auto values_iter = s_writeConfigFlagsEnumValues.begin(); values_iter != s_writeConfigFlagsEnumValues.end(); ++values_iter)
+ {
+ if (value.testFlag(values_iter.value()))
+ {
+ result.push_back(values_iter.key());
+ reconstructed_value |= values_iter.value();
+ }
+ }
+
+ ok = (value == reconstructed_value);
+
+ return result.join(QChar::fromLatin1('|'));;
+}
+
+KConfigBase::WriteConfigFlags deserializeWriteConfigFlags(const QString &value, bool &ok)
+{
+ KConfigBase::WriteConfigFlags result;
+
+ QStringList values_list = value.split(QChar::fromLatin1('|'));
+
+ bool no_unknown_values = true;
+
+ for (const auto &value_iter: values_list)
+ {
+ auto map_iter = s_writeConfigFlagsEnumValues.find(value_iter);
+ if (map_iter != s_writeConfigFlagsEnumValues.end())
+ {
+ result |= map_iter.value();
+ }
+ else
+ {
+ no_unknown_values = false;
+ }
+ }
+
+ ok = no_unknown_values;
+
+ return result;
+}
diff --git a/kconfig/src/core/kconfigserialization.h b/kconfig/src/core/kconfigserialization.h
new file mode 100644
index 0000000..52a6da0
--- /dev/null
+++ b/kconfig/src/core/kconfigserialization.h
@@ -0,0 +1,24 @@
+/*
+ This file is part of the KDE libraries
+ SPDX-FileCopyrightText: 2020 Aleksei Nikiforov <darktemplar@basealt.ru>
+
+ SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+#ifndef KCONFIGSERIALIZATION_H
+#define KCONFIGSERIALIZATION_H
+
+#include "kconfigbase.h"
+
+#include <kconfigcore_export.h>
+
+#include <QStandardPaths>
+#include <QString>
+
+KCONFIGCORE_EXPORT QString serializeStandardLocation(QStandardPaths::StandardLocation value, bool &ok);
+KCONFIGCORE_EXPORT QStandardPaths::StandardLocation deserializeStandardLocation(const QString &value, bool &ok);
+
+KCONFIGCORE_EXPORT QString serializeWriteConfigFlags(const KConfigBase::WriteConfigFlags &value, bool &ok);
+KCONFIGCORE_EXPORT KConfigBase::WriteConfigFlags deserializeWriteConfigFlags(const QString &value, bool &ok);
+
+#endif // KCONFIGSERIALIZATION_H
diff --git a/kconfig/src/kconf_watcher/CMakeLists.txt b/kconfig/src/kconf_watcher/CMakeLists.txt
new file mode 100644
index 0000000..2d1e5fe
--- /dev/null
+++ b/kconfig/src/kconf_watcher/CMakeLists.txt
@@ -0,0 +1,15 @@
+include(ECMMarkNonGuiExecutable)
+
+add_executable(kconf_watcher kconf_watcher.cpp)
+target_link_libraries(kconf_watcher Qt5::Core Qt5::DBus)
+ecm_mark_nongui_executable(kconf_watcher)
+
+install(TARGETS kconf_watcher ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
+
+########### next target ###############
+
+add_executable(kconf_apply kconf_apply.cpp)
+target_link_libraries(kconf_apply Qt5::Core KF5::ConfigCore)
+ecm_mark_nongui_executable(kconf_apply)
+
+install(TARGETS kconf_apply ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/kconfig/src/kconf_watcher/kconf_apply.cpp b/kconfig/src/kconf_watcher/kconf_apply.cpp
new file mode 100644
index 0000000..0684640
--- /dev/null
+++ b/kconfig/src/kconf_watcher/kconf_apply.cpp
@@ -0,0 +1,240 @@
+/*
+ This file is part of the KDE libraries
+ SPDX-FileCopyrightText: 2020 Aleksei Nikiforov <darktemplar@basealt.ru>
+
+ SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+#include <QCoreApplication>
+#include <QTextStream>
+#include <QString>
+#include <QMap>
+#include <QLatin1Char>
+
+#include <KConfigSerialization>
+#include <KConfig>
+#include <KConfigGroup>
+
+enum class event_type
+{
+ write_entry = 0,
+ write_path_entry,
+ write_path_array_entry,
+ delete_entry,
+ delete_group,
+};
+
+static const QMap<QString, event_type> string_to_event_type_map =
+{
+ { QStringLiteral("write_entry"), event_type::write_entry },
+ { QStringLiteral("write_path_entry"), event_type::write_path_entry },
+ { QStringLiteral("write_path_array_entry"), event_type::write_path_array_entry },
+ { QStringLiteral("delete_entry"), event_type::delete_entry },
+ { QStringLiteral("delete_group"), event_type::delete_group },
+};
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+ app.setApplicationVersion(QStringLiteral("1.0"));
+
+ QTextStream stdin_stream(stdin, QIODevice::ReadOnly);
+
+ bool in_message = false;
+ QMap<QString, QString> message_data;
+
+ while (!stdin_stream.atEnd())
+ {
+ QString line = stdin_stream.readLine();
+
+ if (in_message)
+ {
+ if (line == QStringLiteral("message_end"))
+ {
+ do
+ {
+ if (message_data[QStringLiteral("version")] != QStringLiteral("1"))
+ {
+ break;
+ }
+
+ event_type event_type_value;
+ QString filename;
+ QStandardPaths::StandardLocation standard_location;
+ KConfigBase::WriteConfigFlags write_config_flags;
+ QStringList group_names;
+ QString entry_name;
+ QString entry_value;
+
+ // TODO: process uid/username, use it for optional filtering
+
+ {
+ auto iter = string_to_event_type_map.find(message_data[QStringLiteral("event")]);
+ if (iter == string_to_event_type_map.end())
+ {
+ break;
+ }
+
+ event_type_value = iter.value();
+ }
+
+ {
+ auto iter = message_data.find(QStringLiteral("filename"));
+ if (iter == message_data.end())
+ {
+ break;
+ }
+
+ filename = iter.value();
+ }
+
+ {
+ auto iter = message_data.find(QStringLiteral("location_type"));;
+ if (iter == message_data.end())
+ {
+ break;
+ }
+
+ bool ok = false;
+ standard_location = deserializeStandardLocation(iter.value(), ok);
+ if (!ok)
+ {
+ break;
+ }
+ }
+
+ {
+ auto iter = message_data.find(QStringLiteral("flags"));;
+ if (iter == message_data.end())
+ {
+ break;
+ }
+
+ bool ok = false;
+ write_config_flags = deserializeWriteConfigFlags(iter.value(), ok);
+ if (!ok)
+ {
+ break;
+ }
+ }
+
+ {
+ auto iter = message_data.find(QStringLiteral("group_name"));;
+ if (iter == message_data.end())
+ {
+ break;
+ }
+
+ group_names = iter.value().split(QLatin1Char('|'));
+ if (group_names.isEmpty())
+ {
+ break;
+ }
+ }
+
+ {
+ auto iter = message_data.find(QStringLiteral("entry_name"));;
+ if ((iter == message_data.end())
+ && ((event_type_value == event_type::write_entry)
+ || (event_type_value == event_type::write_path_entry)
+ || (event_type_value == event_type::write_path_array_entry)
+ || (event_type_value == event_type::delete_entry)))
+ {
+ break;
+ }
+
+ if (iter != message_data.end())
+ {
+ entry_name = iter.value();
+ }
+ }
+
+ {
+ auto iter = message_data.find(QStringLiteral("entry_value"));;
+ if ((iter == message_data.end())
+ && ((event_type_value == event_type::write_entry)
+ || (event_type_value == event_type::write_path_entry)))
+ {
+ break;
+ }
+
+ if (iter != message_data.end())
+ {
+ entry_value = iter.value();
+ }
+ }
+
+ KConfig config(filename, KConfig::FullConfig, standard_location);
+ config.setUpdateNotificationEnabled(false);
+
+ auto group_name_iter = group_names.begin();
+ KConfigGroup config_group = config.group(*group_name_iter);
+
+ for (++group_name_iter; group_name_iter != group_names.end(); ++group_name_iter)
+ {
+ config_group = config_group.group(*group_name_iter);
+ }
+
+ switch (event_type_value)
+ {
+ case event_type::write_entry:
+ config_group.writeEntry(entry_name, entry_value, write_config_flags);
+ break;
+
+ case event_type::write_path_entry:
+ config_group.writePathEntry(entry_name, entry_value, write_config_flags);
+ break;
+
+ case event_type::write_path_array_entry:
+ {
+ QStringList entries_array;
+
+ for (int idx = 1; ; ++idx)
+ {
+ auto iter = message_data.find(QStringLiteral("entry_path_%1").arg(idx));
+ if (iter == message_data.end())
+ {
+ break;
+ }
+
+ entries_array.push_back(iter.value());
+ }
+
+ config_group.writePathEntry(entry_name, entries_array, write_config_flags);
+ }
+ break;
+
+ case event_type::delete_entry:
+ config_group.deleteEntry(entry_name, write_config_flags);
+ break;
+
+ case event_type::delete_group:
+ config_group.deleteGroup(write_config_flags);
+ break;
+ }
+ } while (false);
+
+ in_message = false;
+ message_data.clear();
+ }
+ else
+ {
+ // if '=' character is present and not first character in line
+ int index = line.indexOf(QLatin1Char('='));
+ if (index > 0)
+ {
+ message_data[line.left(index)] = line.mid(index + 1);
+ }
+ }
+ }
+ else
+ {
+ if (line == QStringLiteral("message_begin"))
+ {
+ in_message = true;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/kconfig/src/kconf_watcher/kconf_watcher.cpp b/kconfig/src/kconf_watcher/kconf_watcher.cpp
new file mode 100644
index 0000000..47103f8
--- /dev/null
+++ b/kconfig/src/kconf_watcher/kconf_watcher.cpp
@@ -0,0 +1,135 @@
+/*
+ This file is part of the KDE libraries
+ SPDX-FileCopyrightText: 2020 Aleksei Nikiforov <darktemplar@basealt.ru>
+
+ SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include <QCoreApplication>
+#include <QSocketNotifier>
+#include <QDBusConnection>
+#include <QDBusMessage>
+#include <QStringList>
+#include <QString>
+
+#include "kconf_watcher_p.h"
+
+static int sigintFd[2];
+static int sigtermFd[2];
+
+void intSignalHandler(int)
+{
+ char a = 1;
+ ::write(sigintFd[0], &a, sizeof(a));
+}
+
+void termSignalHandler(int)
+{
+ char a = 1;
+ ::write(sigtermFd[0], &a, sizeof(a));
+}
+
+void qtHandleSigInt(QSocketNotifier ¬ifier)
+{
+ notifier.setEnabled(false);
+
+ char tmp;
+ ::read(sigintFd[1], &tmp, sizeof(tmp));
+
+ QCoreApplication::quit();
+
+ notifier.setEnabled(true);
+}
+
+void qtHandleSigTerm(QSocketNotifier ¬ifier)
+{
+ notifier.setEnabled(false);
+
+ char tmp;
+ ::read(sigtermFd[1], &tmp, sizeof(tmp));
+
+ QCoreApplication::quit();
+
+ notifier.setEnabled(true);
+}
+
+void DBusPrinter::messageSlot(const QStringList &values)
+{
+ static qint64 message_idx = 1;
+ fprintf(stdout, "message %" PRIi64 "\n", message_idx++);
+ fprintf(stdout, "message_begin\n");
+
+ for (const auto &item: values)
+ {
+ fprintf(stdout, "%s\n", item.toLocal8Bit().data());
+ }
+
+ fprintf(stdout, "message_end\n\n");
+ fflush(stdout);
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+ app.setApplicationVersion(QStringLiteral("1.0"));
+
+ if (!QDBusConnection::sessionBus().isConnected())
+ {
+ fprintf(stderr,
+ "Cannot connect to the D-Bus session bus.\n"
+ "Please check your system settings and try again.\n");
+
+ return -1;
+ }
+
+ if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFd))
+ {
+ fprintf(stderr, "Couldn't create INT socketpair\n");
+ return -1;
+ }
+
+ if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigtermFd))
+ {
+ fprintf(stderr, "Couldn't create TERM socketpair\n");
+ return -1;
+ }
+
+ QSocketNotifier sigIntNotifier(sigintFd[1], QSocketNotifier::Read);
+ QObject::connect(&sigIntNotifier, &QSocketNotifier::activated, [&sigIntNotifier] () { qtHandleSigInt(sigIntNotifier); });
+
+ QSocketNotifier sigTermNotifier(sigtermFd[1], QSocketNotifier::Read);
+ QObject::connect(&sigTermNotifier, &QSocketNotifier::activated, [&sigTermNotifier] () { qtHandleSigTerm(sigTermNotifier); });
+
+ {
+ struct sigaction sigaction_int, sigaction_term;
+
+ sigaction_int.sa_handler = &intSignalHandler;
+ sigemptyset(&sigaction_int.sa_mask);
+ sigaction_int.sa_flags = 0;
+ sigaction_int.sa_flags |= SA_RESTART;
+ sigaction(SIGINT, &sigaction_int, 0);
+
+ sigaction_term.sa_handler = &termSignalHandler;
+ sigemptyset(&sigaction_term.sa_mask);
+ sigaction_term.sa_flags = 0;
+ sigaction_term.sa_flags |= SA_RESTART;
+ sigaction(SIGTERM, &sigaction_term, 0);
+ }
+
+ DBusPrinter dbus_printer;
+ QDBusConnection::sessionBus().connect(
+ QString(),
+ QStringLiteral("/"),
+ QStringLiteral("org.alt.kde.kconfig.extra.notify"),
+ QStringLiteral("ConfigChanged"),
+ &dbus_printer,
+ SLOT(messageSlot(QStringList)));
+
+ return app.exec();
+}
diff --git a/kconfig/src/kconf_watcher/kconf_watcher_p.h b/kconfig/src/kconf_watcher/kconf_watcher_p.h
new file mode 100644
index 0000000..cb7d38d
--- /dev/null
+++ b/kconfig/src/kconf_watcher/kconf_watcher_p.h
@@ -0,0 +1,18 @@
+/*
+ This file is part of the KDE libraries
+ SPDX-FileCopyrightText: 2020 Aleksei Nikiforov <darktemplar@basealt.ru>
+
+ SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+#include <QObject>
+
+class QStringList;
+
+class DBusPrinter: public QObject
+{
+ Q_OBJECT
+
+public Q_SLOTS:
+ void messageSlot(const QStringList &values);
+};
--
2.29.3