File indexing completed on 2024-04-28 05:36:16

0001 /*
0002  *  SPDX-FileCopyrightText: Copyright 2023 Jakob Petsovits <jpetso@petsovits.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "powerdevilmigrateconfig.h"
0008 
0009 #include "powerdevilenums.h"
0010 
0011 #include <PowerDevilActivitySettings.h>
0012 #include <PowerDevilProfileSettings.h>
0013 #include <powerdevil_version.h>
0014 
0015 #include <KConfigGroup>
0016 #include <KSharedConfig>
0017 
0018 #include <algorithm> // std::min, std::max
0019 #include <functional> // std::invoke
0020 #include <type_traits> // std::remove_cvref_t
0021 
0022 namespace
0023 {
0024 
0025 template<typename T>
0026 struct IdentityTransformer {
0027     std::remove_cvref_t<T> operator()(T t)
0028     {
0029         return std::move(t);
0030     }
0031 };
0032 
0033 } // anonymous namespace
0034 
0035 namespace PowerDevil
0036 {
0037 
0038 void migrateActivitiesConfig(KSharedConfig::Ptr profilesConfig)
0039 {
0040     KConfigGroup migrationGroup = profilesConfig->group(QStringLiteral("Migration"));
0041     if (migrationGroup.hasKey("MigratedActivitiesToPlasma6")) {
0042         return;
0043     }
0044 
0045     // Activity special behavior config written via ActivitySettings, reading must be done manually.
0046     const KConfigGroup oldActivitiesGroup = profilesConfig->group(QStringLiteral("Activities"));
0047     if (!oldActivitiesGroup.exists()) {
0048         return;
0049     }
0050 
0051     // Every activity has its own group identified by its UUID.
0052     for (const QString &activityId : oldActivitiesGroup.groupList()) {
0053         const KConfigGroup oldConfig = oldActivitiesGroup.group(activityId);
0054         PowerDevil::ActivitySettings newConfig(activityId);
0055 
0056         if (oldConfig.readEntry("mode", "None") == "SpecialBehavior") {
0057             if (const KConfigGroup oldSB = oldConfig.group(QStringLiteral("SpecialBehavior")); oldSB.exists()) {
0058                 newConfig.setInhibitScreenManagement(oldSB.readEntry("noScreenManagement", false));
0059                 newConfig.setInhibitSuspend(oldSB.readEntry("noSuspend", false));
0060             }
0061         }
0062         newConfig.save();
0063     }
0064 
0065     migrationGroup.writeEntry("MigratedActivitiesToPlasma6", "powerdevilrc");
0066     profilesConfig->sync();
0067 }
0068 
0069 void ensureLockScreenIdleTimeoutInKScreenLockerKCM(int seconds)
0070 {
0071     KSharedConfig::Ptr lockerConfig = KSharedConfig::openConfig(QStringLiteral("kscreenlockerrc"));
0072     KConfigGroup group = lockerConfig->group(QStringLiteral("Daemon"));
0073     if (group.readEntry("Autolock", true) == false) {
0074         group.deleteEntry("Autolock"); // reset to default true value
0075         group.writeEntry("Timeout", std::max(1, std::min(group.readEntry("Timeout", 5), seconds / 60)));
0076     }
0077     lockerConfig->sync();
0078 }
0079 
0080 void migrateProfilesConfig(KSharedConfig::Ptr profilesConfig, bool isMobile, bool isVM, bool canSuspend)
0081 {
0082     KConfigGroup migrationGroup = profilesConfig->group(QStringLiteral("Migration"));
0083     if (migrationGroup.hasKey(QStringLiteral("MigratedProfilesToPlasma6"))) {
0084         return;
0085     }
0086 
0087     for (const auto &profileName : {QStringLiteral("AC"), QStringLiteral("Battery"), QStringLiteral("LowBattery")}) {
0088         const KConfigGroup oldProfileGroup = profilesConfig->group(profileName);
0089         if (!oldProfileGroup.exists()) {
0090             continue;
0091         }
0092         PowerDevil::ProfileSettings profileSettings(profileName, isMobile, isVM, canSuspend);
0093 
0094         auto migrateEntry = [&]<typename T, typename Transformer = IdentityTransformer<T>>(KConfigGroup & oldGroup,
0095                                                                                            const QString &oldKey,
0096                                                                                            void (ProfileSettings::*setter)(T),
0097                                                                                            Transformer transform = IdentityTransformer<T>())
0098         {
0099             if (!oldGroup.hasKey(oldKey)) {
0100                 return;
0101             }
0102             // We know oldKey exists, so use any T value as default because we don't care what's in it
0103             T newValue = std::invoke(transform, oldGroup.readEntry(oldKey, std::remove_cvref_t<T>{}));
0104             (profileSettings.*setter)(newValue);
0105         };
0106 
0107         if (KConfigGroup group = oldProfileGroup.group(QStringLiteral("KeyboardBrightnessControl")); group.exists()) {
0108             profileSettings.setUseProfileSpecificKeyboardBrightness(true);
0109             migrateEntry(group, "value", &ProfileSettings::setKeyboardBrightness);
0110         } else {
0111             profileSettings.setUseProfileSpecificKeyboardBrightness(false);
0112         }
0113 
0114         if (KConfigGroup group = oldProfileGroup.group(QStringLiteral("BrightnessControl")); group.exists()) {
0115             profileSettings.setUseProfileSpecificDisplayBrightness(true);
0116             migrateEntry(group, "value", &ProfileSettings::setDisplayBrightness);
0117         } else {
0118             profileSettings.setUseProfileSpecificDisplayBrightness(false);
0119         }
0120 
0121         if (KConfigGroup group = oldProfileGroup.group(QStringLiteral("DimDisplay")); group.exists()) {
0122             profileSettings.setDimDisplayWhenIdle(true);
0123             migrateEntry(group, "idleTime", &ProfileSettings::setDimDisplayIdleTimeoutSec, [](int oldMsec) {
0124                 return oldMsec / 1000; // standarize on using seconds, see powerdevil issue #3 on KDE Invent
0125             });
0126         } else {
0127             profileSettings.setDimDisplayWhenIdle(false);
0128         }
0129 
0130         if (KConfigGroup group = oldProfileGroup.group(QStringLiteral("DPMSControl")); group.exists()) {
0131             profileSettings.setTurnOffDisplayWhenIdle(true);
0132             // The "DPMSControl" group used seconds for "idleTime". Unlike other groups which
0133             // used milliseconds in Plasma 5, this one doesn't need division by 1000.
0134             migrateEntry(group, "idleTime", &ProfileSettings::setTurnOffDisplayIdleTimeoutSec);
0135             migrateEntry(group, "idleTimeoutWhenLocked", &ProfileSettings::setTurnOffDisplayIdleTimeoutWhenLockedSec);
0136             migrateEntry(group, "lockBeforeTurnOff", &ProfileSettings::setLockBeforeTurnOffDisplay);
0137         } else {
0138             profileSettings.setTurnOffDisplayWhenIdle(false);
0139         }
0140 
0141         bool suspendThenHibernate = false;
0142         bool hybridSuspend = false;
0143         bool migrateLockScreenIdleTimeout = false;
0144 
0145         if (KConfigGroup group = oldProfileGroup.group(QStringLiteral("SuspendSession")); group.exists()) {
0146             if (group.readEntry("suspendThenHibernate", false)) {
0147                 suspendThenHibernate = true;
0148             }
0149             migrateEntry(group, "suspendType", &ProfileSettings::setAutoSuspendAction, [&](uint oldAction) {
0150                 if (oldAction == 4 /* the old PowerButtonAction::SuspendHybrid */) {
0151                     hybridSuspend = true;
0152                     return qToUnderlying(PowerButtonAction::Sleep);
0153                 }
0154                 if (oldAction == qToUnderlying(PowerButtonAction::LockScreen)) {
0155                     migrateLockScreenIdleTimeout = true; // see bottom of function
0156                     return qToUnderlying(PowerButtonAction::NoAction);
0157                 }
0158                 return oldAction;
0159             });
0160             migrateEntry(group, "idleTime", &ProfileSettings::setAutoSuspendIdleTimeoutSec, [](int oldMsec) {
0161                 return oldMsec / 1000; // standarize on using seconds, see powerdevil issue #3 on KDE Invent
0162             });
0163             // Note: setSleepMode() is called at the end, completing this section.
0164         } else {
0165             profileSettings.setAutoSuspendAction(qToUnderlying(PowerButtonAction::NoAction));
0166         }
0167 
0168         if (KConfigGroup group = oldProfileGroup.group(QStringLiteral("HandleButtonEvents")); group.exists()) {
0169             migrateEntry(group, "powerButtonAction", &ProfileSettings::setPowerButtonAction, [&](uint oldAction) {
0170                 if (oldAction == 4 /* the old PowerButtonAction::SuspendHybrid */) {
0171                     hybridSuspend = true;
0172                     return qToUnderlying(PowerButtonAction::Sleep);
0173                 }
0174                 return oldAction;
0175             });
0176             migrateEntry(group, "powerDownAction", &ProfileSettings::setPowerDownAction, [&](uint oldAction) {
0177                 if (oldAction == 4 /* the old PowerButtonAction::SuspendHybrid */) {
0178                     hybridSuspend = true;
0179                     return qToUnderlying(PowerButtonAction::Sleep);
0180                 }
0181                 return oldAction;
0182             });
0183             migrateEntry(group, "lidAction", &ProfileSettings::setLidAction, [&](uint oldAction) {
0184                 if (oldAction == 4 /* the old PowerButtonAction::SuspendHybrid */) {
0185                     hybridSuspend = true;
0186                     return qToUnderlying(PowerButtonAction::Sleep);
0187                 }
0188                 return oldAction;
0189             });
0190             migrateEntry(group,
0191                          "triggerLidActionWhenExternalMonitorPresent",
0192                          &ProfileSettings::setInhibitLidActionWhenExternalMonitorPresent,
0193                          [](bool shouldTrigger) {
0194                              return !shouldTrigger;
0195                          });
0196         } else {
0197             profileSettings.setPowerButtonAction(qToUnderlying(PowerButtonAction::NoAction));
0198             profileSettings.setPowerDownAction(qToUnderlying(PowerButtonAction::NoAction));
0199             profileSettings.setLidAction(qToUnderlying(PowerButtonAction::NoAction));
0200         }
0201 
0202         if (KConfigGroup group = oldProfileGroup.group(QStringLiteral("PowerProfile")); group.exists()) {
0203             migrateEntry(group, "profile", &ProfileSettings::setPowerProfile);
0204         }
0205 
0206         if (KConfigGroup group = oldProfileGroup.group(QStringLiteral("RunScript")); group.exists()) {
0207             switch (group.readEntry("scriptPhase", 0)) {
0208             case 0: // on profile load
0209                 migrateEntry(group, "scriptCommand", &ProfileSettings::setProfileLoadCommand);
0210                 break;
0211             case 1: // on profile unload
0212                 migrateEntry(group, "scriptCommand", &ProfileSettings::setProfileUnloadCommand);
0213                 break;
0214             case 2: // on idle timeout
0215                 migrateEntry(group, "scriptCommand", &ProfileSettings::setIdleTimeoutCommand);
0216                 break;
0217             }
0218             migrateEntry(group, "idleTime", &ProfileSettings::setRunScriptIdleTimeoutSec, [](int oldMsec) {
0219                 return oldMsec / 1000; // standarize on using seconds, see powerdevil issue #3 on KDE Invent
0220             });
0221         }
0222 
0223         if (suspendThenHibernate) {
0224             profileSettings.setSleepMode(qToUnderlying(SleepMode::SuspendThenHibernate));
0225         } else if (hybridSuspend) {
0226             profileSettings.setSleepMode(qToUnderlying(SleepMode::HybridSuspend));
0227         }
0228 
0229         if (migrateLockScreenIdleTimeout) {
0230             // We had two different settings for this, ditch this and only keep the one in kscreenlockerrc
0231             ensureLockScreenIdleTimeoutInKScreenLockerKCM(profileSettings.autoSuspendIdleTimeoutSec());
0232         }
0233 
0234         profileSettings.save();
0235     }
0236 
0237     migrationGroup.writeEntry("MigratedProfilesToPlasma6", "powerdevilrc");
0238     profilesConfig->sync();
0239 }
0240 
0241 void migrateConfig(bool isMobile, bool isVM, bool canSuspend)
0242 {
0243     KSharedConfig::Ptr profilesConfig = KSharedConfig::openConfig(QStringLiteral("powermanagementprofilesrc"));
0244     // TODO KF7 (or perhaps earlier): Remove Plasma 5->6 migration code and delete powermanagementprofilesrc
0245 
0246     migrateActivitiesConfig(profilesConfig);
0247     migrateProfilesConfig(profilesConfig, isMobile, isVM, canSuspend);
0248 
0249 #if POWERDEVIL_VERSION < QT_VERSION_CHECK(6, 0, 0)
0250     // quick-fixes for configs migrated prior to Plasma 6 RC1 (which we tweaked before final release)
0251     KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(QStringLiteral("powerdevilrc"));
0252     KConfigGroup batteryManagementGroup = globalConfig->group(QStringLiteral("BatteryManagement"));
0253     if (batteryManagementGroup.readEntry("BatteryCriticalAction", 0) == 4 /* the old PowerButtonAction::SuspendHybrid */) {
0254         batteryManagementGroup.writeEntry("BatteryCriticalAction", qToUnderlying(PowerButtonAction::Hibernate));
0255         globalConfig->sync();
0256     }
0257     for (const auto &profileName : {QStringLiteral("AC"), QStringLiteral("Battery"), QStringLiteral("LowBattery")}) {
0258         PowerDevil::ProfileSettings profileSettings(profileName, isMobile, isVM, canSuspend);
0259         if (profileSettings.autoSuspendAction() == qToUnderlying(PowerButtonAction::LockScreen)) {
0260             profileSettings.setAutoSuspendAction(qToUnderlying(PowerButtonAction::NoAction));
0261             ensureLockScreenIdleTimeoutInKScreenLockerKCM(profileSettings.autoSuspendIdleTimeoutSec());
0262             profileSettings.save();
0263         }
0264     }
0265 #endif
0266 }
0267 
0268 } // namespace PowerDevil