File indexing completed on 2024-05-05 17:45:02

0001 /*
0002     SPDX-FileCopyrightText: 2008 Dario Freddi <drf@kdemod.ath.cx>
0003     SPDX-FileCopyrightText: 2008 Sebastian Kügler <sebas@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "PowerDevilRunner.h"
0009 
0010 // kde-workspace/libs
0011 #include <sessionmanagement.h>
0012 
0013 #include <QDBusConnectionInterface>
0014 #include <QDBusInterface>
0015 #include <QDBusReply>
0016 #include <QDebug>
0017 #include <QIcon>
0018 
0019 #include <KLocalizedString>
0020 #include <KSharedConfig>
0021 
0022 #include <cmath>
0023 
0024 K_PLUGIN_CLASS_WITH_JSON(PowerDevilRunner, "plasma-runner-powerdevil.json")
0025 
0026 PowerDevilRunner::PowerDevilRunner(QObject *parent, const KPluginMetaData &metaData, const QVariantList &args)
0027     : Plasma::AbstractRunner(parent, metaData, args)
0028     , m_session(new SessionManagement(this))
0029 {
0030     setObjectName(QStringLiteral("PowerDevil"));
0031     setMinLetterCount(3);
0032     const KLocalizedString suspend = ki18nc("Note this is a KRunner keyword", "suspend");
0033     m_suspend = RunnerKeyword{suspend.untranslatedText(), suspend.toString()};
0034     const KLocalizedString toRam = ki18nc("Note this is a KRunner keyword", "to ram");
0035     m_toRam = RunnerKeyword{toRam.untranslatedText(), toRam.toString(), false};
0036     const KLocalizedString sleep = ki18nc("Note this is a KRunner keyword", "sleep");
0037     m_sleep = RunnerKeyword{sleep.untranslatedText(), sleep.toString()};
0038     const KLocalizedString hibernate = ki18nc("Note this is a KRunner keyword", "hibernate");
0039     m_hibernate = RunnerKeyword{hibernate.untranslatedText(), hibernate.toString()};
0040     const KLocalizedString toDisk = ki18nc("Note this is a KRunner keyword", "to disk");
0041     m_toDisk = RunnerKeyword{toDisk.untranslatedText(), toDisk.toString(), false};
0042     const KLocalizedString dimScreen = ki18nc("Note this is a KRunner keyword", "dim screen");
0043     m_dimScreen = RunnerKeyword{dimScreen.untranslatedText(), dimScreen.toString()};
0044     const KLocalizedString screenBrightness = ki18nc("Note this is a KRunner keyword", "dim screen");
0045     m_screenBrightness = RunnerKeyword{screenBrightness.untranslatedText(), screenBrightness.toString()};
0046     updateStatus();
0047 }
0048 
0049 void PowerDevilRunner::updateSyntaxes()
0050 {
0051     setSyntaxes({}); // Clear the existing ones
0052     addSyntaxForKeyword({m_suspend},
0053                         i18n("Lists system suspend (e.g. sleep, hibernate) options "
0054                              "and allows them to be activated"));
0055 
0056     if (m_session->canSuspend()) {
0057         addSyntaxForKeyword({m_sleep, m_toRam}, i18n("Suspends the system to RAM"));
0058     }
0059 
0060     if (m_session->canHibernate()) {
0061         addSyntaxForKeyword({m_hibernate, m_toDisk}, i18n("Suspends the system to disk"));
0062     }
0063 
0064     Plasma::RunnerSyntax brightnessSyntax(
0065         i18nc("Note this is a KRunner keyword, <> is a placeholder and should be at the end", "screen brightness <percent value>"),
0066         // xgettext:no-c-format
0067         i18n("Lists screen brightness options or sets it to the brightness defined by the search term; "
0068              "e.g. screen brightness 50 would dim the screen to 50% maximum brightness"));
0069     brightnessSyntax.addExampleQuery(i18nc("Note this is a KRunner keyword", "dim screen"));
0070     addSyntax(brightnessSyntax);
0071 }
0072 
0073 PowerDevilRunner::~PowerDevilRunner()
0074 {
0075 }
0076 
0077 void PowerDevilRunner::updateStatus()
0078 {
0079     updateSyntaxes();
0080 }
0081 
0082 enum SleepState { StandbyState = 1, SuspendState = 2, HibernateState = 4, HybridSuspendState = 8 };
0083 
0084 void PowerDevilRunner::match(Plasma::RunnerContext &context)
0085 {
0086     const QString term = context.query();
0087     Plasma::QueryMatch::Type type = Plasma::QueryMatch::ExactMatch;
0088     QList<Plasma::QueryMatch> matches;
0089 
0090     int screenBrightnessResults = matchesScreenBrightnessKeywords(term);
0091     if (screenBrightnessResults != -1) {
0092         Plasma::QueryMatch match(this);
0093         match.setType(type);
0094         match.setIconName(QStringLiteral("preferences-system-power-management"));
0095         match.setText(i18n("Set Brightness to %1%", screenBrightnessResults));
0096         match.setData(screenBrightnessResults);
0097         match.setRelevance(1);
0098         match.setId(QStringLiteral("BrightnessChange"));
0099         matches.append(match);
0100     } else if (matchesRunnerKeywords({m_screenBrightness, m_dimScreen}, type, term)) {
0101         Plasma::QueryMatch match1(this);
0102         match1.setType(Plasma::QueryMatch::ExactMatch);
0103         match1.setIconName(QStringLiteral("preferences-system-power-management"));
0104         match1.setText(i18n("Dim screen totally"));
0105         match1.setRelevance(1);
0106         match1.setId(QStringLiteral("DimTotal"));
0107         matches.append(match1);
0108 
0109         Plasma::QueryMatch match2(this);
0110         match2.setType(type);
0111         match2.setIconName(QStringLiteral("preferences-system-power-management"));
0112         match2.setText(i18n("Dim screen by half"));
0113         match2.setRelevance(1);
0114         match2.setId(QStringLiteral("DimHalf"));
0115         matches.append(match2);
0116     } else if (matchesRunnerKeywords({m_sleep}, type, term)) {
0117         if (m_session->canSuspend()) {
0118             addSuspendMatch(SuspendState, matches, type);
0119         }
0120 
0121         if (m_session->canHibernate()) {
0122             addSuspendMatch(HibernateState, matches, type);
0123         }
0124     } else if (matchesRunnerKeywords({m_suspend, m_toRam}, type, term)) {
0125         addSuspendMatch(SuspendState, matches, type);
0126     } else if (matchesRunnerKeywords({m_hibernate, m_toDisk}, type, term)) {
0127         addSuspendMatch(HibernateState, matches, type);
0128     }
0129 
0130     context.addMatches(matches);
0131 }
0132 
0133 void PowerDevilRunner::addSuspendMatch(int value, QList<Plasma::QueryMatch> &matches, Plasma::QueryMatch::Type type)
0134 {
0135     Plasma::QueryMatch match(this);
0136     match.setType(type);
0137 
0138     switch ((SleepState)value) {
0139     case SuspendState:
0140     case StandbyState:
0141         match.setIconName(QStringLiteral("system-suspend"));
0142         match.setText(i18nc("Suspend to RAM", "Sleep"));
0143         match.setSubtext(i18n("Suspend to RAM"));
0144         match.setRelevance(1);
0145         break;
0146     case HibernateState:
0147         match.setIconName(QStringLiteral("system-suspend-hibernate"));
0148         match.setText(i18nc("Suspend to disk", "Hibernate"));
0149         match.setSubtext(i18n("Suspend to disk"));
0150         match.setRelevance(0.99);
0151         break;
0152     }
0153 
0154     match.setData(value);
0155     match.setId(QStringLiteral("Sleep"));
0156     matches.append(match);
0157 }
0158 
0159 void PowerDevilRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match)
0160 {
0161     Q_UNUSED(context)
0162 
0163     QDBusInterface iface(QStringLiteral("org.kde.Solid.PowerManagement"),
0164                          QStringLiteral("/org/kde/Solid/PowerManagement"),
0165                          QStringLiteral("org.kde.Solid.PowerManagement"));
0166     QDBusInterface brightnessIface(QStringLiteral("org.kde.Solid.PowerManagement"),
0167                                    QStringLiteral("/org/kde/Solid/PowerManagement/Actions/BrightnessControl"),
0168                                    QStringLiteral("org.kde.Solid.PowerManagement.Actions.BrightnessControl"));
0169     if (match.id().startsWith(QLatin1String("PowerDevil_ProfileChange"))) {
0170         iface.asyncCall(QStringLiteral("loadProfile"), match.data().toString());
0171     } else if (match.id() == QLatin1String("PowerDevil_BrightnessChange")) {
0172         QDBusReply<int> max = brightnessIface.call("brightnessMax");
0173         const int value = max.isValid() ? std::round(match.data().toInt() * max / 100.0) : match.data().toInt();
0174         brightnessIface.asyncCall("setBrightness", value);
0175     } else if (match.id() == QLatin1String("PowerDevil_DimTotal")) {
0176         brightnessIface.asyncCall(QStringLiteral("setBrightness"), 0);
0177     } else if (match.id() == QLatin1String("PowerDevil_DimHalf")) {
0178         QDBusReply<int> brightness = brightnessIface.asyncCall(QStringLiteral("brightness"));
0179         brightnessIface.asyncCall(QStringLiteral("setBrightness"), static_cast<int>(brightness / 2));
0180     } else if (match.id().startsWith(QLatin1String("PowerDevil_Sleep"))) {
0181         switch ((SleepState)match.data().toInt()) {
0182         case SuspendState:
0183         case StandbyState:
0184             m_session->suspend();
0185             break;
0186         case HibernateState:
0187             m_session->hibernate();
0188             break;
0189         }
0190     }
0191 }
0192 
0193 bool PowerDevilRunner::matchesRunnerKeywords(const QList<RunnerKeyword> &keywords, Plasma::QueryMatch::Type &type, const QString &query) const
0194 {
0195     return std::any_of(keywords.begin(), keywords.end(), [&query, &type](const RunnerKeyword &keyword) {
0196         bool exactMatch =
0197             keyword.triggerWord.compare(query, Qt::CaseInsensitive) == 0 || keyword.translatedTriggerWord.compare(query, Qt::CaseInsensitive) == 0;
0198         type = exactMatch ? Plasma::QueryMatch::ExactMatch : Plasma::QueryMatch::CompletionMatch;
0199         if (!exactMatch && keyword.supportPartialMatch) {
0200             return keyword.triggerWord.startsWith(query, Qt::CaseInsensitive) || keyword.translatedTriggerWord.startsWith(query, Qt::CaseInsensitive);
0201         }
0202         return exactMatch;
0203     });
0204 }
0205 
0206 void PowerDevilRunner::addSyntaxForKeyword(const QList<RunnerKeyword> &keywords, const QString &description)
0207 {
0208     Plasma::RunnerSyntax syntax(keywords.first().translatedTriggerWord, description);
0209     for (int i = 1; i < keywords.size(); ++i) {
0210         syntax.addExampleQuery(keywords.at(i).translatedTriggerWord);
0211     }
0212     addSyntax(syntax);
0213 }
0214 
0215 int PowerDevilRunner::matchesScreenBrightnessKeywords(const QString &query) const
0216 {
0217     const static QStringList expressions = {QStringLiteral("screen brightness "),
0218                                             i18nc("Note this is a KRunner keyword, it should end with a space", "screen brightness "),
0219                                             QStringLiteral("dim screen "),
0220                                             i18nc("Note this is a KRunner keyword, it should end with a space", "dim screen ")};
0221 
0222     for (const QString &expression : expressions) {
0223         if (query.startsWith(expression)) {
0224             const QString number = query.mid(expression.size());
0225             bool ok;
0226             int result = qBound(0, number.toInt(&ok), 100);
0227             return ok ? result : -1;
0228         }
0229     }
0230     return -1;
0231 }
0232 
0233 #include "PowerDevilRunner.moc"