KDED

kded.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
4 SPDX-FileCopyrightText: 2000 Waldo Bastian <bastian@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-only
7*/
8
9#include "kded.h"
10#include "kded_debug.h"
11#include "kded_version.h"
12#include "kdedadaptor.h"
13
14#include <KCrash>
15
16#include <qplatformdefs.h>
17
18#include <QApplication>
19#include <QCommandLineParser>
20#include <QDir>
21#include <QLoggingCategory>
22#include <QProcess>
23
24#include <QDBusConnection>
25#include <QDBusConnectionInterface>
26#include <QDBusServiceWatcher>
27
28#include <KConfigGroup>
29#include <KDBusService>
30#include <KDirWatch>
31#include <KPluginFactory>
32#include <KPluginMetaData>
33#include <KSharedConfig>
34
35#include <memory>
36
37Kded *Kded::_self = nullptr;
38
39static bool delayedCheck;
40static bool bCheckSycoca;
41static bool bCheckUpdates;
42
43#ifdef Q_DBUS_EXPORT
44extern Q_DBUS_EXPORT void qDBusAddSpyHook(void (*)(const QDBusMessage &));
45#else
46extern QDBUS_EXPORT void qDBusAddSpyHook(void (*)(const QDBusMessage &));
47#endif
48
49static void runKonfUpdate()
50{
51 int ret = QProcess::execute(QStringLiteral(KCONF_UPDATE_EXE), QStringList());
52 if (ret != 0) {
53 qCWarning(KDED) << KCONF_UPDATE_EXE << "returned" << ret;
54 }
55}
56
57Kded::Kded()
58 : m_pDirWatch(new KDirWatch(this))
59 , m_pTimer(new QTimer(this))
60 , m_needDelayedCheck(false)
61{
62 _self = this;
63
64 m_serviceWatcher = new QDBusServiceWatcher(this);
65 m_serviceWatcher->setConnection(QDBusConnection::sessionBus());
66 m_serviceWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
67 QObject::connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &Kded::slotApplicationRemoved);
68
69 new KdedAdaptor(this);
70
72 session.registerObject(QStringLiteral("/kbuildsycoca"), this);
73 session.registerObject(QStringLiteral("/kded"), this);
74
75 m_pTimer->setSingleShot(true);
76 connect(m_pTimer, &QTimer::timeout, this, static_cast<void (Kded::*)()>(&Kded::recreate));
77}
78
79Kded::~Kded()
80{
81 _self = nullptr;
82 m_pTimer->stop();
83
84 for (auto it = m_modules.cbegin(); it != m_modules.cend(); ++it) {
85 delete *it;
86 }
87 m_modules.clear();
88}
89
90// on-demand module loading
91// this function is called by the D-Bus message processing function before
92// calls are delivered to objects
93void Kded::messageFilter(const QDBusMessage &message)
94{
95 // This happens when kded goes down and some modules try to clean up.
96 if (!self()) {
97 return;
98 }
99
101 if (obj.isEmpty() || obj == QLatin1String("ksycoca")) {
102 return;
103 }
104
105 if (self()->m_dontLoad.value(obj, nullptr)) {
106 return;
107 }
108
109 self()->loadModule(obj, true);
110}
111
112static int phaseForModule(const KPluginMetaData &module)
113{
114 return module.value(QStringLiteral("X-KDE-Kded-phase"), 2);
115}
116
117QList<KPluginMetaData> Kded::availableModules() const
118{
119 QList<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf6/kded"));
120 QSet<QString> moduleIds;
121 for (const KPluginMetaData &md : std::as_const(plugins)) {
122 moduleIds.insert(md.pluginId());
123 }
124 return plugins;
125}
126
127static KPluginMetaData findModule(const QString &id)
128{
129 KPluginMetaData module(QStringLiteral("kf6/kded/") + id);
130 if (module.isValid()) {
131 return module;
132 }
133 qCWarning(KDED) << "could not find kded module with id" << id;
134 return KPluginMetaData();
135}
136
137void Kded::initModules()
138{
139 m_dontLoad.clear();
140
141 bool kde_running = !qEnvironmentVariableIsEmpty("KDE_FULL_SESSION");
142 if (kde_running) {
143 // not the same user like the one running the session (most likely we're run via sudo or something)
144 const QByteArray sessionUID = qgetenv("KDE_SESSION_UID");
145 if (!sessionUID.isEmpty() && uid_t(sessionUID.toInt()) != getuid()) {
146 kde_running = false;
147 }
148 // not the same kde version as the current desktop
149 const QByteArray kdeSession = qgetenv("KDE_SESSION_VERSION");
150 if (kdeSession.toInt() != 6) {
151 kde_running = false;
152 }
153 }
154
155 // Preload kded modules.
156 const QList<KPluginMetaData> kdedModules = availableModules();
157 for (const KPluginMetaData &module : kdedModules) {
158 // Should the service load on startup?
159 const bool autoload = isModuleAutoloaded(module);
160 if (!platformSupportsModule(module)) {
161 continue;
162 }
163
164 // see ksmserver's README for description of the phases
165 bool prevent_autoload = false;
166 switch (phaseForModule(module)) {
167 case 0: // always autoload
168 break;
169 case 1: // autoload only in KDE
170 if (!kde_running) {
171 prevent_autoload = true;
172 }
173 break;
174 case 2: // autoload delayed, only in KDE
175 default:
176 if (!kde_running) {
177 prevent_autoload = true;
178 }
179 break;
180 }
181
182 // Load the module if necessary and allowed
183 if (autoload && !prevent_autoload) {
184 if (!loadModule(module, false)) {
185 continue;
186 }
187 }
188
189 // Remember if the module is allowed to load on demand
190 bool loadOnDemand = isModuleLoadedOnDemand(module);
191 if (!loadOnDemand) {
192 noDemandLoad(module.pluginId());
193 }
194
195 // In case of reloading the configuration it is possible for a module
196 // to run even if it is now allowed to. Stop it then.
197 if (!loadOnDemand && !autoload) {
198 unloadModule(module.pluginId());
199 }
200 }
201}
202
203void Kded::noDemandLoad(const QString &obj)
204{
205 m_dontLoad.insert(obj, this);
206}
207
208void Kded::setModuleAutoloading(const QString &obj, bool autoload)
209{
210 KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kded5rc"));
211 // Ensure the service exists.
212 KPluginMetaData module = findModule(obj);
213 if (!module.isValid()) {
214 return;
215 }
216 KConfigGroup cg(config, QStringLiteral("Module-").append(module.pluginId()));
217 cg.writeEntry("autoload", autoload);
218 cg.sync();
219}
220
221bool Kded::isModuleAutoloaded(const QString &obj) const
222{
223 return isModuleAutoloaded(findModule(obj));
224}
225
226bool Kded::isModuleAutoloaded(const KPluginMetaData &module) const
227{
228 if (!module.isValid()) {
229 return false;
230 }
231 KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kded5rc"));
232 bool autoload = module.value(QStringLiteral("X-KDE-Kded-autoload"), false);
233 KConfigGroup cg(config, QStringLiteral("Module-").append(module.pluginId()));
234 autoload = cg.readEntry("autoload", autoload);
235 return autoload;
236}
237
238bool Kded::platformSupportsModule(const KPluginMetaData &module) const
239{
240 const QStringList supportedPlatforms = module.value(QStringLiteral("X-KDE-OnlyShowOnQtPlatforms"), QStringList());
241
242 return supportedPlatforms.isEmpty() || supportedPlatforms.contains(qApp->platformName());
243}
244
245bool Kded::isModuleLoadedOnDemand(const QString &obj) const
246{
247 return isModuleLoadedOnDemand(findModule(obj));
248}
249
250bool Kded::isModuleLoadedOnDemand(const KPluginMetaData &module) const
251{
252 if (!module.isValid()) {
253 return false;
254 }
255 KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kded5rc"));
256 return module.value(QStringLiteral("X-KDE-Kded-load-on-demand"), true);
257}
258
259KDEDModule *Kded::loadModule(const QString &obj, bool onDemand)
260{
261 // Make sure this method is only called with valid module names.
262 if (obj.contains(QLatin1Char('/'))) {
263 qCWarning(KDED) << "attempting to load invalid kded module name:" << obj;
264 return nullptr;
265 }
266 KDEDModule *module = m_modules.value(obj, nullptr);
267 if (module) {
268 return module;
269 }
270 return loadModule(findModule(obj), onDemand);
271}
272
273KDEDModule *Kded::loadModule(const KPluginMetaData &module, bool onDemand)
274{
275 if (!module.isValid() || module.fileName().isEmpty()) {
276 qCWarning(KDED) << "attempted to load an invalid module.";
277 return nullptr;
278 }
279 const QString moduleId = module.pluginId();
280 KDEDModule *oldModule = m_modules.value(moduleId, nullptr);
281 if (oldModule) {
282 qCDebug(KDED) << "kded module" << moduleId << "is already loaded.";
283 return oldModule;
284 }
285
286 if (onDemand) {
287 if (!module.value(QStringLiteral("X-KDE-Kded-load-on-demand"), true)) {
288 noDemandLoad(moduleId);
289 return nullptr;
290 }
291 }
292
293 KDEDModule *kdedModule = nullptr;
294
295 auto factoryResult = KPluginFactory::loadFactory(module);
296 if (factoryResult) {
297 kdedModule = factoryResult.plugin->create<KDEDModule>(this);
298 } else {
299 qCWarning(KDED).nospace() << "Could not load kded module " << moduleId << ":" << factoryResult.errorText << " (library path was:" << module.fileName()
300 << ")";
301 }
302
303 if (kdedModule) {
304 kdedModule->setModuleName(moduleId);
305 m_modules.insert(moduleId, kdedModule);
306 // m_libs.insert(moduleId, lib);
307 qCDebug(KDED) << "Successfully loaded module" << moduleId;
308 return kdedModule;
309 }
310 return nullptr;
311}
312
313bool Kded::unloadModule(const QString &obj)
314{
315 KDEDModule *module = m_modules.value(obj, nullptr);
316 if (!module) {
317 return false;
318 }
319 qCDebug(KDED) << "Unloading module" << obj;
320 m_modules.remove(obj);
321 delete module;
322 return true;
323}
324
325QStringList Kded::loadedModules()
326{
327 return m_modules.keys();
328}
329
330void Kded::slotApplicationRemoved(const QString &name)
331{
332#if 0 // see kdedmodule.cpp (KDED_OBJECTS)
333 foreach (KDEDModule *module, m_modules) {
334 module->removeAll(appId);
335 }
336#endif
337 m_serviceWatcher->removeWatchedService(name);
338 const QList<qlonglong> windowIds = m_windowIdList.value(name);
339 for (const auto id : windowIds) {
340 m_globalWindowIdList.remove(id);
341 for (KDEDModule *module : std::as_const(m_modules)) {
342 Q_EMIT module->windowUnregistered(id);
343 }
344 }
345 m_windowIdList.remove(name);
346}
347
348void Kded::updateDirWatch()
349{
350 if (!bCheckUpdates) {
351 return;
352 }
353
354 delete m_pDirWatch;
355 m_pDirWatch = new KDirWatch(this);
356
357 QObject::connect(m_pDirWatch, &KDirWatch::dirty, this, &Kded::update);
358 QObject::connect(m_pDirWatch, &KDirWatch::created, this, &Kded::update);
359 QObject::connect(m_pDirWatch, &KDirWatch::deleted, this, &Kded::dirDeleted);
360
361 // For each resource
362 for (const QString &dir : std::as_const(m_allResourceDirs)) {
363 readDirectory(dir);
364 }
365
367 for (auto &dir : dataDirs) {
368 dir += QLatin1String("/icons");
369 if (!m_pDirWatch->contains(dir)) {
370 m_pDirWatch->addDir(dir, KDirWatch::WatchDirOnly);
371 }
372 }
373}
374
375void Kded::updateResourceList()
376{
377 KSycoca::clearCaches();
378
379 if (!bCheckUpdates) {
380 return;
381 }
382
383 if (delayedCheck) {
384 return;
385 }
386
387 const QStringList dirs = KSycoca::self()->allResourceDirs();
388 // For each resource
389 for (const auto &dir : dirs) {
390 if (!m_allResourceDirs.contains(dir)) {
391 m_allResourceDirs.append(dir);
392 readDirectory(dir);
393 }
394 }
395}
396
397void Kded::recreate()
398{
399 recreate(false);
400}
401
402void Kded::runDelayedCheck()
403{
404 if (m_needDelayedCheck) {
405 recreate(false);
406 }
407 m_needDelayedCheck = false;
408}
409
410void Kded::recreate(bool initial)
411{
412 // Using KLauncher here is difficult since we might not have a
413 // database
414
415 if (!initial) {
416 updateDirWatch(); // Update tree first, to be sure to miss nothing.
418 recreateDone();
419 } else {
420 if (!delayedCheck) {
421 updateDirWatch(); // this would search all the directories
422 }
423 if (bCheckSycoca) {
425 }
426 recreateDone();
427 if (delayedCheck) {
428 // do a proper ksycoca check after a delay
429 QTimer::singleShot(60000, this, &Kded::runDelayedCheck);
430 m_needDelayedCheck = true;
431 delayedCheck = false;
432 } else {
433 m_needDelayedCheck = false;
434 }
435 }
436}
437
438void Kded::recreateDone()
439{
440 updateResourceList();
441
442 initModules();
443}
444
445void Kded::dirDeleted(const QString &path)
446{
447 update(path);
448}
449
450void Kded::update(const QString &path)
451{
452 if (path.endsWith(QLatin1String("/icons")) && m_pDirWatch->contains(path)) {
453 // If the dir was created or updated there could be new folders to merge into the active theme(s)
454 QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KIconLoader"), QStringLiteral("org.kde.KIconLoader"), QStringLiteral("iconChanged"));
455 message << 0;
457 } else {
458 m_pTimer->start(1000);
459 }
460}
461
462void Kded::readDirectory(const QString &_path)
463{
464 QString path(_path);
465 if (!path.endsWith(QLatin1Char('/'))) {
466 path += QLatin1Char('/');
467 }
468
469 if (m_pDirWatch->contains(path)) { // Already seen this one?
470 return;
471 }
472
473 Q_ASSERT(path != QDir::homePath());
474 m_pDirWatch->addDir(path, KDirWatch::WatchFiles | KDirWatch::WatchSubDirs); // add watch on this dir
475}
476
477void Kded::registerWindowId(qlonglong windowId, const QString &sender)
478{
479 if (!m_windowIdList.contains(sender)) {
480 m_serviceWatcher->addWatchedService(sender);
481 }
482
483 m_globalWindowIdList.insert(windowId);
484 QList<qlonglong> windowIds = m_windowIdList.value(sender);
485 windowIds.append(windowId);
486 m_windowIdList.insert(sender, windowIds);
487
488 for (KDEDModule *module : std::as_const(m_modules)) {
489 qCDebug(KDED) << module->moduleName();
490 Q_EMIT module->windowRegistered(windowId);
491 }
492}
493
494void Kded::unregisterWindowId(qlonglong windowId, const QString &sender)
495{
496 m_globalWindowIdList.remove(windowId);
497 QList<qlonglong> windowIds = m_windowIdList.value(sender);
498 if (!windowIds.isEmpty()) {
499 windowIds.removeAll(windowId);
500 if (windowIds.isEmpty()) {
501 m_serviceWatcher->removeWatchedService(sender);
502 m_windowIdList.remove(sender);
503 } else {
504 m_windowIdList.insert(sender, windowIds);
505 }
506 }
507
508 for (KDEDModule *module : std::as_const(m_modules)) {
509 qCDebug(KDED) << module->moduleName();
510 Q_EMIT module->windowUnregistered(windowId);
511 }
512}
513
514static void sighandler(int /*sig*/)
515{
516 if (qApp) {
517 qApp->quit();
518 }
519}
520
521KUpdateD::KUpdateD()
522{
523 m_pDirWatch = new KDirWatch(this);
524 m_pTimer = new QTimer(this);
525 m_pTimer->setSingleShot(true);
526 connect(m_pTimer, &QTimer::timeout, this, &KUpdateD::runKonfUpdate);
527 QObject::connect(m_pDirWatch, &KDirWatch::dirty, this, &KUpdateD::slotNewUpdateFile);
528
530 for (auto &path : dirs) {
531 Q_ASSERT(path != QDir::homePath());
532 if (!path.endsWith(QLatin1Char('/'))) {
533 path += QLatin1Char('/');
534 }
535
536 if (!m_pDirWatch->contains(path)) {
537 m_pDirWatch->addDir(path, KDirWatch::WatchFiles);
538 }
539 }
540}
541
542KUpdateD::~KUpdateD()
543{
544}
545
546void KUpdateD::runKonfUpdate()
547{
548 ::runKonfUpdate();
549}
550
551void KUpdateD::slotNewUpdateFile(const QString &dirty)
552{
553 Q_UNUSED(dirty);
554 qCDebug(KDED) << dirty;
555 m_pTimer->start(500);
556}
557
558static void setupAppInfo(QApplication *app)
559{
560 app->setApplicationName(QStringLiteral("kded6"));
561 app->setOrganizationDomain(QStringLiteral("kde.org"));
562 app->setApplicationVersion(QStringLiteral(KDED_VERSION_STRING));
563}
564
565static bool detectPlatform(int argc, char **argv)
566{
567 if (qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) {
568 return false;
569 }
570 for (int i = 0; i < argc; i++) {
571 /* clang-format off */
572 if (qstrcmp(argv[i], "-platform") == 0
573 || qstrcmp(argv[i], "--platform") == 0
574 || QByteArrayView(argv[i]).startsWith("-platform=")
575 || QByteArrayView(argv[i]).startsWith("--platform=")) { /* clang-format on */
576 return false;
577 }
578 }
579 const QByteArray sessionType = qgetenv("XDG_SESSION_TYPE");
580 if (sessionType.isEmpty()) {
581 return false;
582 }
583 if (qstrcmp(sessionType.data(), "wayland") == 0) {
584 qputenv("QT_QPA_PLATFORM", "wayland");
585 return true;
586 } else if (qstrcmp(sessionType.data(), "x11") == 0) {
587 qputenv("QT_QPA_PLATFORM", "xcb");
588 return true;
589 }
590 return false;
591}
592
593int main(int argc, char *argv[])
594{
595 // options.add("check", qi18n("Check Sycoca database only once"));
596
597 // WABA: Make sure not to enable session management.
598 qunsetenv("SESSION_MANAGER");
599
600 const bool unsetQpa = detectPlatform(argc, argv);
601
602 // In older versions, QApplication creation was postponed until after
603 // testing for --check, in which case, only a QCoreApplication was created.
604 // Since that option is no longer used at startup, we removed that speed
605 // optimization for code clarity and easier support of standard parameters.
606
607 QApplication app(argc, argv);
608 if (unsetQpa) {
609 qunsetenv("QT_QPA_PLATFORM");
610 }
611 setupAppInfo(&app);
612 app.setQuitOnLastWindowClosed(false);
613 app.setQuitLockEnabled(false);
614
615 QCommandLineParser parser;
616 parser.addHelpOption();
617 parser.addVersionOption();
618 parser.addOption(QCommandLineOption(QStringLiteral("check"), QStringLiteral("Check cache validity")));
619 QCommandLineOption replaceOption({QStringLiteral("replace")}, QStringLiteral("Replace an existing instance"));
620 parser.addOption(replaceOption);
621 parser.process(app);
622
623 // Parse command line before checking D-Bus
624 if (parser.isSet(QStringLiteral("check"))) {
625 // KDBusService not wanted here.
627 runKonfUpdate();
628 return 0;
629 }
630
631 // Qt now has DBus in another thread, so we need to handle any messages
632 // between the service registration and our paths existing
633 // This means adding the spy now, so any received message gets
634 // posted to the main thread
635 qDBusAddSpyHook(Kded::messageFilter);
636
638 // Also register as all the names we should respond to (org.kde.kcookiejar, org.kde.khotkeys etc.)
639 // so that the calling code is independent from the physical "location" of the service.
640 const QList<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf6/kded"));
641 for (const KPluginMetaData &metaData : plugins) {
642 const QString serviceName = metaData.value(QStringLiteral("X-KDE-DBus-ServiceName"));
643 if (serviceName.isEmpty()) {
644 continue;
645 }
646 // register them queued as an old kded could be running at this point
648 qCWarning(KDED) << "Couldn't register name" << serviceName << "with DBUS - another process owns it already!";
649 }
650 }
652
653 KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kded5rc"));
654 KConfigGroup cg(config, QStringLiteral("General"));
655
656 bCheckSycoca = cg.readEntry("CheckSycoca", true);
657 bCheckUpdates = cg.readEntry("CheckUpdates", true);
658 delayedCheck = cg.readEntry("DelayedCheck", false);
659
660 signal(SIGTERM, sighandler);
661 signal(SIGHUP, sighandler);
662
664
665 std::unique_ptr<Kded> kded = std::make_unique<Kded>();
666
667 kded->recreate(true); // initial
668
669 if (bCheckUpdates) {
670 (void)new KUpdateD; // Watch for updates
671 }
672
673 runKonfUpdate(); // Run it once.
674
675 return app.exec(); // keep running
676}
677
678#include "moc_kded.cpp"
static QString moduleForMessage(const QDBusMessage &message)
void setModuleName(const QString &name)
bool contains(const QString &path) const
void deleted(const QString &path)
void addDir(const QString &path, WatchModes watchModes=WatchDirOnly)
void dirty(const QString &path)
void created(const QString &path)
static Result< KPluginFactory > loadFactory(const KPluginMetaData &data)
QString pluginId() const
bool value(const QString &key, bool defaultValue) const
QString fileName() const
static QList< KPluginMetaData > findPlugins(const QString &directory, std::function< bool(const KPluginMetaData &)> filter={}, KPluginMetaDataOptions options={})
bool isValid() const
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QStringList allResourceDirs()
static KSycoca * self()
void ensureCacheValid()
KCRASH_EXPORT void setFlags(KCrash::CrashFlags flags)
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
char * data()
bool isEmpty() const const
int toInt(bool *ok, int base) const const
QCommandLineOption addHelpOption()
bool addOption(const QCommandLineOption &option)
QCommandLineOption addVersionOption()
bool isSet(const QCommandLineOption &option) const const
void process(const QCoreApplication &app)
void setApplicationName(const QString &application)
void setApplicationVersion(const QString &version)
void setOrganizationDomain(const QString &orgDomain)
void setQuitLockEnabled(bool enabled)
QDBusConnectionInterface * interface() const const
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
bool send(const QDBusMessage &message) const const
QDBusConnection sessionBus()
QDBusReply< QDBusConnectionInterface::RegisterServiceReply > registerService(const QString &serviceName, ServiceQueueOptions qoption, ServiceReplacementOptions roption)
QDBusMessage createSignal(const QString &path, const QString &interface, const QString &name)
void addWatchedService(const QString &newService)
bool removeWatchedService(const QString &service)
void serviceUnregistered(const QString &serviceName)
QString homePath()
void setQuitOnLastWindowClosed(bool quit)
const_iterator cbegin() const const
const_iterator cend() const const
void clear()
bool contains(const Key &key) const const
iterator insert(const Key &key, const T &value)
QList< Key > keys() const const
bool remove(const Key &key)
T value(const Key &key) const const
void append(QList< T > &&value)
bool isEmpty() const const
qsizetype removeAll(const AT &t)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * sender() const const
int execute(const QString &program, const QStringList &arguments)
iterator insert(const T &value)
bool remove(const T &value)
QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options)
QStringList standardLocations(StandardLocation type)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void start()
void stop()
void timeout()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 14 2024 11:59:05 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.