File indexing completed on 2024-12-01 03:40:14
0001 /* This file is part of the KDE libraries 0002 * SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "kidletime.h" 0008 0009 #include <config-kidletime.h> 0010 0011 #include "kabstractidletimepoller_p.h" 0012 #include "logging.h" 0013 0014 #include <QDir> 0015 #include <QGuiApplication> 0016 #include <QJsonArray> 0017 #include <QPluginLoader> 0018 #include <QPointer> 0019 #include <QSet> 0020 0021 class KIdleTimeHelper 0022 { 0023 public: 0024 KIdleTimeHelper() 0025 : q(nullptr) 0026 { 0027 } 0028 ~KIdleTimeHelper() 0029 { 0030 delete q; 0031 } 0032 KIdleTimeHelper(const KIdleTimeHelper &) = delete; 0033 KIdleTimeHelper &operator=(const KIdleTimeHelper &) = delete; 0034 KIdleTime *q; 0035 }; 0036 0037 Q_GLOBAL_STATIC(KIdleTimeHelper, s_globalKIdleTime) 0038 0039 KIdleTime *KIdleTime::instance() 0040 { 0041 if (!s_globalKIdleTime()->q) { 0042 new KIdleTime; 0043 } 0044 0045 return s_globalKIdleTime()->q; 0046 } 0047 0048 class KIdleTimePrivate 0049 { 0050 Q_DECLARE_PUBLIC(KIdleTime) 0051 KIdleTime *q_ptr; 0052 0053 public: 0054 KIdleTimePrivate() 0055 : catchResume(false) 0056 , currentId(0) 0057 { 0058 } 0059 0060 void loadSystem(); 0061 void unloadCurrentSystem(); 0062 void resumingFromIdle(); 0063 void timeoutReached(int msec); 0064 0065 QPointer<KAbstractIdleTimePoller> poller; 0066 bool catchResume; 0067 0068 int currentId; 0069 QHash<int, int> associations; 0070 }; 0071 0072 KIdleTime::KIdleTime() 0073 : QObject(nullptr) 0074 , d_ptr(new KIdleTimePrivate()) 0075 { 0076 Q_ASSERT(!s_globalKIdleTime()->q); 0077 s_globalKIdleTime()->q = this; 0078 0079 d_ptr->q_ptr = this; 0080 0081 Q_D(KIdleTime); 0082 d->loadSystem(); 0083 0084 connect(d->poller.data(), &KAbstractIdleTimePoller::resumingFromIdle, this, [d]() { 0085 d->resumingFromIdle(); 0086 }); 0087 connect(d->poller.data(), &KAbstractIdleTimePoller::timeoutReached, this, [d](int msec) { 0088 d->timeoutReached(msec); 0089 }); 0090 } 0091 0092 KIdleTime::~KIdleTime() 0093 { 0094 Q_D(KIdleTime); 0095 d->unloadCurrentSystem(); 0096 } 0097 0098 void KIdleTime::catchNextResumeEvent() 0099 { 0100 Q_D(KIdleTime); 0101 0102 if (!d->catchResume && d->poller) { 0103 d->catchResume = true; 0104 d->poller.data()->catchIdleEvent(); 0105 } 0106 } 0107 0108 void KIdleTime::stopCatchingResumeEvent() 0109 { 0110 Q_D(KIdleTime); 0111 0112 if (d->catchResume && d->poller) { 0113 d->catchResume = false; 0114 d->poller.data()->stopCatchingIdleEvents(); 0115 } 0116 } 0117 0118 int KIdleTime::addIdleTimeout(int msec) 0119 { 0120 Q_D(KIdleTime); 0121 if (Q_UNLIKELY(!d->poller)) { 0122 return 0; 0123 } 0124 0125 d->poller.data()->addTimeout(msec); 0126 0127 ++d->currentId; 0128 d->associations[d->currentId] = msec; 0129 0130 return d->currentId; 0131 } 0132 0133 void KIdleTime::removeIdleTimeout(int identifier) 0134 { 0135 Q_D(KIdleTime); 0136 0137 const auto it = d->associations.constFind(identifier); 0138 if (it == d->associations.cend() || !d->poller) { 0139 return; 0140 } 0141 0142 const int msec = it.value(); 0143 0144 d->associations.erase(it); 0145 0146 const bool isFound = std::any_of(d->associations.cbegin(), d->associations.cend(), [msec](int i) { 0147 return i == msec; 0148 }); 0149 0150 if (!isFound) { 0151 d->poller.data()->removeTimeout(msec); 0152 } 0153 } 0154 0155 void KIdleTime::removeAllIdleTimeouts() 0156 { 0157 Q_D(KIdleTime); 0158 0159 std::vector<int> removed; 0160 0161 for (auto it = d->associations.cbegin(); it != d->associations.cend(); ++it) { 0162 const int msec = it.value(); 0163 const bool alreadyIns = std::find(removed.cbegin(), removed.cend(), msec) != removed.cend(); 0164 if (!alreadyIns && d->poller) { 0165 removed.push_back(msec); 0166 d->poller.data()->removeTimeout(msec); 0167 } 0168 } 0169 0170 d->associations.clear(); 0171 } 0172 0173 static QStringList pluginCandidates() 0174 { 0175 QStringList ret; 0176 0177 const QStringList libPath = QCoreApplication::libraryPaths(); 0178 for (const QString &path : libPath) { 0179 #ifdef Q_OS_MACOS 0180 const QDir pluginDir(path + QStringLiteral("/kf6/kidletime")); 0181 #else 0182 const QDir pluginDir(path + QStringLiteral("/kf6/org.kde.kidletime.platforms")); 0183 #endif 0184 if (!pluginDir.exists()) { 0185 continue; 0186 } 0187 0188 const auto entries = pluginDir.entryList(QDir::Files | QDir::NoDotAndDotDot); 0189 0190 ret.reserve(ret.size() + entries.size()); 0191 for (const QString &entry : entries) { 0192 ret << pluginDir.absoluteFilePath(entry); 0193 } 0194 } 0195 0196 return ret; 0197 } 0198 0199 static bool checkPlatform(const QJsonObject &metadata, const QString &platformName) 0200 { 0201 const QJsonArray platforms = metadata.value(QStringLiteral("MetaData")).toObject().value(QStringLiteral("platforms")).toArray(); 0202 return std::any_of(platforms.begin(), platforms.end(), [&platformName](const QJsonValue &value) { 0203 return QString::compare(platformName, value.toString(), Qt::CaseInsensitive) == 0; 0204 }); 0205 } 0206 0207 static KAbstractIdleTimePoller *loadPoller() 0208 { 0209 const QString platformName = QGuiApplication::platformName(); 0210 0211 const QList<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins(); 0212 for (const QStaticPlugin &staticPlugin : staticPlugins) { 0213 const QJsonObject metadata = staticPlugin.metaData(); 0214 if (metadata.value(QLatin1String("IID")) != QLatin1String(KAbstractIdleTimePoller_iid)) { 0215 continue; 0216 } 0217 if (checkPlatform(metadata, platformName)) { 0218 auto *poller = qobject_cast<KAbstractIdleTimePoller *>(staticPlugin.instance()); 0219 if (poller) { 0220 if (poller->isAvailable()) { 0221 qCDebug(KIDLETIME) << "Loaded system poller from a static plugin"; 0222 return poller; 0223 } 0224 delete poller; 0225 } 0226 } 0227 } 0228 0229 const QStringList lstPlugins = pluginCandidates(); 0230 for (const QString &candidate : lstPlugins) { 0231 if (!QLibrary::isLibrary(candidate)) { 0232 continue; 0233 } 0234 QPluginLoader loader(candidate); 0235 if (checkPlatform(loader.metaData(), platformName)) { 0236 auto *poller = qobject_cast<KAbstractIdleTimePoller *>(loader.instance()); 0237 if (poller) { 0238 qCDebug(KIDLETIME) << "Trying plugin" << candidate; 0239 if (poller->isAvailable()) { 0240 qCDebug(KIDLETIME) << "Using" << candidate << "for platform" << platformName; 0241 return poller; 0242 } 0243 delete poller; 0244 } 0245 } 0246 } 0247 0248 qCWarning(KIDLETIME) << "Could not find any system poller plugin"; 0249 return nullptr; 0250 } 0251 0252 void KIdleTimePrivate::loadSystem() 0253 { 0254 if (!poller.isNull()) { 0255 unloadCurrentSystem(); 0256 } 0257 0258 // load plugin 0259 poller = loadPoller(); 0260 0261 if (poller && !poller->isAvailable()) { 0262 poller = nullptr; 0263 } 0264 if (!poller.isNull()) { 0265 poller.data()->setUpPoller(); 0266 } 0267 } 0268 0269 void KIdleTimePrivate::unloadCurrentSystem() 0270 { 0271 if (!poller.isNull()) { 0272 poller.data()->unloadPoller(); 0273 poller.data()->deleteLater(); 0274 } 0275 } 0276 0277 void KIdleTimePrivate::resumingFromIdle() 0278 { 0279 Q_Q(KIdleTime); 0280 0281 if (catchResume) { 0282 Q_EMIT q->resumingFromIdle(); 0283 q->stopCatchingResumeEvent(); 0284 } 0285 } 0286 0287 void KIdleTimePrivate::timeoutReached(int msec) 0288 { 0289 Q_Q(KIdleTime); 0290 0291 const auto listKeys = associations.keys(msec); 0292 0293 for (const auto key : listKeys) { 0294 Q_EMIT q->timeoutReached(key, msec); 0295 } 0296 } 0297 0298 void KIdleTime::simulateUserActivity() 0299 { 0300 Q_D(KIdleTime); 0301 0302 if (Q_LIKELY(d->poller)) { 0303 d->poller.data()->simulateUserActivity(); 0304 } 0305 } 0306 0307 int KIdleTime::idleTime() const 0308 { 0309 Q_D(const KIdleTime); 0310 if (Q_LIKELY(d->poller)) { 0311 return d->poller.data()->forcePollRequest(); 0312 } 0313 return 0; 0314 } 0315 0316 QHash<int, int> KIdleTime::idleTimeouts() const 0317 { 0318 Q_D(const KIdleTime); 0319 0320 return d->associations; 0321 } 0322 0323 #include "moc_kidletime.cpp"