24 #include "config-plasma.h"
28 #include <QCoreApplication>
31 #include <kplugininfo.h>
32 #include <kservicetypetrader.h>
33 #include <kstandarddirs.h>
35 #ifndef PLASMA_NO_SOLID
36 #include <solid/device.h>
37 #include <solid/deviceinterface.h>
40 #include <Weaver/DebuggingAids.h>
41 #include <Weaver/State.h>
42 #include <Weaver/Thread.h>
43 #include <Weaver/ThreadWeaver.h>
45 #include "private/runnerjobs_p.h"
49 using ThreadWeaver::Weaver;
50 using ThreadWeaver::Job;
61 class RunnerManagerPrivate
65 RunnerManagerPrivate(RunnerManager *parent)
68 currentSingleRunner(0),
70 allRunnersPrepped(false),
71 singleRunnerPrepped(false),
72 teardownRequested(false),
74 singleRunnerWasLoaded(false)
76 matchChangeTimer.setSingleShot(
true);
77 delayTimer.setSingleShot(
true);
79 QObject::connect(&matchChangeTimer, SIGNAL(timeout()), q, SLOT(matchesChanged()));
80 QObject::connect(&context, SIGNAL(matchesChanged()), q, SLOT(scheduleMatchesChanged()));
81 QObject::connect(&delayTimer, SIGNAL(timeout()), q, SLOT(unblockJobs()));
84 ~RunnerManagerPrivate()
86 KConfigGroup config = configGroup();
90 void scheduleMatchesChanged()
92 matchChangeTimer.start(100);
97 emit q->matchesChanged(context.matches());
100 void loadConfiguration()
102 KConfigGroup config = configGroup();
105 #ifndef PLASMA_NO_SOLID
107 qMax(Solid::Device::listFromType(Solid::DeviceInterface::Processor).count(), 1);
109 const int numProcs = 1;
112 const int maxThreads = config.readEntry(
"maxThreads", 16);
113 const int numThreads = qMin(maxThreads, 2 + ((numProcs - 1) * 2));
115 if (numThreads > Weaver::instance()->maximumNumberOfThreads()) {
116 Weaver::instance()->setMaximumNumberOfThreads(numThreads);
120 const int cap = qMax(2, numThreads/2);
121 DefaultRunnerPolicy::instance().setCap(cap);
123 context.restore(config);
126 KConfigGroup configGroup()
128 return conf.isValid() ? conf : KConfigGroup(KGlobal::config(),
"PlasmaRunnerManager");
131 void clearSingleRunner()
133 if (singleRunnerWasLoaded) {
134 delete currentSingleRunner;
137 currentSingleRunner = 0;
140 void loadSingleRunner()
142 if (!singleMode || singleModeRunnerId.isEmpty()) {
147 if (currentSingleRunner) {
148 if (currentSingleRunner->id() == singleModeRunnerId) {
155 AbstractRunner *loadedRunner = q->runner(singleModeRunnerId);
157 singleRunnerWasLoaded =
false;
158 currentSingleRunner = loadedRunner;
162 KService::List offers = KServiceTypeTrader::self()->query(
"Plasma/Runner", QString(
"[X-KDE-PluginInfo-Name] == '%1'").arg(singleModeRunnerId));
163 if (!offers.isEmpty()) {
164 const KService::Ptr &service = offers[0];
165 currentSingleRunner = loadInstalledRunner(service);
167 if (currentSingleRunner) {
168 emit currentSingleRunner->prepare();
169 singleRunnerWasLoaded =
true;
176 KConfigGroup config = configGroup();
179 const bool loadAll = config.readEntry(
"loadAll",
false);
180 const QStringList whiteList = config.readEntry(
"pluginWhiteList", QStringList());
181 const bool noWhiteList = whiteList.isEmpty();
182 KConfigGroup pluginConf;
183 if (conf.isValid()) {
184 pluginConf = KConfigGroup(&conf,
"Plugins");
186 pluginConf = KConfigGroup(KGlobal::config(),
"Plugins");
189 advertiseSingleRunnerIds.clear();
191 QSet<AbstractRunner *> deadRunners;
192 QMutableListIterator<KPluginInfo> it(offers);
193 while (it.hasNext()) {
194 KPluginInfo &description = it.next();
196 QString tryExec = description.property(
"TryExec").toString();
198 if (!tryExec.isEmpty() && KStandardDirs::findExe(tryExec).isEmpty()) {
203 const QString runnerName = description.pluginName();
204 description.load(pluginConf);
206 const bool loaded = runners.contains(runnerName);
207 const bool selected = loadAll || (description.isPluginEnabled() && (noWhiteList || whiteList.contains(runnerName)));
209 const bool singleQueryModeEnabled = description.property(
"X-Plasma-AdvertiseSingleRunnerQueryMode").toBool();
211 if (singleQueryModeEnabled) {
212 advertiseSingleRunnerIds.insert(runnerName, description.name());
218 AbstractRunner *runner = loadInstalledRunner(description.service());
221 runners.insert(runnerName, runner);
226 deadRunners.insert(runners.take(runnerName));
227 kDebug() <<
"Removing runner: " << runnerName;
231 if (!deadRunners.isEmpty()) {
232 QSet<FindMatchesJob *> deadJobs;
233 foreach (FindMatchesJob *job, searchJobs) {
234 if (deadRunners.contains(job->runner())) {
235 QObject::disconnect(job, SIGNAL(done(ThreadWeaver::Job*)), q, SLOT(jobDone(ThreadWeaver::Job*)));
236 searchJobs.remove(job);
237 deadJobs.insert(job);
241 foreach (FindMatchesJob *job, oldSearchJobs) {
242 if (deadRunners.contains(job->runner())) {
243 oldSearchJobs.remove(job);
244 deadJobs.insert(job);
248 if (deadJobs.isEmpty()) {
249 qDeleteAll(deadRunners);
251 new DelayedJobCleaner(deadJobs, deadRunners);
255 if (!singleRunnerWasLoaded) {
260 kDebug() <<
"All runners loaded, total:" << runners.count();
263 AbstractRunner *loadInstalledRunner(
const KService::Ptr service)
272 runner->setParent(q);
274 const QString api = service->property(
"X-Plasma-API").toString();
278 args << service->storageId();
281 runner = service->createInstance<AbstractRunner>(q, args, &error);
283 kDebug() <<
"Failed to load runner:" << service->
name() <<
". error reported:" << error;
288 runner =
new AbstractRunner(service, q);
293 kDebug() <<
"================= loading runner:" << service->name() <<
"=================";
294 QObject::connect(runner, SIGNAL(matchingSuspended(
bool)), q, SLOT(runnerMatchingSuspended(
bool)));
295 QMetaObject::invokeMethod(runner,
"init");
301 void jobDone(ThreadWeaver::Job *job)
303 FindMatchesJob *runJob =
dynamic_cast<FindMatchesJob *
>(job);
309 if (deferredRun.isEnabled() && runJob->runner() == deferredRun.runner()) {
311 QueryMatch tmpRun = deferredRun;
312 deferredRun = QueryMatch(0);
316 searchJobs.remove(runJob);
317 oldSearchJobs.remove(runJob);
318 runJob->deleteLater();
320 if (searchJobs.isEmpty() && context.matches().isEmpty()) {
324 emit q->matchesChanged(context.matches());
334 if (!prepped || !teardownRequested) {
338 if (Weaver::instance()->isIdle()) {
339 qDeleteAll(searchJobs);
341 qDeleteAll(oldSearchJobs);
342 oldSearchJobs.clear();
345 if (searchJobs.isEmpty() && oldSearchJobs.isEmpty()) {
346 if (allRunnersPrepped) {
347 foreach (AbstractRunner *runner, runners) {
348 emit runner->teardown();
351 allRunnersPrepped =
false;
354 if (singleRunnerPrepped) {
355 if (currentSingleRunner) {
356 emit currentSingleRunner->teardown();
359 singleRunnerPrepped =
false;
362 emit q->queryFinished();
365 teardownRequested =
false;
372 if (searchJobs.isEmpty() && Weaver::instance()->isIdle()) {
373 qDeleteAll(oldSearchJobs);
374 oldSearchJobs.clear();
379 DummyJob *dummy =
new DummyJob(q);
380 Weaver::instance()->enqueue(dummy);
381 QObject::connect(dummy, SIGNAL(done(ThreadWeaver::Job*)), dummy, SLOT(deleteLater()));
384 void runnerMatchingSuspended(
bool suspended)
386 if (suspended || !prepped || teardownRequested) {
390 AbstractRunner *runner = qobject_cast<AbstractRunner *>(q->sender());
397 void startJob(AbstractRunner *runner)
399 if ((runner->ignoredTypes() & context.type()) == 0) {
400 FindMatchesJob *job =
new FindMatchesJob(runner, &context, Weaver::instance());
401 QObject::connect(job, SIGNAL(done(ThreadWeaver::Job*)), q, SLOT(jobDone(ThreadWeaver::Job*)));
403 job->setDelayTimer(&delayTimer);
405 Weaver::instance()->enqueue(job);
406 searchJobs.insert(job);
411 static const int slowRunDelay = 400;
414 QueryMatch deferredRun;
415 RunnerContext context;
416 QTimer matchChangeTimer;
418 QHash<QString, AbstractRunner*> runners;
419 QHash<QString, QString> advertiseSingleRunnerIds;
420 AbstractRunner* currentSingleRunner;
421 QSet<FindMatchesJob*> searchJobs;
422 QSet<FindMatchesJob*> oldSearchJobs;
424 QString singleModeRunnerId;
427 bool allRunnersPrepped : 1;
428 bool singleRunnerPrepped : 1;
429 bool teardownRequested : 1;
431 bool singleRunnerWasLoaded : 1;
440 d(new RunnerManagerPrivate(this))
442 d->loadConfiguration();
448 d(new RunnerManagerPrivate(this))
452 d->conf = KConfigGroup(&c,
"PlasmaRunnerManager");
453 d->loadConfiguration();
459 if (!qApp->closingDown() && (!d->searchJobs.isEmpty() || !d->oldSearchJobs.isEmpty())) {
460 new DelayedJobCleaner(d->searchJobs + d->oldSearchJobs);
468 d->loadConfiguration();
474 KConfigGroup config = d->configGroup();
475 config.writeEntry(
"pluginWhiteList", runners);
477 if (!d->runners.isEmpty()) {
485 KConfigGroup config = d->configGroup();
486 return config.readEntry(
"pluginWhiteList", QStringList());
491 KPluginInfo description(service);
492 const QString runnerName = description.pluginName();
493 if (!runnerName.isEmpty() && !d->runners.contains(runnerName)) {
496 d->runners.insert(runnerName, runner);
503 if (!d->runners.contains(path)) {
505 connect(runner, SIGNAL(matchingSuspended(
bool)),
this, SLOT(runnerMatchingSuspended(
bool)));
506 d->runners.insert(path, runner);
512 if (d->runners.isEmpty()) {
516 return d->runners.value(name, 0);
521 return d->currentSingleRunner;
526 d->singleModeRunnerId = id;
527 d->loadSingleRunner();
532 return d->singleModeRunnerId;
537 return d->singleMode;
542 if (d->singleMode == singleMode) {
549 d->loadSingleRunner();
550 d->singleMode = d->currentSingleRunner;
552 if (prevSingleRunner != d->currentSingleRunner) {
565 return d->runners.values();
570 return d->advertiseSingleRunnerIds.keys();
578 return d->advertiseSingleRunnerIds.value(
id, QString());
590 return d->context.matches();
595 run(d->context.match(
id));
607 foreach (FindMatchesJob *job, d->searchJobs) {
608 if (job->runner() == runner && !job->isFinished()) {
609 kDebug() <<
"deferred run";
610 d->deferredRun = match;
615 if (d->deferredRun.isValid()) {
619 d->context.run(match);
629 return QList<QAction*>();
642 if (runner && QMetaObject::invokeMethod(
644 "mimeDataForMatch", Qt::DirectConnection,
645 Q_RETURN_ARG(QMimeData*, mimeData),
661 d->teardownRequested =
false;
669 if (d->currentSingleRunner) {
670 emit d->currentSingleRunner->prepare();
671 d->singleRunnerPrepped =
true;
675 #ifdef MEASURE_PREPTIME
680 #ifdef MEASURE_PREPTIME
681 kDebug() << t.elapsed() << runner->
name();
685 d->allRunnersPrepped =
true;
695 d->teardownRequested =
true;
707 QString term = untrimmedTerm.trimmed();
712 if (term.isEmpty()) {
713 if (d->singleMode && d->currentSingleRunner && d->currentSingleRunner->defaultSyntax()) {
714 term = d->currentSingleRunner->defaultSyntax()->exampleQueries().first().remove(QRegExp(
":q:"));
721 if (d->context.query() == term) {
726 if (d->singleMode && !d->currentSingleRunner) {
731 if (d->runners.isEmpty()) {
737 d->context.setQuery(term);
739 QHash<QString, AbstractRunner*> runable;
742 if (d->singleMode && d->currentSingleRunner) {
743 runable.insert(QString(), d->currentSingleRunner);
744 d->context.setSingleRunnerQueryMode(
true);
746 runable = d->runners;
758 d->delayTimer.start(RunnerManagerPrivate::slowRunDelay);
768 QString term = untrimmedTerm.trimmed();
770 if (term.isEmpty()) {
775 if (d->runners.isEmpty()) {
779 if (d->context.query() == term) {
787 d->context.setQuery(term);
808 return d->context.query();
814 if (Weaver::instance()->isIdle()) {
815 qDeleteAll(d->searchJobs);
816 qDeleteAll(d->oldSearchJobs);
817 d->oldSearchJobs.clear();
819 Q_FOREACH(FindMatchesJob *job, d->searchJobs) {
820 Weaver::instance()->dequeue(job);
822 d->oldSearchJobs += d->searchJobs;
825 d->searchJobs.clear();
827 if (d->deferredRun.isEnabled()) {
831 tmpRun.
run(d->context);
839 #include "runnermanager.moc"