KIdleTime

kidletime.cpp
1 /* This file is part of the KDE libraries
2  * SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  */
6 
7 #include "kidletime.h"
8 
9 #include <config-kidletime.h>
10 
11 #include "abstractsystempoller.h"
12 #include "logging.h"
13 
14 #include <QDir>
15 #include <QGuiApplication>
16 #include <QJsonArray>
17 #include <QPluginLoader>
18 #include <QPointer>
19 #include <QSet>
20 
21 class KIdleTimeHelper
22 {
23 public:
24  KIdleTimeHelper()
25  : q(nullptr)
26  {
27  }
28  ~KIdleTimeHelper()
29  {
30  delete q;
31  }
32  KIdleTimeHelper(const KIdleTimeHelper &) = delete;
33  KIdleTimeHelper &operator=(const KIdleTimeHelper &) = delete;
34  KIdleTime *q;
35 };
36 
37 Q_GLOBAL_STATIC(KIdleTimeHelper, s_globalKIdleTime)
38 
39 KIdleTime *KIdleTime::instance()
40 {
41  if (!s_globalKIdleTime()->q) {
42  new KIdleTime;
43  }
44 
45  return s_globalKIdleTime()->q;
46 }
47 
48 class KIdleTimePrivate
49 {
50  Q_DECLARE_PUBLIC(KIdleTime)
51  KIdleTime *q_ptr;
52 
53 public:
54  KIdleTimePrivate()
55  : catchResume(false)
56  , currentId(0)
57  {
58  }
59 
60  void loadSystem();
61  void unloadCurrentSystem();
62  void resumingFromIdle();
63  void timeoutReached(int msec);
64 
66  bool catchResume;
67 
68  int currentId;
69  QHash<int, int> associations;
70 };
71 
72 KIdleTime::KIdleTime()
73  : QObject(nullptr)
74  , d_ptr(new KIdleTimePrivate())
75 {
76  Q_ASSERT(!s_globalKIdleTime()->q);
77  s_globalKIdleTime()->q = this;
78 
79  d_ptr->q_ptr = this;
80 
81  Q_D(KIdleTime);
82  d->loadSystem();
83 
84  connect(d->poller.data(), &AbstractSystemPoller::resumingFromIdle, this, [d]() {
85  d->resumingFromIdle();
86  });
87  connect(d->poller.data(), &AbstractSystemPoller::timeoutReached, this, [d](int msec) {
88  d->timeoutReached(msec);
89  });
90 }
91 
93 {
94  Q_D(KIdleTime);
95  d->unloadCurrentSystem();
96 }
97 
99 {
100  Q_D(KIdleTime);
101 
102  if (!d->catchResume && d->poller) {
103  d->catchResume = true;
104  d->poller.data()->catchIdleEvent();
105  }
106 }
107 
109 {
110  Q_D(KIdleTime);
111 
112  if (d->catchResume && d->poller) {
113  d->catchResume = false;
114  d->poller.data()->stopCatchingIdleEvents();
115  }
116 }
117 
119 {
120  Q_D(KIdleTime);
121  if (Q_UNLIKELY(!d->poller)) {
122  return 0;
123  }
124 
125  d->poller.data()->addTimeout(msec);
126 
127  ++d->currentId;
128  d->associations[d->currentId] = msec;
129 
130  return d->currentId;
131 }
132 
133 void KIdleTime::removeIdleTimeout(int identifier)
134 {
135  Q_D(KIdleTime);
136 
137  const auto it = d->associations.constFind(identifier);
138  if (it == d->associations.cend() || !d->poller) {
139  return;
140  }
141 
142  const int msec = it.value();
143 
144  d->associations.erase(it);
145 
146  const bool isFound = std::any_of(d->associations.cbegin(), d->associations.cend(), [msec](int i) {
147  return i == msec;
148  });
149 
150  if (!isFound) {
151  d->poller.data()->removeTimeout(msec);
152  }
153 }
154 
156 {
157  Q_D(KIdleTime);
158 
159  std::vector<int> removed;
160 
161  for (auto it = d->associations.cbegin(); it != d->associations.cend(); ++it) {
162  const int msec = it.value();
163  const bool alreadyIns = std::find(removed.cbegin(), removed.cend(), msec) != removed.cend();
164  if (!alreadyIns && d->poller) {
165  removed.push_back(msec);
166  d->poller.data()->removeTimeout(msec);
167  }
168  }
169 
170  d->associations.clear();
171 }
172 
173 static QStringList pluginCandidates()
174 {
175  QStringList ret;
176 
178  for (const QString &path : libPath) {
179 #ifdef Q_OS_MACOS
180  const QDir pluginDir(path + QStringLiteral("/kf" QT_STRINGIFY(QT_VERSION_MAJOR) "/kidletime"));
181 #else
182  const QDir pluginDir(path + QStringLiteral("/kf" QT_STRINGIFY(QT_VERSION_MAJOR) "/org.kde.kidletime.platforms"));
183 #endif
184  if (!pluginDir.exists()) {
185  continue;
186  }
187 
188  const auto entries = pluginDir.entryList(QDir::Files | QDir::NoDotAndDotDot);
189 
190  ret.reserve(ret.size() + entries.size());
191  for (const QString &entry : entries) {
192  ret << pluginDir.absoluteFilePath(entry);
193  }
194  }
195 
196  return ret;
197 }
198 
199 static bool checkPlatform(const QJsonObject &metadata, const QString &platformName)
200 {
201  const QJsonArray platforms = metadata.value(QStringLiteral("MetaData")).toObject().value(QStringLiteral("platforms")).toArray();
202  return std::any_of(platforms.begin(), platforms.end(), [&platformName](const QJsonValue &value) {
203  return QString::compare(platformName, value.toString(), Qt::CaseInsensitive) == 0;
204  });
205 }
206 
207 static AbstractSystemPoller *loadPoller()
208 {
209  const QString platformName = QGuiApplication::platformName();
210 
212  for (const QStaticPlugin &staticPlugin : staticPlugins) {
213  const QJsonObject metadata = staticPlugin.metaData();
214  if (metadata.value(QLatin1String("IID")) != QLatin1String(AbstractSystemPoller_iid)) {
215  continue;
216  }
217  if (checkPlatform(metadata, platformName)) {
218  AbstractSystemPoller *poller = qobject_cast<AbstractSystemPoller *>(staticPlugin.instance());
219  if (poller) {
220  if (poller->isAvailable()) {
221  qCDebug(KIDLETIME) << "Loaded system poller from a static plugin";
222  return poller;
223  }
224  delete poller;
225  }
226  }
227  }
228 
229  const QStringList lstPlugins = pluginCandidates();
230  for (const QString &candidate : lstPlugins) {
231  if (!QLibrary::isLibrary(candidate)) {
232  continue;
233  }
234  QPluginLoader loader(candidate);
235  if (checkPlatform(loader.metaData(), platformName)) {
236  AbstractSystemPoller *poller = qobject_cast<AbstractSystemPoller *>(loader.instance());
237  if (poller) {
238  qCDebug(KIDLETIME) << "Trying plugin" << candidate;
239  if (poller->isAvailable()) {
240  qCDebug(KIDLETIME) << "Using" << candidate << "for platform" << platformName;
241  return poller;
242  }
243  delete poller;
244  }
245  }
246  }
247 
248  qCWarning(KIDLETIME) << "Could not find any system poller plugin";
249  return nullptr;
250 }
251 
252 void KIdleTimePrivate::loadSystem()
253 {
254  if (!poller.isNull()) {
255  unloadCurrentSystem();
256  }
257 
258  // load plugin
259  poller = loadPoller();
260 
261  if (poller && !poller->isAvailable()) {
262  poller = nullptr;
263  }
264  if (!poller.isNull()) {
265  poller.data()->setUpPoller();
266  }
267 }
268 
269 void KIdleTimePrivate::unloadCurrentSystem()
270 {
271  if (!poller.isNull()) {
272  poller.data()->unloadPoller();
273  poller.data()->deleteLater();
274  }
275 }
276 
277 void KIdleTimePrivate::resumingFromIdle()
278 {
279  Q_Q(KIdleTime);
280 
281  if (catchResume) {
282  Q_EMIT q->resumingFromIdle();
283  q->stopCatchingResumeEvent();
284  }
285 }
286 
287 void KIdleTimePrivate::timeoutReached(int msec)
288 {
289  Q_Q(KIdleTime);
290 
291  const auto listKeys = associations.keys(msec);
292 
293  for (const auto key : listKeys) {
294 #if KIDLETIME_BUILD_DEPRECATED_SINCE(5, 76)
295  Q_EMIT q->timeoutReached(key);
296 #endif
297  Q_EMIT q->timeoutReached(key, msec);
298  }
299 }
300 
302 {
303  Q_D(KIdleTime);
304 
305  if (Q_LIKELY(d->poller)) {
306  d->poller.data()->simulateUserActivity();
307  }
308 }
309 
311 {
312  Q_D(const KIdleTime);
313  if (Q_LIKELY(d->poller)) {
314  return d->poller.data()->forcePollRequest();
315  }
316  return 0;
317 }
318 
320 {
321  Q_D(const KIdleTime);
322 
323  return d->associations;
324 }
325 
326 #include "moc_kidletime.cpp"
QVector< QStaticPlugin > staticPlugins()
QJsonObject toObject() const const
QHash< int, int > idleTimeouts() const
Returns the list of timeout identifiers associated with their duration, in milliseconds,...
Definition: kidletime.cpp:319
~KIdleTime() override
The destructor.
Definition: kidletime.cpp:92
int idleTime() const
Retrieves the idle time of the system, in milliseconds.
Definition: kidletime.cpp:310
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void stopCatchingResumeEvent()
Stops listening for resume event.
Definition: kidletime.cpp:108
bool isLibrary(const QString &fileName)
Q_GLOBAL_STATIC(Internal::StaticControl, s_instance) class ControlPrivate
QStringList libraryPaths()
void reserve(int alloc)
int size() const const
void removeIdleTimeout(int identifier)
Stops catching the idle timeout identified by the token identifier, if it was registered earlier with...
Definition: kidletime.cpp:133
void removeAllIdleTimeouts()
Stops catching every set timeout (if any).
Definition: kidletime.cpp:155
QJsonArray::iterator begin()
QJsonValue value(const QString &key) const const
QJsonArray toArray() const const
QJsonArray::iterator end()
void catchNextResumeEvent()
Catches the next resume from idle event.
Definition: kidletime.cpp:98
void simulateUserActivity()
Attempts to simulate user activity.
Definition: kidletime.cpp:301
int addIdleTimeout(int msec)
Adds a new timeout to catch.
Definition: kidletime.cpp:118
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Mon Jun 27 2022 03:56:15 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.