2023-02-23 16:43:38 +00:00
|
|
|
// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
|
2024-12-20 12:59:06 +00:00
|
|
|
// SPDX-FileCopyrightText: 2024 Luis Büchi <luis.buechi@kdemail.net>
|
2023-02-23 16:43:38 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
|
|
|
|
|
#include "settings.h"
|
|
|
|
|
#include "config.h"
|
panels: Add support for defining device specific panel tweaks
This adds support for specifying options needed to deal with phone
display panel pecularities (ex. screen curves, notches, punch holes)
This is implemented as settings in ~/.config/plasmamobilerc, which can
set panel heights, paddings, and center spacings to duck display
cutouts. The pixel values are scaling independent, and so are not
affected when the display scaling is changed.
This is then exposed over DBus, so that components from outside of
plasmashell (ex. KWin) can access it easily without needing to connect to
kscreen themselves. Each screen is exposed as a single object.
Currently support is only added in the status bar and the navigation
panel.
Currently all screens have the settings applied. In the future, we may
want to limit this just to the internal screen (?)
---
This also adds a "devices" folder (in `devices/configs`) where per-device configs can be set.
This is installed to `/usr/share/plasma-mobile-device-configs`.
In `plasmamobilerc` (installed to `/etc/xdg/plasmamobilerc`, or
`~/.config/plasmamobilerc`), envmanager will read:
```toml
[Device]
device=oneplus-enchilada
```
for the device config to use and write its settings to
`~/.config/plasma-mobile/plasmamobilerc`.
2025-10-05 23:06:52 +00:00
|
|
|
#include "devicepresets.h"
|
2023-02-23 16:43:38 +00:00
|
|
|
#include "utils.h"
|
|
|
|
|
|
|
|
|
|
#include <KRuntimePlatform>
|
|
|
|
|
|
|
|
|
|
#include <QDBusConnection>
|
|
|
|
|
#include <QDBusMessage>
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QProcess>
|
|
|
|
|
|
2024-01-17 00:07:31 +00:00
|
|
|
using namespace Qt::Literals::StringLiterals;
|
|
|
|
|
|
|
|
|
|
const QString CONFIG_FILE = u"plasmamobilerc"_s;
|
|
|
|
|
const QString SAVED_CONFIG_GROUP = u"SavedConfig"_s;
|
2023-02-23 16:43:38 +00:00
|
|
|
|
2025-04-24 15:26:11 +00:00
|
|
|
// 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_KSMSERVERRC_FILE = u"plasma-mobile/ksmserverrc"_s;
|
2025-06-13 11:16:18 +00:00
|
|
|
const QString MOBILE_KDEGLOBALS_FILE = u"plasma-mobile/kdeglobals"_s;
|
2025-09-15 20:23:20 +00:00
|
|
|
const QString MOBILE_APPLICATIONS_BLACKLIST_FILE = u"plasma-mobile/applications-blacklistrc"_s;
|
2025-04-24 15:26:11 +00:00
|
|
|
|
2025-10-28 02:21:13 +00:00
|
|
|
const QString DESKTOP_KWINRC_FILE = u"kwinrc"_s;
|
|
|
|
|
|
2023-02-23 16:43:38 +00:00
|
|
|
Settings::Settings(QObject *parent)
|
|
|
|
|
: QObject{parent}
|
2024-01-17 00:07:31 +00:00
|
|
|
, m_isMobilePlatform{KRuntimePlatform::runtimePlatform().contains(u"phone"_s)}
|
2023-03-30 02:40:47 +00:00
|
|
|
, m_mobileConfig{KSharedConfig::openConfig(CONFIG_FILE, KConfig::SimpleConfig)}
|
2023-02-23 16:43:38 +00:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-17 00:02:51 +00:00
|
|
|
Settings &Settings::self()
|
2023-02-23 16:43:38 +00:00
|
|
|
{
|
2024-01-17 00:02:51 +00:00
|
|
|
static Settings settings;
|
2023-02-23 16:43:38 +00:00
|
|
|
return settings;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Settings::applyConfiguration()
|
|
|
|
|
{
|
|
|
|
|
if (!m_isMobilePlatform) {
|
|
|
|
|
qCDebug(LOGGING_CATEGORY) << "Configuration will not be applied, as the session is not Plasma Mobile.";
|
|
|
|
|
loadSavedConfiguration();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qCDebug(LOGGING_CATEGORY) << "Checking and applying mobile configuration...";
|
|
|
|
|
applyMobileConfiguration();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Settings::loadSavedConfiguration()
|
|
|
|
|
{
|
2025-04-24 15:26:11 +00:00
|
|
|
// kwinrc (legacy, we only write in the plasma-mobile/kwinrc file now)
|
2025-10-28 02:21:13 +00:00
|
|
|
auto originalKwinrcConfig = KSharedConfig::openConfig(DESKTOP_KWINRC_FILE, KConfig::SimpleConfig);
|
|
|
|
|
loadKeys(DESKTOP_KWINRC_FILE, originalKwinrcConfig, getKwinrcSettings(m_mobileConfig));
|
2025-06-13 11:16:18 +00:00
|
|
|
originalKwinrcConfig->sync();
|
2025-10-28 02:21:13 +00:00
|
|
|
reloadKWinConfig(originalKwinrcConfig);
|
2023-02-23 16:43:38 +00:00
|
|
|
|
2025-06-13 11:16:18 +00:00
|
|
|
// kdeglobals (legacy, we only write in the plasma-mobile/kdeglobals file now)
|
|
|
|
|
auto originalKdeglobalsConfig = KSharedConfig::openConfig(u"kdeglobals"_s, KConfig::SimpleConfig);
|
|
|
|
|
loadKeys(u"kdeglobals"_s, originalKdeglobalsConfig, KDEGLOBALS_SETTINGS);
|
|
|
|
|
originalKdeglobalsConfig->sync();
|
2023-02-23 16:43:38 +00:00
|
|
|
|
|
|
|
|
// save our changes
|
2023-03-30 02:40:47 +00:00
|
|
|
m_mobileConfig->sync();
|
2023-02-23 16:43:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Settings::applyMobileConfiguration()
|
|
|
|
|
{
|
2025-06-13 11:16:18 +00:00
|
|
|
// kwinrc
|
|
|
|
|
{
|
|
|
|
|
auto kwinSettings = getKwinrcSettings(m_mobileConfig);
|
|
|
|
|
setOptionsImmutable(false, MOBILE_KWINRC_FILE, kwinSettings);
|
|
|
|
|
|
2025-10-28 02:21:13 +00:00
|
|
|
auto kwinrc = KSharedConfig::openConfig(MOBILE_KWINRC_FILE, KConfig::SimpleConfig);
|
2025-06-13 11:16:18 +00:00
|
|
|
writeKeys(MOBILE_KWINRC_FILE, kwinrc, kwinSettings);
|
2025-07-02 04:45:02 +00:00
|
|
|
writeKeys(MOBILE_KWINRC_FILE, kwinrc, KWINRC_DEFAULT_SETTINGS); // only write, don't make immutable
|
2025-06-13 11:16:18 +00:00
|
|
|
kwinrc->sync();
|
2025-10-28 02:21:13 +00:00
|
|
|
reloadKWinConfig(kwinrc);
|
2025-06-13 11:16:18 +00:00
|
|
|
|
|
|
|
|
setOptionsImmutable(true, MOBILE_KWINRC_FILE, kwinSettings);
|
|
|
|
|
}
|
2023-02-23 16:43:38 +00:00
|
|
|
|
|
|
|
|
// applications-blacklistrc
|
2025-06-13 11:16:18 +00:00
|
|
|
{
|
2025-09-15 20:23:20 +00:00
|
|
|
// We don't set these options as immutable
|
|
|
|
|
auto appBlacklistConfig = KSharedConfig::openConfig(MOBILE_APPLICATIONS_BLACKLIST_FILE, KConfig::SimpleConfig);
|
|
|
|
|
writeKeys(MOBILE_APPLICATIONS_BLACKLIST_FILE, appBlacklistConfig, APPLICATIONS_BLACKLIST_DEFAULT_SETTINGS);
|
|
|
|
|
appBlacklistConfig->sync();
|
2025-06-13 11:16:18 +00:00
|
|
|
}
|
2023-02-23 16:43:38 +00:00
|
|
|
|
|
|
|
|
// kdeglobals
|
2025-06-13 11:16:18 +00:00
|
|
|
{
|
|
|
|
|
setOptionsImmutable(false, MOBILE_KDEGLOBALS_FILE, KDEGLOBALS_SETTINGS);
|
|
|
|
|
|
|
|
|
|
auto kdeglobals = KSharedConfig::openConfig(MOBILE_KDEGLOBALS_FILE, KConfig::SimpleConfig);
|
|
|
|
|
writeKeys(MOBILE_KDEGLOBALS_FILE, kdeglobals, KDEGLOBALS_DEFAULT_SETTINGS); // only write, don't make immutable
|
|
|
|
|
writeKeys(MOBILE_KDEGLOBALS_FILE, kdeglobals, KDEGLOBALS_SETTINGS);
|
|
|
|
|
kdeglobals->sync();
|
|
|
|
|
|
|
|
|
|
setOptionsImmutable(true, MOBILE_KDEGLOBALS_FILE, KDEGLOBALS_SETTINGS);
|
|
|
|
|
}
|
2023-02-23 16:43:38 +00:00
|
|
|
|
2024-12-20 12:59:06 +00:00
|
|
|
// ksmserver
|
2025-06-13 11:16:18 +00:00
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
2024-12-20 12:59:06 +00:00
|
|
|
|
panels: Add support for defining device specific panel tweaks
This adds support for specifying options needed to deal with phone
display panel pecularities (ex. screen curves, notches, punch holes)
This is implemented as settings in ~/.config/plasmamobilerc, which can
set panel heights, paddings, and center spacings to duck display
cutouts. The pixel values are scaling independent, and so are not
affected when the display scaling is changed.
This is then exposed over DBus, so that components from outside of
plasmashell (ex. KWin) can access it easily without needing to connect to
kscreen themselves. Each screen is exposed as a single object.
Currently support is only added in the status bar and the navigation
panel.
Currently all screens have the settings applied. In the future, we may
want to limit this just to the internal screen (?)
---
This also adds a "devices" folder (in `devices/configs`) where per-device configs can be set.
This is installed to `/usr/share/plasma-mobile-device-configs`.
In `plasmamobilerc` (installed to `/etc/xdg/plasmamobilerc`, or
`~/.config/plasmamobilerc`), envmanager will read:
```toml
[Device]
device=oneplus-enchilada
```
for the device config to use and write its settings to
`~/.config/plasma-mobile/plasmamobilerc`.
2025-10-05 23:06:52 +00:00
|
|
|
// Save our changes
|
2023-03-30 02:40:47 +00:00
|
|
|
m_mobileConfig->sync();
|
panels: Add support for defining device specific panel tweaks
This adds support for specifying options needed to deal with phone
display panel pecularities (ex. screen curves, notches, punch holes)
This is implemented as settings in ~/.config/plasmamobilerc, which can
set panel heights, paddings, and center spacings to duck display
cutouts. The pixel values are scaling independent, and so are not
affected when the display scaling is changed.
This is then exposed over DBus, so that components from outside of
plasmashell (ex. KWin) can access it easily without needing to connect to
kscreen themselves. Each screen is exposed as a single object.
Currently support is only added in the status bar and the navigation
panel.
Currently all screens have the settings applied. In the future, we may
want to limit this just to the internal screen (?)
---
This also adds a "devices" folder (in `devices/configs`) where per-device configs can be set.
This is installed to `/usr/share/plasma-mobile-device-configs`.
In `plasmamobilerc` (installed to `/etc/xdg/plasmamobilerc`, or
`~/.config/plasmamobilerc`), envmanager will read:
```toml
[Device]
device=oneplus-enchilada
```
for the device config to use and write its settings to
`~/.config/plasma-mobile/plasmamobilerc`.
2025-10-05 23:06:52 +00:00
|
|
|
|
|
|
|
|
// Setup device configs
|
|
|
|
|
DevicePresets devicePresets;
|
|
|
|
|
devicePresets.initialize();
|
2023-02-23 16:43:38 +00:00
|
|
|
}
|
|
|
|
|
|
2025-04-24 15:26:11 +00:00
|
|
|
void Settings::writeKeys(const QString &fileName, KSharedConfig::Ptr &config, const QMap<QString, QMap<QString, QVariant>> &settings)
|
|
|
|
|
{
|
|
|
|
|
const auto groupNames = settings.keys();
|
|
|
|
|
for (const auto &groupName : groupNames) {
|
|
|
|
|
auto group = KConfigGroup{config, groupName};
|
|
|
|
|
|
|
|
|
|
const auto keys = settings[groupName].keys();
|
|
|
|
|
for (const auto &key : keys) {
|
|
|
|
|
group.writeEntry(key, settings[groupName][key], KConfigGroup::Notify);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-23 16:43:38 +00:00
|
|
|
void Settings::loadKeys(const QString &fileName, KSharedConfig::Ptr &config, const QMap<QString, QMap<QString, QVariant>> &settings)
|
|
|
|
|
{
|
2024-01-22 15:33:41 +00:00
|
|
|
const auto groupNames = settings.keys();
|
|
|
|
|
for (const auto &groupName : groupNames) {
|
|
|
|
|
const auto group = KConfigGroup{config, groupName};
|
2023-02-23 16:43:38 +00:00
|
|
|
|
2024-01-22 15:33:41 +00:00
|
|
|
const auto keys = settings[groupName].keys();
|
|
|
|
|
for (const auto &key : keys) {
|
2023-02-23 16:43:38 +00:00
|
|
|
loadSavedConfigSetting(config, fileName, groupName, key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NOTE: this only saves a value if it hasn't already been saved
|
|
|
|
|
void Settings::saveConfigSetting(const QString &fileName, const QString &group, const QString &key, const QVariant value)
|
|
|
|
|
{
|
2024-06-26 17:15:57 +00:00
|
|
|
// These are not const because we are writing an entry
|
|
|
|
|
auto savedGroup = KConfigGroup{m_mobileConfig, SAVED_CONFIG_GROUP};
|
|
|
|
|
auto fileGroup = KConfigGroup{&savedGroup, fileName};
|
2023-02-23 16:43:38 +00:00
|
|
|
auto keyGroup = KConfigGroup{&fileGroup, group};
|
|
|
|
|
|
|
|
|
|
if (!keyGroup.hasKey(key)) {
|
2023-12-09 03:09:26 +00:00
|
|
|
qCDebug(LOGGING_CATEGORY) << "In" << fileName << "saved" << key << "=" << value;
|
2023-02-23 16:43:38 +00:00
|
|
|
keyGroup.writeEntry(key, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NOTE: this deletes the stored value from the config after loading
|
2023-03-30 05:43:44 +00:00
|
|
|
const QString Settings::loadSavedConfigSetting(KSharedConfig::Ptr &config, const QString &fileName, const QString &group, const QString &key, bool write)
|
2023-02-23 16:43:38 +00:00
|
|
|
{
|
2024-01-20 23:18:13 +00:00
|
|
|
auto savedGroup = KConfigGroup{m_mobileConfig, SAVED_CONFIG_GROUP};
|
|
|
|
|
auto fileGroup = KConfigGroup{&savedGroup, fileName};
|
2023-02-23 16:43:38 +00:00
|
|
|
auto keyGroup = KConfigGroup{&fileGroup, group};
|
|
|
|
|
|
|
|
|
|
if (!keyGroup.hasKey(key)) {
|
2023-03-30 05:43:44 +00:00
|
|
|
return {};
|
2023-02-23 16:43:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto value = keyGroup.readEntry(key);
|
|
|
|
|
|
|
|
|
|
// write to real config
|
|
|
|
|
auto configGroup = KConfigGroup{config, group};
|
|
|
|
|
|
2023-03-30 05:43:44 +00:00
|
|
|
if ((!configGroup.hasKey(key) || configGroup.readEntry(key) != value) && write) {
|
2024-01-22 15:33:41 +00:00
|
|
|
qCDebug(LOGGING_CATEGORY) << "In" << fileName << "loading saved value of" << key << "which is" << value << "in" << group;
|
2023-02-23 16:43:38 +00:00
|
|
|
|
|
|
|
|
if (value.isEmpty()) { // delete blank entries!
|
|
|
|
|
configGroup.deleteEntry(key);
|
|
|
|
|
} else {
|
|
|
|
|
configGroup.writeEntry(key, value, KConfigGroup::Notify);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remove saved config option
|
2023-03-30 15:42:28 +00:00
|
|
|
keyGroup.deleteEntry(key);
|
2023-03-30 05:43:44 +00:00
|
|
|
return value;
|
2023-02-23 16:43:38 +00:00
|
|
|
}
|
|
|
|
|
|
2025-10-28 02:21:13 +00:00
|
|
|
void Settings::reloadKWinConfig(KSharedConfig::Ptr kwinrc)
|
2023-02-23 16:43:38 +00:00
|
|
|
{
|
2024-11-14 17:18:23 +00:00
|
|
|
// Reload config
|
|
|
|
|
QDBusMessage reloadMessage = QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
|
|
|
|
|
QDBusConnection::sessionBus().send(reloadMessage);
|
|
|
|
|
|
|
|
|
|
// Effects need to manually be loaded/unloaded in a live KWin session.
|
2024-10-31 06:32:30 +00:00
|
|
|
|
2025-10-28 02:21:13 +00:00
|
|
|
KConfigGroup pluginsGroup{kwinrc, QStringLiteral("Plugins")};
|
2024-10-31 06:32:30 +00:00
|
|
|
|
|
|
|
|
for (const auto &effect : KWIN_EFFECTS) {
|
|
|
|
|
// Read from the config whether the effect is enabled (settings are suffixed with "Enabled", ex. blurEnabled)
|
|
|
|
|
bool status = pluginsGroup.readEntry(effect + u"Enabled"_s, false);
|
|
|
|
|
const QString method = status ? u"loadEffect"_s : u"unloadEffect"_s;
|
|
|
|
|
|
|
|
|
|
QDBusMessage message = QDBusMessage::createMethodCall(u"org.kde.KWin"_s, u"/Effects"_s, u"org.kde.kwin.Effects"_s, method);
|
|
|
|
|
message.setArguments({effect});
|
|
|
|
|
QDBusConnection::sessionBus().send(message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unload KWin scripts that are now disabled.
|
|
|
|
|
for (const auto &script : KWIN_SCRIPTS) {
|
|
|
|
|
// Read from the config whether the effect is enabled (settings are suffixed with "Enabled", ex. blurEnabled)
|
|
|
|
|
bool status = pluginsGroup.readEntry(script + u"Enabled"_s, false);
|
|
|
|
|
|
|
|
|
|
if (!status) {
|
|
|
|
|
QDBusMessage message = QDBusMessage::createMethodCall(u"org.kde.KWin"_s, u"/Scripting"_s, u"org.kde.kwin.Scripting"_s, u"unloadScript"_s);
|
|
|
|
|
message.setArguments({script});
|
|
|
|
|
QDBusConnection::sessionBus().send(message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Call "start" to load enabled KWin scripts.
|
2025-04-24 09:28:49 +00:00
|
|
|
QDBusMessage message = QDBusMessage::createMethodCall(u"org.kde.KWin"_s, u"/Scripting"_s, u"org.kde.kwin.Scripting"_s, u"start"_s);
|
|
|
|
|
QDBusConnection::sessionBus().send(message);
|
2025-04-24 15:26:11 +00:00
|
|
|
|
|
|
|
|
// Call reconfigure
|
|
|
|
|
QDBusMessage reconfigureMessage = QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reconfigure");
|
|
|
|
|
QDBusConnection::sessionBus().send(reconfigureMessage);
|
2023-02-23 16:43:38 +00:00
|
|
|
}
|