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");
297 emit runner->prepare();
304 void jobDone(ThreadWeaver::Job *job)
306 FindMatchesJob *runJob =
dynamic_cast<FindMatchesJob *
>(job);
312 if (deferredRun.isEnabled() && runJob->runner() == deferredRun.runner()) {
314 QueryMatch tmpRun = deferredRun;
315 deferredRun = QueryMatch(0);
319 searchJobs.remove(runJob);
320 oldSearchJobs.remove(runJob);
321 runJob->deleteLater();
323 if (searchJobs.isEmpty() && context.matches().isEmpty()) {
327 emit q->matchesChanged(context.matches());
337 if (!prepped || !teardownRequested) {
341 if (Weaver::instance()->isIdle()) {
342 qDeleteAll(searchJobs);
344 qDeleteAll(oldSearchJobs);
345 oldSearchJobs.clear();
348 if (searchJobs.isEmpty() && oldSearchJobs.isEmpty()) {
349 if (allRunnersPrepped) {
350 foreach (AbstractRunner *runner, runners) {
351 emit runner->teardown();
354 allRunnersPrepped =
false;
357 if (singleRunnerPrepped) {
358 if (currentSingleRunner) {
359 emit currentSingleRunner->teardown();
362 singleRunnerPrepped =
false;
365 emit q->queryFinished();
368 teardownRequested =
false;
375 if (searchJobs.isEmpty() && Weaver::instance()->isIdle()) {
376 qDeleteAll(oldSearchJobs);
377 oldSearchJobs.clear();
382 DummyJob *dummy =
new DummyJob(q);
383 Weaver::instance()->enqueue(dummy);
384 QObject::connect(dummy, SIGNAL(done(ThreadWeaver::Job*)), dummy, SLOT(deleteLater()));
387 void runnerMatchingSuspended(
bool suspended)
389 if (suspended || !prepped || teardownRequested) {
393 AbstractRunner *runner = qobject_cast<AbstractRunner *>(q->sender());
400 void startJob(AbstractRunner *runner)
402 if ((runner->ignoredTypes() & context.type()) == 0) {
403 FindMatchesJob *job =
new FindMatchesJob(runner, &context, Weaver::instance());
404 QObject::connect(job, SIGNAL(done(ThreadWeaver::Job*)), q, SLOT(jobDone(ThreadWeaver::Job*)));
406 job->setDelayTimer(&delayTimer);
408 Weaver::instance()->enqueue(job);
409 searchJobs.insert(job);
414 static const int slowRunDelay = 400;
417 QueryMatch deferredRun;
418 RunnerContext context;
419 QTimer matchChangeTimer;
421 QHash<QString, AbstractRunner*> runners;
422 QHash<QString, QString> advertiseSingleRunnerIds;
423 AbstractRunner* currentSingleRunner;
424 QSet<FindMatchesJob*> searchJobs;
425 QSet<FindMatchesJob*> oldSearchJobs;
427 QString singleModeRunnerId;
430 bool allRunnersPrepped : 1;
431 bool singleRunnerPrepped : 1;
432 bool teardownRequested : 1;
434 bool singleRunnerWasLoaded : 1;
443 d(new RunnerManagerPrivate(this))
445 d->loadConfiguration();
451 d(new RunnerManagerPrivate(this))
455 d->conf = KConfigGroup(&c,
"PlasmaRunnerManager");
456 d->loadConfiguration();
462 if (!qApp->closingDown() && (!d->searchJobs.isEmpty() || !d->oldSearchJobs.isEmpty())) {
463 new DelayedJobCleaner(d->searchJobs + d->oldSearchJobs);
471 d->loadConfiguration();
477 KConfigGroup config = d->configGroup();
478 config.writeEntry(
"pluginWhiteList", runners);
480 if (!d->runners.isEmpty()) {
488 KConfigGroup config = d->configGroup();
489 return config.readEntry(
"pluginWhiteList", QStringList());
494 KPluginInfo description(service);
495 const QString runnerName = description.pluginName();
496 if (!runnerName.isEmpty() && !d->runners.contains(runnerName)) {
499 d->runners.insert(runnerName, runner);
506 if (!d->runners.contains(path)) {
508 connect(runner, SIGNAL(matchingSuspended(
bool)),
this, SLOT(runnerMatchingSuspended(
bool)));
509 d->runners.insert(path, runner);
515 if (d->runners.isEmpty()) {
519 return d->runners.value(name, 0);
524 return d->currentSingleRunner;
529 d->singleModeRunnerId = id;
530 d->loadSingleRunner();
535 return d->singleModeRunnerId;
540 return d->singleMode;
545 if (d->singleMode == singleMode) {
552 d->loadSingleRunner();
553 d->singleMode = d->currentSingleRunner;
555 if (prevSingleRunner != d->currentSingleRunner) {
568 return d->runners.values();
573 return d->advertiseSingleRunnerIds.keys();
581 return d->advertiseSingleRunnerIds.value(
id, QString());
593 return d->context.matches();
598 run(d->context.match(
id));
610 foreach (FindMatchesJob *job, d->searchJobs) {
611 if (job->runner() == runner && !job->isFinished()) {
612 kDebug() <<
"deferred run";
613 d->deferredRun = match;
618 if (d->deferredRun.isValid()) {
622 d->context.run(match);
632 return QList<QAction*>();
645 if (runner && QMetaObject::invokeMethod(
647 "mimeDataForMatch", Qt::DirectConnection,
648 Q_RETURN_ARG(QMimeData*, mimeData),
664 d->teardownRequested =
false;
672 if (d->currentSingleRunner) {
673 emit d->currentSingleRunner->prepare();
674 d->singleRunnerPrepped =
true;
678 #ifdef MEASURE_PREPTIME
683 #ifdef MEASURE_PREPTIME
684 kDebug() << t.elapsed() << runner->
name();
688 d->allRunnersPrepped =
true;
698 d->teardownRequested =
true;
710 QString term = untrimmedTerm.trimmed();
715 if (term.isEmpty()) {
716 if (d->singleMode && d->currentSingleRunner && d->currentSingleRunner->defaultSyntax()) {
717 term = d->currentSingleRunner->defaultSyntax()->exampleQueries().first().remove(QRegExp(
":q:"));
724 if (d->context.query() == term) {
729 if (d->singleMode && !d->currentSingleRunner) {
734 if (d->runners.isEmpty()) {
740 d->context.setQuery(term);
742 QHash<QString, AbstractRunner*> runable;
745 if (d->singleMode && d->currentSingleRunner) {
746 runable.insert(QString(), d->currentSingleRunner);
747 d->context.setSingleRunnerQueryMode(
true);
749 runable = d->runners;
761 d->delayTimer.start(RunnerManagerPrivate::slowRunDelay);
771 QString term = untrimmedTerm.trimmed();
773 if (term.isEmpty()) {
778 if (d->runners.isEmpty()) {
782 if (d->context.query() == term) {
790 d->context.setQuery(term);
811 return d->context.query();
817 if (Weaver::instance()->isIdle()) {
818 qDeleteAll(d->searchJobs);
819 qDeleteAll(d->oldSearchJobs);
820 d->oldSearchJobs.clear();
822 Q_FOREACH(FindMatchesJob *job, d->searchJobs) {
823 Weaver::instance()->dequeue(job);
825 d->oldSearchJobs += d->searchJobs;
828 d->searchJobs.clear();
830 if (d->deferredRun.isEnabled()) {
834 tmpRun.
run(d->context);
842 #include "runnermanager.moc"
static PluginLoader * pluginLoader()
Return the active plugin loader.
QStringList allowedRunners() const
AbstractRunner * singleModeRunner() const
QStringList singleModeAdvertisedRunnerIds() const
An abstract base class for Plasma Runner plugins.
void setSingleMode(bool singleMode)
Sets whether or not the manager is in single mode.
void setupMatchSession()
Call this method when the runners should be prepared for a query session.
AbstractRunner * loadRunner(const QString &name)
Load a Runner plugin.
RunnerManager(QObject *parent=0)
void run(const RunnerContext &context) const
Requests this match to activae using the given context.
void setAllowedRunners(const QStringList &runners)
Sets a whitelist for the plugins that can be loaded.
void reloadConfiguration()
Causes a reload of the current configuration.
void setSingleModeRunnerId(const QString &id)
Puts the manager into "single runner" mode using the given runner; if the runner does not exist or ca...
virtual QList< QAction * > actionsForMatch(const Plasma::QueryMatch &match)
A given match can have more than action that can be performed on it.
void loadRunner(const KService::Ptr service)
Attempts to add the AbstractRunner plugin represented by the KService passed in.
QList< AbstractRunner * > runners() const
bool isPluginVersionCompatible(unsigned int version)
Verifies that a plugin is compatible with plasma.
void matchesChanged(const QList< Plasma::QueryMatch > &matches)
Emitted each time a new match is added to the list.
QMimeData * mimeDataForMatch(const QueryMatch &match) const
QList< QueryMatch > matches() const
Retrieves all available matches found so far for the previously launched query.
void prepare()
This signal is emitted when matching is about to commence, giving runners an opportunity to prepare t...
RunnerContext * searchContext() const
Retrieves the current context.
bool isMatchingSuspended() const
The RunnerContext class provides information related to a search, including the search term...
void run(const QueryMatch &match)
Runs a given match.
A match returned by an AbstractRunner in response to a given RunnerContext.
QString singleModeRunnerId() const
KPluginInfo::List listRunnerInfo(const QString &parentApp=QString())
Returns a list of all known Runner implementations.
void launchQuery(const QString &term, const QString &runnerId)
Launch a query, this will create threads and return inmediately.
RunnerContext::Types ignoredTypes() const
Returns the OR'ed value of all the Information types (as defined in RunnerContext::Type) this runner ...
AbstractRunner * runner() const
void performMatch(Plasma::RunnerContext &context)
Triggers a call to match.
AbstractRunner * runner(const QString &name) const
Finds and returns a loaded runner or NULL.
QString runnerName(const QString &id) const
Returns the translated name of a runner.
QList< QAction * > actionsForMatch(const QueryMatch &match)
Retrieves the list of actions, if any, for a match.
void reset()
Reset the current data and stops the query.
void matchSessionComplete()
Call this method when the query session is finished for the time being.
static KPluginInfo::List listRunnerInfo(const QString &parentApp=QString())
Returns a list of all known Runner implementations.
bool execQuery(const QString &term, const QString &runnerName)
Execute a query, this method will only return when the query is executed This means that the method m...