envmanager: Write options as immutable, and add kdeglobals

This commit writes options as immutable to the config file ([$i]
suffix), so that user defined options from desktop do not override our
specified mobile settings.

This commit also moves kdeglobals settings to be written to
~/.config/plasma-mobile/kdeglobals rather than directly to
~/.config/kdeglobals, continuing the work from
!723

Fixes https://invent.kde.org/plasma/plasma-mobile/-/issues/467
This commit is contained in:
Devin Lin 2025-06-13 07:16:18 -04:00
parent babb8587f8
commit 92a1cfc740
5 changed files with 152 additions and 38 deletions

View file

@ -4,7 +4,7 @@
set(plasma-mobile-envmanager_SRCS set(plasma-mobile-envmanager_SRCS
main.cpp main.cpp
settings.cpp settings.cpp
utils.h utils.cpp
config.h config.h
) )

View file

@ -21,17 +21,13 @@ const QString SAVED_CONFIG_GROUP = u"SavedConfig"_s;
// In bin/startplasmamobile, we add `~/.config/plasma-mobile` to XDG_CONFIG_DIRS to overlay our own configs // In bin/startplasmamobile, we add `~/.config/plasma-mobile` to XDG_CONFIG_DIRS to overlay our own configs
const QString MOBILE_KWINRC_FILE = u"plasma-mobile/kwinrc"_s; const QString MOBILE_KWINRC_FILE = u"plasma-mobile/kwinrc"_s;
const QString MOBILE_KSMSERVERRC_FILE = u"plasma-mobile/ksmserverrc"_s; const QString MOBILE_KSMSERVERRC_FILE = u"plasma-mobile/ksmserverrc"_s;
const QString MOBILE_KDEGLOBALS_FILE = u"plasma-mobile/kdeglobals"_s;
Settings::Settings(QObject *parent) Settings::Settings(QObject *parent)
: QObject{parent} : QObject{parent}
, m_isMobilePlatform{KRuntimePlatform::runtimePlatform().contains(u"phone"_s)} , m_isMobilePlatform{KRuntimePlatform::runtimePlatform().contains(u"phone"_s)}
, m_mobileConfig{KSharedConfig::openConfig(CONFIG_FILE, KConfig::SimpleConfig)} , m_mobileConfig{KSharedConfig::openConfig(CONFIG_FILE, KConfig::SimpleConfig)}
, m_kwinrcConfig{KSharedConfig::openConfig(MOBILE_KWINRC_FILE, KConfig::SimpleConfig)}
, m_appBlacklistConfig{KSharedConfig::openConfig(u"applications-blacklistrc"_s, KConfig::SimpleConfig)} , m_appBlacklistConfig{KSharedConfig::openConfig(u"applications-blacklistrc"_s, KConfig::SimpleConfig)}
, m_kdeglobalsConfig{KSharedConfig::openConfig(u"kdeglobals"_s, KConfig::SimpleConfig)}
, m_ksmServerConfig{KSharedConfig::openConfig(MOBILE_KSMSERVERRC_FILE, KConfig::SimpleConfig)}
, m_originalKwinrcConfig{KSharedConfig::openConfig(u"kwinrc"_s, KConfig::SimpleConfig)}
, m_configWatcher{KConfigWatcher::create(m_mobileConfig)}
{ {
} }
@ -57,18 +53,19 @@ void Settings::applyConfiguration()
void Settings::loadSavedConfiguration() void Settings::loadSavedConfiguration()
{ {
// kwinrc (legacy, we only write in the plasma-mobile/kwinrc file now) // kwinrc (legacy, we only write in the plasma-mobile/kwinrc file now)
loadKeys(u"kwinrc"_s, m_originalKwinrcConfig, getKwinrcSettings(m_mobileConfig)); auto originalKwinrcConfig = KSharedConfig::openConfig(u"kwinrc"_s, KConfig::SimpleConfig);
m_originalKwinrcConfig->sync(); loadKeys(u"kwinrc"_s, originalKwinrcConfig, getKwinrcSettings(m_mobileConfig));
originalKwinrcConfig->sync();
reloadKWinConfig(); reloadKWinConfig();
// applications-blacklistrc // applications-blacklistrc
loadKeys(u"applications-blacklistrc"_s, m_appBlacklistConfig, APPLICATIONS_BLACKLIST_DEFAULT_SETTINGS); loadKeys(u"applications-blacklistrc"_s, m_appBlacklistConfig, APPLICATIONS_BLACKLIST_DEFAULT_SETTINGS);
m_appBlacklistConfig->sync(); m_appBlacklistConfig->sync();
// kdeglobals // kdeglobals (legacy, we only write in the plasma-mobile/kdeglobals file now)
loadKeys(u"kdeglobals"_s, m_kdeglobalsConfig, KDEGLOBALS_DEFAULT_SETTINGS); auto originalKdeglobalsConfig = KSharedConfig::openConfig(u"kdeglobals"_s, KConfig::SimpleConfig);
loadKeys(u"kdeglobals"_s, m_kdeglobalsConfig, KDEGLOBALS_SETTINGS); loadKeys(u"kdeglobals"_s, originalKdeglobalsConfig, KDEGLOBALS_SETTINGS);
m_kdeglobalsConfig->sync(); originalKdeglobalsConfig->sync();
// save our changes // save our changes
m_mobileConfig->sync(); m_mobileConfig->sync();
@ -76,29 +73,50 @@ void Settings::loadSavedConfiguration()
void Settings::applyMobileConfiguration() void Settings::applyMobileConfiguration()
{ {
// kwinrc-plasma-mobile // kwinrc
writeKeys(MOBILE_KWINRC_FILE, m_kwinrcConfig, getKwinrcSettings(m_mobileConfig)); {
m_kwinrcConfig->sync(); auto kwinSettings = getKwinrcSettings(m_mobileConfig);
reloadKWinConfig(); setOptionsImmutable(false, MOBILE_KWINRC_FILE, kwinSettings);
auto kwinrc = kwinrcConfig();
writeKeys(MOBILE_KWINRC_FILE, kwinrc, kwinSettings);
kwinrc->sync();
reloadKWinConfig();
setOptionsImmutable(true, MOBILE_KWINRC_FILE, kwinSettings);
}
// applications-blacklistrc // applications-blacklistrc
writeKeysAndSave(u"applications-blacklistrc"_s, {
m_appBlacklistConfig, writeKeysAndSave(u"applications-blacklistrc"_s,
APPLICATIONS_BLACKLIST_DEFAULT_SETTINGS, m_appBlacklistConfig,
true); // only write entries if they are not already defined in the config APPLICATIONS_BLACKLIST_DEFAULT_SETTINGS,
m_appBlacklistConfig->sync(); true); // only write entries if they are not already defined in the config
m_appBlacklistConfig->sync();
}
// kdeglobals // kdeglobals
writeKeysAndSave(u"kdeglobals"_s, {
m_kdeglobalsConfig, setOptionsImmutable(false, MOBILE_KDEGLOBALS_FILE, KDEGLOBALS_SETTINGS);
KDEGLOBALS_DEFAULT_SETTINGS,
true); // only write entries if they are not already defined in the config auto kdeglobals = KSharedConfig::openConfig(MOBILE_KDEGLOBALS_FILE, KConfig::SimpleConfig);
writeKeysAndSave(u"kdeglobals"_s, m_kdeglobalsConfig, KDEGLOBALS_SETTINGS, false); writeKeys(MOBILE_KDEGLOBALS_FILE, kdeglobals, KDEGLOBALS_DEFAULT_SETTINGS); // only write, don't make immutable
m_kdeglobalsConfig->sync(); writeKeys(MOBILE_KDEGLOBALS_FILE, kdeglobals, KDEGLOBALS_SETTINGS);
kdeglobals->sync();
setOptionsImmutable(true, MOBILE_KDEGLOBALS_FILE, KDEGLOBALS_SETTINGS);
}
// ksmserver // ksmserver
writeKeys(MOBILE_KSMSERVERRC_FILE, m_ksmServerConfig, KSMSERVER_SETTINGS); {
m_ksmServerConfig->sync(); setOptionsImmutable(false, MOBILE_KSMSERVERRC_FILE, KSMSERVER_SETTINGS);
auto ksmserver = KSharedConfig::openConfig(MOBILE_KSMSERVERRC_FILE, KConfig::SimpleConfig);
writeKeys(MOBILE_KSMSERVERRC_FILE, ksmserver, KSMSERVER_SETTINGS);
ksmserver->sync();
setOptionsImmutable(true, MOBILE_KSMSERVERRC_FILE, KSMSERVER_SETTINGS);
}
// save our changes // save our changes
m_mobileConfig->sync(); m_mobileConfig->sync();
@ -197,6 +215,11 @@ const QString Settings::loadSavedConfigSetting(KSharedConfig::Ptr &config, const
return value; return value;
} }
KSharedConfig::Ptr Settings::kwinrcConfig() const
{
return KSharedConfig::openConfig(MOBILE_KWINRC_FILE, KConfig::SimpleConfig);
}
void Settings::reloadKWinConfig() void Settings::reloadKWinConfig()
{ {
// Reload config // Reload config
@ -205,7 +228,7 @@ void Settings::reloadKWinConfig()
// Effects need to manually be loaded/unloaded in a live KWin session. // Effects need to manually be loaded/unloaded in a live KWin session.
KConfigGroup pluginsGroup{m_kwinrcConfig, QStringLiteral("Plugins")}; KConfigGroup pluginsGroup{kwinrcConfig(), QStringLiteral("Plugins")};
for (const auto &effect : KWIN_EFFECTS) { for (const auto &effect : KWIN_EFFECTS) {
// Read from the config whether the effect is enabled (settings are suffixed with "Enabled", ex. blurEnabled) // Read from the config whether the effect is enabled (settings are suffixed with "Enabled", ex. blurEnabled)

View file

@ -36,19 +36,18 @@ private:
void saveConfigSetting(const QString &fileName, const QString &group, const QString &key, const QVariant value); void saveConfigSetting(const QString &fileName, const QString &group, const QString &key, const QVariant value);
const QString loadSavedConfigSetting(KSharedConfig::Ptr &config, const QString &fileName, const QString &group, const QString &key, bool write = true); const QString loadSavedConfigSetting(KSharedConfig::Ptr &config, const QString &fileName, const QString &group, const QString &key, bool write = true);
KSharedConfig::Ptr kwinrcConfig() const;
void reloadKWinConfig(); void reloadKWinConfig();
// whether this is Plasma Mobile // whether this is Plasma Mobile
bool m_isMobilePlatform; bool m_isMobilePlatform;
KSharedConfig::Ptr m_mobileConfig; KSharedConfig::Ptr m_mobileConfig;
KSharedConfig::Ptr m_kwinrcConfig; // (~/.config/kwinrc-plasma-mobile) KSharedConfig::Ptr m_kwinrcConfig; // (~/.config/plasma-mobile/kwinrc)
KSharedConfig::Ptr m_appBlacklistConfig; KSharedConfig::Ptr m_appBlacklistConfig;
KSharedConfig::Ptr m_kdeglobalsConfig; KSharedConfig::Ptr m_kdeglobalsConfig; // (~/.config/plasma-mobile/kdeglobals)
KSharedConfig::Ptr m_ksmServerConfig; KSharedConfig::Ptr m_ksmServerConfig; // (~/.config/plamsma-mobile/ksmserverrc)
// For legacy upgrade purposes (~/.config/kwinrc) // For legacy upgrade purposes
KSharedConfig::Ptr m_originalKwinrcConfig; KSharedConfig::Ptr m_originalKdeglobalsConfig; // (~/.config/kdeglobals)
KConfigWatcher::Ptr m_configWatcher;
}; };

79
envmanager/utils.cpp Normal file
View file

@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "utils.h"
#include <QStandardPaths>
void setOptionsImmutable(bool immutable, const QString &configFilePath, const QMap<QString, QMap<QString, QVariant>> &options)
{
// Find ~/.config/{configFilePath}
QDir basePath{QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)};
QString fullPath = basePath.filePath(configFilePath);
QFile file{fullPath};
if (!file.exists()) {
return;
}
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qCCritical(LOGGING_CATEGORY) << "Unable to read from" << configFilePath << "to change immutability!";
return;
}
QTextStream in(&file);
QStringList lines;
QString configGroup;
// Read file line by line, and add/remove [$i] suffixes from each option
while (!in.atEnd()) {
QString line = in.readLine();
if (line.trimmed().startsWith("//")) {
lines << line;
continue;
}
// Split by first '=' sign
int equalsIndex = line.indexOf('=');
if (equalsIndex == -1) {
lines << line;
// Is it a group?
if (line.startsWith("[") && line.endsWith("]")) {
configGroup = line.mid(1, line.length() - 2);
}
continue;
}
QString key = line.mid(0, equalsIndex);
QString value = line.mid(equalsIndex + 1);
const QString immutableSuffix = "[$i]";
// Remove [$i] key suffix
if (key.endsWith(immutableSuffix)) {
key.chop(immutableSuffix.length());
}
// Add [$i] key suffix, only edit line if it's found in provided options
if (immutable && (options.contains(configGroup) && options[configGroup].contains(key))) {
key += immutableSuffix;
}
lines << (key + "=" + value);
}
file.close();
// Overwrite file with edited lines
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qCCritical(LOGGING_CATEGORY) << "Unable to write to" << configFilePath << "to change immutability!";
return;
}
QTextStream out(&file);
for (const QString &line : std::as_const(lines)) {
out << line << "\n";
}
file.close();
}

View file

@ -1,12 +1,25 @@
// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org> // SPDX-FileCopyrightText: 2023-2025 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <QDir>
#include <QFile>
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QTextStream>
static const QLoggingCategory &LOGGING_CATEGORY() static const QLoggingCategory &LOGGING_CATEGORY()
{ {
static const QLoggingCategory category("plasma-mobile-envmanager"); static const QLoggingCategory category("plasma-mobile-envmanager");
return category; return category;
} }
/**
* Sets each config option in the config file to be immutable or not (appended with [$i])
* See https://api.kde.org/frameworks/kconfig/html/options.html for more details.
*
* @param immutable whether to set options to be immutable, or to remove immutability
* @param configFilePath path to the config file
* @param options the options in the config file to affect (format: <config group, <key, value>>)
*/
void setOptionsImmutable(bool immutable, const QString &configFilePath, const QMap<QString, QMap<QString, QVariant>> &options);