File indexing completed on 2024-05-12 17:08:56

0001 /*
0002     SPDX-FileCopyrightText: 2015 Kai Uwe Broulik <kde@privat.broulik.de>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include "sessionsmodel.h"
0008 
0009 #include <utility>
0010 
0011 #include <KAuthorized>
0012 #include <KLocalizedString>
0013 #include <KUser>
0014 
0015 #include "kscreensaversettings.h"
0016 
0017 #include "screensaver_interface.h"
0018 
0019 SessionsModel::SessionsModel(QObject *parent)
0020     : QAbstractListModel(parent)
0021     , m_screensaverInterface(
0022           new org::freedesktop::ScreenSaver(QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("/ScreenSaver"), QDBusConnection::sessionBus(), this))
0023 {
0024     reload();
0025 
0026     // wait for the screen locker to be ready before actually switching
0027     connect(m_screensaverInterface, &org::freedesktop::ScreenSaver::ActiveChanged, this, [this](bool active) {
0028         if (active) {
0029             if (m_pendingVt) {
0030                 m_displayManager.switchVT(m_pendingVt);
0031                 Q_EMIT switchedUser(m_pendingVt);
0032             } else if (m_pendingReserve) {
0033                 m_displayManager.startReserve();
0034                 Q_EMIT startedNewSession();
0035             }
0036 
0037             m_pendingVt = 0;
0038             m_pendingReserve = false;
0039         }
0040     });
0041 }
0042 
0043 bool SessionsModel::canSwitchUser() const
0044 {
0045     return const_cast<SessionsModel *>(this)->m_displayManager.isSwitchable() && KAuthorized::authorizeAction(QStringLiteral("switch_user"));
0046 }
0047 
0048 bool SessionsModel::canStartNewSession() const
0049 {
0050     return const_cast<SessionsModel *>(this)->m_displayManager.numReserve() > 0 && KAuthorized::authorizeAction(QStringLiteral("start_new_session"));
0051 }
0052 
0053 bool SessionsModel::shouldLock() const
0054 {
0055     return m_shouldLock;
0056 }
0057 
0058 bool SessionsModel::includeUnusedSessions() const
0059 {
0060     return m_includeUnusedSessions;
0061 }
0062 
0063 void SessionsModel::setIncludeUnusedSessions(bool includeUnusedSessions)
0064 {
0065     if (m_includeUnusedSessions != includeUnusedSessions) {
0066         m_includeUnusedSessions = includeUnusedSessions;
0067 
0068         reload();
0069 
0070         Q_EMIT includeUnusedSessionsChanged();
0071     }
0072 }
0073 
0074 void SessionsModel::switchUser(int vt, bool shouldLock)
0075 {
0076     if (vt < 0) {
0077         startNewSession(shouldLock);
0078         return;
0079     }
0080 
0081     if (!canSwitchUser()) {
0082         return;
0083     }
0084 
0085     if (!shouldLock) {
0086         m_displayManager.switchVT(vt);
0087         Q_EMIT switchedUser(vt);
0088         return;
0089     }
0090 
0091     checkScreenLocked([this, vt](bool locked) {
0092         if (locked) {
0093             // already locked, switch right away
0094             m_displayManager.switchVT(vt);
0095             Q_EMIT switchedUser(vt);
0096         } else {
0097             m_pendingReserve = false;
0098             m_pendingVt = vt;
0099 
0100             Q_EMIT aboutToLockScreen();
0101             m_screensaverInterface->Lock();
0102         }
0103     });
0104 }
0105 
0106 void SessionsModel::startNewSession(bool shouldLock)
0107 {
0108     if (!canStartNewSession()) {
0109         return;
0110     }
0111 
0112     if (!shouldLock) {
0113         m_displayManager.startReserve();
0114         Q_EMIT startedNewSession();
0115         return;
0116     }
0117 
0118     checkScreenLocked([this](bool locked) {
0119         if (locked) {
0120             // already locked, switch right away
0121             m_displayManager.startReserve();
0122             Q_EMIT startedNewSession();
0123         } else {
0124             m_pendingReserve = true;
0125             m_pendingVt = 0;
0126 
0127             Q_EMIT aboutToLockScreen();
0128             m_screensaverInterface->Lock();
0129         }
0130     });
0131 }
0132 
0133 void SessionsModel::reload()
0134 {
0135     static QHash<QString, KUser> kusers;
0136 
0137     const bool oldShouldLock = m_shouldLock;
0138     m_shouldLock = KAuthorized::authorizeAction(QStringLiteral("lock_screen")) && KScreenSaverSettings::autolock();
0139     if (m_shouldLock != oldShouldLock) {
0140         Q_EMIT shouldLockChanged();
0141     }
0142 
0143     SessList sessions;
0144     m_displayManager.localSessions(sessions);
0145 
0146     const int oldCount = m_data.count();
0147 
0148     beginResetModel();
0149 
0150     m_data.clear();
0151     m_data.reserve(sessions.count());
0152 
0153     for (const SessEnt &session : std::as_const(sessions)) {
0154         if (!session.vt || session.self) {
0155             continue;
0156         }
0157 
0158         if (!m_includeUnusedSessions && session.session.isEmpty()) {
0159             continue;
0160         }
0161 
0162         SessionEntry entry;
0163         entry.name = session.user;
0164         entry.displayNumber = session.display;
0165         entry.vtNumber = session.vt;
0166         entry.session = session.session;
0167         entry.isTty = session.tty;
0168 
0169         auto it = kusers.constFind(session.user);
0170         if (it != kusers.constEnd()) {
0171             entry.realName = it->property(KUser::FullName).toString();
0172             entry.icon = it->faceIconPath();
0173         } else {
0174             KUser user(session.user);
0175             entry.realName = user.property(KUser::FullName).toString();
0176             entry.icon = user.faceIconPath();
0177             kusers.insert(session.user, user);
0178         }
0179 
0180         m_data.append(entry);
0181     }
0182 
0183     endResetModel();
0184 
0185     if (oldCount != m_data.count()) {
0186         Q_EMIT countChanged();
0187     }
0188 }
0189 
0190 void SessionsModel::checkScreenLocked(const std::function<void(bool)> &cb)
0191 {
0192     auto reply = m_screensaverInterface->GetActive();
0193     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
0194     QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [cb](QDBusPendingCallWatcher *watcher) {
0195         QDBusPendingReply<bool> reply = *watcher;
0196         if (!reply.isError()) {
0197             cb(reply.value());
0198         }
0199         watcher->deleteLater();
0200     });
0201 }
0202 
0203 void SessionsModel::setShowNewSessionEntry(bool showNewSessionEntry)
0204 {
0205     if (!canStartNewSession()) {
0206         return;
0207     }
0208 
0209     if (showNewSessionEntry == m_showNewSessionEntry) {
0210         return;
0211     }
0212 
0213     int row = m_data.size();
0214     if (showNewSessionEntry) {
0215         beginInsertRows(QModelIndex(), row, row);
0216         m_showNewSessionEntry = showNewSessionEntry;
0217         endInsertRows();
0218     } else {
0219         beginRemoveRows(QModelIndex(), row, row);
0220         m_showNewSessionEntry = showNewSessionEntry;
0221         endRemoveRows();
0222     }
0223     Q_EMIT countChanged();
0224 }
0225 
0226 QVariant SessionsModel::data(const QModelIndex &index, int role) const
0227 {
0228     if (index.row() < 0 || index.row() > rowCount(QModelIndex())) {
0229         return QVariant();
0230     }
0231 
0232     if (index.row() == m_data.count()) {
0233         switch (role) {
0234         case RealNameRole:
0235             return i18n("New Session");
0236         case IconNameRole:
0237             return QStringLiteral("system-switch-user");
0238         case NameRole:
0239             return i18n("New Session");
0240         case DisplayNumberRole:
0241             return 0; // NA
0242         case VtNumberRole:
0243             return -1; // an invalid VtNumber - which we'll use to indicate it's to start a new session
0244         case SessionRole:
0245             return 0; // NA
0246         case IsTtyRole:
0247             return false; // NA
0248         default:
0249             return QVariant();
0250         }
0251     }
0252 
0253     const SessionEntry &item = m_data.at(index.row());
0254 
0255     switch (role) {
0256     case RealNameRole:
0257         return item.realName;
0258     case IconRole:
0259         return item.icon;
0260     case NameRole:
0261         return item.name;
0262     case DisplayNumberRole:
0263         return item.displayNumber;
0264     case VtNumberRole:
0265         return item.vtNumber;
0266     case SessionRole:
0267         return item.session;
0268     case IsTtyRole:
0269         return item.isTty;
0270     default:
0271         return QVariant();
0272     }
0273 }
0274 
0275 int SessionsModel::rowCount(const QModelIndex &parent) const
0276 {
0277     Q_UNUSED(parent);
0278     return m_data.count() + (m_showNewSessionEntry ? 1 : 0);
0279 }
0280 
0281 QHash<int, QByteArray> SessionsModel::roleNames() const
0282 {
0283     QHash<int, QByteArray> roleNames;
0284 
0285     roleNames[NameRole] = QByteArrayLiteral("name");
0286     roleNames[RealNameRole] = QByteArrayLiteral("realName");
0287     roleNames[IconRole] = QByteArrayLiteral("icon");
0288     roleNames[IconNameRole] = QByteArrayLiteral("iconName");
0289     roleNames[DisplayNumberRole] = QByteArrayLiteral("displayNumber");
0290     roleNames[VtNumberRole] = QByteArrayLiteral("vtNumber");
0291     roleNames[SessionRole] = QByteArrayLiteral("session");
0292     roleNames[IsTtyRole] = QByteArrayLiteral("isTty");
0293 
0294     return roleNames;
0295 }