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

0001 /*
0002     SPDX-FileCopyrightText: 2006 Aaron Seigo <aseigo@kde.org>
0003     SPDX-FileCopyrightText: 2022 Natalie Clarius <natalie_clarius@yahoo.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "sessionrunner.h"
0009 
0010 #include <KGuiItem>
0011 #include <KLocalizedString>
0012 #include <KMessageBox>
0013 #include <KSharedConfig>
0014 
0015 K_PLUGIN_CLASS_WITH_JSON(SessionRunner, "plasma-runner-sessions.json")
0016 
0017 SessionRunner::SessionRunner(QObject *parent, const KPluginMetaData &metaData, const QVariantList &args)
0018     : Plasma::AbstractRunner(parent, metaData, args)
0019 {
0020     setObjectName(QStringLiteral("Sessions"));
0021     setPriority(LowPriority);
0022 
0023     m_logoutKeywords = i18nc("KRunner keywords (split by semicolons without whitespace) to log out of the session", "logout;log out")
0024                            .split(QLatin1Char(';'), Qt::SkipEmptyParts);
0025     if (m_session.canLogout()) {
0026         Plasma::RunnerSyntax logoutSyntax(m_logoutKeywords.first(), i18n("Logs out, exiting the current desktop session"));
0027         for (QString keyword : m_logoutKeywords.mid(1)) {
0028             logoutSyntax.addExampleQuery(keyword);
0029         }
0030         addSyntax(logoutSyntax);
0031     }
0032 
0033     m_shutdownKeywords = i18nc("KRunner keywords (split by semicolons without whitespace) to shut down the computer", "shutdown;shut down")
0034                              .split(QLatin1Char(';'), Qt::SkipEmptyParts);
0035     if (m_session.canShutdown()) {
0036         Plasma::RunnerSyntax shutdownSyntax(m_shutdownKeywords.first(), i18n("Turns off the computer"));
0037         for (QString keyword : m_shutdownKeywords.mid(1)) {
0038             shutdownSyntax.addExampleQuery(keyword);
0039         }
0040         addSyntax(shutdownSyntax);
0041     }
0042 
0043     m_restartKeywords = i18nc("KRunner keywords (split by semicolons without whitespace) to restart the computer", "restart;reboot")
0044                             .split(QLatin1Char(';'), Qt::SkipEmptyParts);
0045     if (m_session.canReboot()) {
0046         Plasma::RunnerSyntax restartSyntax(m_restartKeywords.first(), i18n("Reboots the computer"));
0047         for (QString keyword : m_restartKeywords.mid(1)) {
0048             restartSyntax.addExampleQuery(keyword);
0049         }
0050         addSyntax(restartSyntax);
0051     }
0052 
0053     m_lockKeywords =
0054         i18nc("KRunner keywords (split by semicolons without whitespace) to lock the screen", "lock;lock screen").split(QLatin1Char(';'), Qt::SkipEmptyParts);
0055     if (m_session.canLock()) {
0056         Plasma::RunnerSyntax lockSyntax(m_lockKeywords.first(), i18n("Locks the current sessions and starts the screen saver"));
0057         for (QString keyword : m_lockKeywords.mid(1)) {
0058             lockSyntax.addExampleQuery(keyword);
0059         }
0060         addSyntax(lockSyntax);
0061     }
0062 
0063     m_saveKeywords = i18nc("KRunner keywords (split by semicolons without whitespace) to save the desktop session", "save;save session")
0064                          .split(QLatin1Char(';'), Qt::SkipEmptyParts);
0065     if (m_session.canSaveSession()) {
0066         Plasma::RunnerSyntax saveSyntax(m_saveKeywords.first(), i18n("Saves the current session for session restoration"));
0067         for (QString keyword : m_saveKeywords.mid(1)) {
0068             saveSyntax.addExampleQuery(keyword);
0069         }
0070         addSyntax(saveSyntax);
0071     }
0072 
0073     m_usersKeywords = i18nc("KRunner keywords (split by semicolons without whitespace) to switch user sessions", "switch user;new session")
0074                           .split(QLatin1Char(';'), Qt::SkipEmptyParts);
0075     if (m_session.canSwitchUser()) {
0076         Plasma::RunnerSyntax usersSyntax(m_usersKeywords.first(), i18n("Starts a new session as a different user"));
0077         for (QString keyword : m_usersKeywords.mid(1)) {
0078             usersSyntax.addExampleQuery(keyword);
0079         }
0080         addSyntax(usersSyntax);
0081     }
0082 
0083     m_sessionsKeyword = i18nc("KRunner keyword to list user sessions", "sessions");
0084     Plasma::RunnerSyntax sessionsSyntax(m_sessionsKeyword, i18n("Lists all sessions"));
0085     addSyntax(sessionsSyntax);
0086 
0087     m_switchKeyword = i18nc("KRunner keyword to switch user sessions", "switch");
0088     Plasma::RunnerSyntax switchSyntax(m_switchKeyword + QStringLiteral(" :q:"),
0089                                       i18n("Switches to the active session for the user :q:, or lists all active sessions if :q: is not provided"));
0090     addSyntax(switchSyntax);
0091 
0092     setMinLetterCount(3);
0093 }
0094 
0095 SessionRunner::~SessionRunner()
0096 {
0097 }
0098 
0099 static inline bool anyKeywordMatches(const QStringList &keywords, const QString &term)
0100 {
0101     return std::any_of(keywords.cbegin(), keywords.cend(), [&term](const QString &keyword) {
0102         return term.compare(keyword, Qt::CaseInsensitive) == 0;
0103     });
0104 }
0105 
0106 void SessionRunner::matchCommands(QList<Plasma::QueryMatch> &matches, const QString &term)
0107 {
0108     if (anyKeywordMatches(m_logoutKeywords, term)) {
0109         if (m_session.canLogout()) {
0110             Plasma::QueryMatch match(this);
0111             match.setText(i18nc("log out command", "Log Out"));
0112             match.setIconName(QStringLiteral("system-log-out"));
0113             match.setData(LogoutAction);
0114             match.setType(Plasma::QueryMatch::ExactMatch);
0115             match.setRelevance(0.9);
0116             matches << match;
0117         }
0118     } else if (anyKeywordMatches(m_shutdownKeywords, term)) {
0119         if (m_session.canShutdown()) {
0120             Plasma::QueryMatch match(this);
0121             match.setText(i18nc("turn off computer command", "Shut Down"));
0122             match.setIconName(QStringLiteral("system-shutdown"));
0123             match.setData(ShutdownAction);
0124             match.setType(Plasma::QueryMatch::ExactMatch);
0125             match.setRelevance(0.9);
0126             matches << match;
0127         }
0128     } else if (anyKeywordMatches(m_restartKeywords, term)) {
0129         if (m_session.canReboot()) {
0130             Plasma::QueryMatch match(this);
0131             match.setText(i18nc("restart computer command", "Restart"));
0132             match.setIconName(QStringLiteral("system-reboot"));
0133             match.setData(RestartAction);
0134             match.setType(Plasma::QueryMatch::ExactMatch);
0135             match.setRelevance(0.9);
0136             matches << match;
0137         }
0138     } else if (anyKeywordMatches(m_lockKeywords, term)) {
0139         if (m_session.canLock()) {
0140             Plasma::QueryMatch match(this);
0141             match.setText(i18nc("lock screen command", "Lock"));
0142             match.setIconName(QStringLiteral("system-lock-screen"));
0143             match.setData(LockAction);
0144             match.setType(Plasma::QueryMatch::ExactMatch);
0145             match.setRelevance(0.9);
0146             matches << match;
0147         }
0148     } else if (anyKeywordMatches(m_saveKeywords, term)) {
0149         if (m_session.canSaveSession()) {
0150             Plasma::QueryMatch match(this);
0151             match.setText(i18n("Save Session"));
0152             match.setIconName(QStringLiteral("system-save-session"));
0153             match.setData(SaveAction);
0154             match.setType(Plasma::QueryMatch::ExactMatch);
0155             match.setRelevance(0.9);
0156             matches << match;
0157         }
0158     }
0159 }
0160 
0161 void SessionRunner::match(Plasma::RunnerContext &context)
0162 {
0163     const QString term = context.query();
0164     QString user;
0165     bool matchUser = false;
0166 
0167     QList<Plasma::QueryMatch> matches;
0168 
0169     // first compare with "sessions" keyword
0170     // "SESSIONS" must *NOT* be translated (i18n)
0171     // as it is used as an internal command trigger (e.g. via d-bus),
0172     // not as a user supplied query. and yes, "Ugh, magic strings"
0173     bool listAll = anyKeywordMatches(QStringList({QStringLiteral("SESSIONS"), m_sessionsKeyword}), term);
0174 
0175     if (!listAll) {
0176         // no luck, try the "switch" user command
0177         if (term.startsWith(m_switchKeyword, Qt::CaseInsensitive)) {
0178             user = term.right(term.size() - m_switchKeyword.length()).trimmed();
0179             listAll = user.isEmpty();
0180             matchUser = !listAll;
0181         } else {
0182             // we know it's not "sessions" or "switch <something>", so let's
0183             // try some other possibilities
0184             matchCommands(matches, term);
0185         }
0186     }
0187 
0188     bool switchUser = listAll || anyKeywordMatches(m_usersKeywords, term);
0189 
0190     if (switchUser && m_session.canSwitchUser() && dm.isSwitchable() && dm.numReserve() >= 0) {
0191         Plasma::QueryMatch match(this);
0192         match.setType(Plasma::QueryMatch::ExactMatch);
0193         match.setIconName(QStringLiteral("system-switch-user"));
0194         match.setText(i18n("Switch User"));
0195         matches << match;
0196     }
0197 
0198     // now add the active sessions
0199     if (listAll || matchUser) {
0200         SessList sessions;
0201         dm.localSessions(sessions);
0202 
0203         for (const SessEnt &session : qAsConst(sessions)) {
0204             if (!session.vt || session.self) {
0205                 continue;
0206             }
0207 
0208             QString name = KDisplayManager::sess2Str(session);
0209             Plasma::QueryMatch::Type type = Plasma::QueryMatch::NoMatch;
0210             qreal relevance = 0.7;
0211 
0212             if (listAll) {
0213                 type = Plasma::QueryMatch::ExactMatch;
0214                 relevance = 1;
0215             } else if (matchUser) {
0216                 if (name.compare(user, Qt::CaseInsensitive) == 0) {
0217                     // we need an elif branch here because we don't
0218                     // want the last conditional to be checked if !listAll
0219                     type = Plasma::QueryMatch::ExactMatch;
0220                     relevance = 1;
0221                 } else if (name.contains(user, Qt::CaseInsensitive)) {
0222                     type = Plasma::QueryMatch::PossibleMatch;
0223                 }
0224             }
0225 
0226             if (type != Plasma::QueryMatch::NoMatch) {
0227                 Plasma::QueryMatch match(this);
0228                 match.setType(type);
0229                 match.setRelevance(relevance);
0230                 match.setIconName(QStringLiteral("user-identity"));
0231                 match.setText(name);
0232                 match.setData(QString::number(session.vt));
0233                 matches << match;
0234             }
0235         }
0236     }
0237 
0238     context.addMatches(matches);
0239 }
0240 
0241 void SessionRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match)
0242 {
0243     Q_UNUSED(context);
0244     if (match.data().type() == QVariant::Int) {
0245         switch (match.data().toInt()) {
0246         case LogoutAction:
0247             m_session.requestLogout();
0248             break;
0249         case RestartAction:
0250             m_session.requestReboot();
0251             break;
0252         case ShutdownAction:
0253             m_session.requestShutdown();
0254             break;
0255         case LockAction:
0256             m_session.lock();
0257             break;
0258         case SaveAction:
0259             m_session.saveSession();
0260             break;
0261         }
0262         return;
0263     }
0264 
0265     if (!match.data().toString().isEmpty()) {
0266         dm.lockSwitchVT(match.data().toString().toInt());
0267         return;
0268     }
0269 
0270     const auto config = KSharedConfig::openConfig(QStringLiteral("ksmserverrc"));
0271     KMessageBox::setDontShowAgainConfig(config.data());
0272     KGuiItem continueButton = KStandardGuiItem::cont();
0273     continueButton.setText("Enter new session");
0274     KGuiItem cancelButton = KStandardGuiItem::cancel();
0275     cancelButton.setText("Stay in current session");
0276     KMessageBox::ButtonCode confirmNewSession =
0277         KMessageBox::warningContinueCancel(nullptr,
0278                                            i18n("<p>You are about to enter a new desktop session.</p>"
0279                                                 "<p>A login screen will be displayed and the current session will be hidden.</p>"
0280                                                 "<p>You can switch between desktop sessions using:</p>"
0281                                                 "<ul>"
0282                                                 "<li>Ctrl+Alt+F{number of session}</li>"
0283                                                 "<li>Plasma search (type '%1')</li>"
0284                                                 "<li>Plasma widgets (such as the application launcher)</li>"
0285                                                 "</ul>",
0286                                                 m_switchKeyword),
0287                                            i18n("New Desktop Session"),
0288                                            continueButton,
0289                                            cancelButton,
0290                                            QStringLiteral("ConfirmNewSession"));
0291     if (confirmNewSession == KMessageBox::Continue) {
0292         m_session.lock();
0293         dm.startReserve();
0294     }
0295 }
0296 
0297 #include "sessionrunner.moc"