12#include <config-kwin.h>
22#include <KConfigGroup>
23#include <KPackage/Package>
24#include <KPackage/PackageLoader>
27#include <QFutureWatcher>
28#include <QPluginLoader>
29#include <QQmlComponent>
31#include <QStaticPlugin>
33#include <QtConcurrentRun>
55 KConfigGroup plugins(m_config, QStringLiteral(
"Plugins"));
57 const QString key = effectName + QStringLiteral(
"Enabled");
60 if (plugins.hasKey(key)) {
62 const bool load = plugins.readEntry(key, defaultValue);
69 return LoadEffectFlags();
72static const QString s_serviceType = QStringLiteral(
"KWin/Effect");
86 return findEffect(name).isValid();
100 const auto effects = findAllEffects();
102 for (
const auto &service :
effects) {
103 result << service.pluginId();
110 auto effect = findEffect(name);
111 if (!effect.isValid()) {
119 const QString name = effect.pluginId();
121 qCDebug(KWIN_CORE) <<
"Loading flags disable effect: " << name;
125 if (m_loadedEffects.contains(name)) {
126 qCDebug(KWIN_CORE) << name <<
"already loaded";
130 const QString api = effect.value(QStringLiteral(
"X-Plasma-API"));
131 if (api == QLatin1String(
"javascript")) {
132 return loadJavascriptEffect(effect);
133 }
else if (api == QLatin1String(
"declarativescript")) {
134 return loadDeclarativeEffect(effect);
136 qCWarning(KWIN_CORE,
"Failed to load %s effect: invalid X-Plasma-API field: %s. "
137 "Available options are javascript, and declarativescript", qPrintable(name), qPrintable(api));
143bool ScriptedEffectLoader::loadJavascriptEffect(
const KPluginMetaData &effect)
145 const QString name = effect.pluginId();
147 qCDebug(KWIN_CORE) <<
"Effect is not supported: " << name;
153 qCDebug(KWIN_CORE) <<
"Could not initialize scripted effect: " << name;
156 connect(e, &ScriptedEffect::destroyed,
this, [
this, name]() {
157 m_loadedEffects.removeAll(name);
160 qCDebug(KWIN_CORE) <<
"Successfully loaded scripted effect: " << name;
162 m_loadedEffects << name;
166bool ScriptedEffectLoader::loadDeclarativeEffect(
const KPluginMetaData &metadata)
168 const QString name = metadata.pluginId();
169 const QString scriptFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
170 QLatin1String(
"kwin/effects/") + name + QLatin1String(
"/contents/ui/main.qml"));
171 if (scriptFile.isNull()) {
172 qCWarning(KWIN_CORE) <<
"Could not locate the effect script";
177 QQmlComponent component(engine);
178 component.loadUrl(QUrl::fromLocalFile(scriptFile));
179 if (component.isError()) {
180 qCWarning(KWIN_CORE).nospace() <<
"Failed to load " << scriptFile <<
": " << component.errors();
184 QObject *
object = component.beginCreate(engine->rootContext());
185 auto effect = qobject_cast<ScriptedQuickSceneEffect *>(
object);
187 qCDebug(KWIN_CORE) <<
"Could not initialize scripted effect: " << name;
191 effect->setMetaData(metadata);
192 component.completeCreate();
194 connect(effect, &Effect::destroyed,
this, [
this, name]() {
195 m_loadedEffects.removeAll(name);
198 qCDebug(KWIN_CORE) <<
"Successfully loaded scripted effect: " << name;
200 m_loadedEffects << name;
206 if (m_queryConnection) {
211 m_queryConnection = connect(
212 watcher, &
QFutureWatcher<QList<KPluginMetaData>>::finished,
this, [
this, watcher]() {
213 const auto effects = watcher->result();
214 for (
const auto &effect :
effects) {
215 const LoadEffectFlags flags =
readConfig(effect.pluginId(), effect.isEnabledByDefault());
217 m_queue->enqueue(qMakePair(effect, flags));
220 watcher->deleteLater();
221 m_queryConnection = QMetaObject::Connection();
223 Qt::QueuedConnection);
224 watcher->setFuture(QtConcurrent::run(&ScriptedEffectLoader::findAllEffects,
this));
227QList<KPluginMetaData> ScriptedEffectLoader::findAllEffects()
const
229 return KPackage::PackageLoader::self()->listPackages(s_serviceType, QStringLiteral(
"kwin/effects"));
232KPluginMetaData ScriptedEffectLoader::findEffect(
const QString &name)
const
234 const auto plugins = KPackage::PackageLoader::self()->findPackages(s_serviceType, QStringLiteral(
"kwin/effects"),
235 [name](
const KPluginMetaData &metadata) {
236 return metadata.pluginId().compare(name, Qt::CaseInsensitive) == 0;
238 if (!plugins.isEmpty()) {
239 return plugins.first();
241 return KPluginMetaData();
246 disconnect(m_queryConnection);
247 m_queryConnection = QMetaObject::Connection();
253 , m_pluginSubDirectory(QStringLiteral(
"kwin/effects/plugins"))
263 const auto info = findEffect(name);
264 return info.isValid();
267KPluginMetaData PluginEffectLoader::findEffect(
const QString &name)
const
269 const auto plugins = KPluginMetaData::findPlugins(m_pluginSubDirectory,
270 [name](
const KPluginMetaData &data) {
271 return data.pluginId().compare(name, Qt::CaseInsensitive) == 0;
273 if (plugins.isEmpty()) {
274 return KPluginMetaData();
276 return plugins.first();
282 return effectFactory->isSupported();
289 if (!info.isValid()) {
292 KPluginFactory *factory;
293 if (info.isStaticPlugin()) {
296 factory = KPluginFactory::loadFactory(info).plugin;
298 QPluginLoader loader(info.fileName());
300 qCDebug(KWIN_CORE) << info.pluginId() <<
" has not matching plugin version, expected " <<
PluginFactory_iid <<
"got "
301 << loader.metaData().value(
"IID");
304 factory = qobject_cast<KPluginFactory *>(loader.instance());
307 qCDebug(KWIN_CORE) <<
"Did not get KPluginFactory for " << info.pluginId();
310 return dynamic_cast<EffectPluginFactory *
>(factory);
315 const auto plugins = findAllEffects();
317 for (
const auto &plugin : plugins) {
318 result << plugin.pluginId();
320 qCDebug(KWIN_CORE) << result;
326 const auto info = findEffect(name);
327 if (!info.isValid()) {
335 if (!info.isValid()) {
336 qCDebug(KWIN_CORE) <<
"Plugin info is not valid";
339 const QString name = info.pluginId();
341 qCDebug(KWIN_CORE) <<
"Loading flags disable effect: " << name;
344 if (m_loadedEffects.contains(name)) {
345 qCDebug(KWIN_CORE) << name <<
" already loaded";
349 if (!effectFactory) {
350 qCDebug(KWIN_CORE) <<
"Couldn't get an EffectPluginFactory for: " << name;
356 qCDebug(KWIN_CORE) <<
"Effect is not supported: " << name;
362 qCDebug(KWIN_CORE) <<
"Enabled by default function disables effect: " << name;
370 qCDebug(KWIN_CORE) <<
"Failed to create effect: " << name;
374 m_loadedEffects << name;
375 connect(e, &Effect::destroyed,
this, [
this, name]() {
376 m_loadedEffects.removeAll(name);
378 qCDebug(KWIN_CORE) <<
"Successfully loaded plugin effect: " << name;
385 const auto effects = findAllEffects();
386 for (
const auto &effect :
effects) {
387 const LoadEffectFlags flags =
readConfig(effect.pluginId(), effect.isEnabledByDefault());
394QList<KPluginMetaData> PluginEffectLoader::findAllEffects()
const
396 return KPluginMetaData::findPlugins(m_pluginSubDirectory);
401 m_pluginSubDirectory = directory;
413 for (
auto it = m_loaders.constBegin(); it != m_loaders.constEnd(); ++it) {
424 return std::any_of(m_loaders.cbegin(), m_loaders.cend(), [&name](
const auto &loader) {
425 return loader->hasEffect(name);
431 return std::any_of(m_loaders.cbegin(), m_loaders.cend(), [&name](
const auto &loader) {
432 return loader->isEffectSupported(name);
439 for (
auto it = m_loaders.constBegin(); it != m_loaders.constEnd(); ++it) {
440 result << (*it)->listOfKnownEffects();
447 for (
auto it = m_loaders.constBegin(); it != m_loaders.constEnd(); ++it) {
448 if ((*it)->loadEffect(name)) {
457 for (
auto it = m_loaders.constBegin(); it != m_loaders.constEnd(); ++it) {
458 (*it)->queryAndLoadAll();
465 for (
auto it = m_loaders.constBegin(); it != m_loaders.constEnd(); ++it) {
466 (*it)->setConfig(config);
472 for (
auto it = m_loaders.constBegin(); it != m_loaders.constEnd(); ++it) {
479#include "moc_effectloader.cpp"
Interface to describe how an effect loader has to function.
void effectLoaded(KWin::Effect *effect, const QString &name)
The loader emits this signal when it successfully loaded an effect.
~AbstractEffectLoader() override
AbstractEffectLoader(QObject *parent=nullptr)
LoadEffectFlags readConfig(const QString &effectName, bool defaultValue) const
Checks the configuration for the Effect identified by effectName.
virtual void setConfig(KSharedConfig::Ptr config)
The KSharedConfig this EffectLoader should operate on.
Base class for all KWin effects.
void clear() override
Clears the load queue, that is all scheduled Effects are discarded from loading.
QStringList listOfKnownEffects() const override
All the Effects this loader knows of.
void setConfig(KSharedConfig::Ptr config) override
The KSharedConfig this EffectLoader should operate on.
bool hasEffect(const QString &name) const override
Whether this Effect Loader can load the Effect with the given name.
EffectLoader(QObject *parent=nullptr)
bool isEffectSupported(const QString &name) const override
Whether the Effect with the given name is supported by the compositing backend.
void queryAndLoadAll() override
The Effect Loader should query its store for all available effects and try to load them.
bool loadEffect(const QString &name) override
Synchronous loading of the Effect with the given name.
bool makeOpenGLContextCurrent()
Makes the OpenGL compositing context current.
void setPluginSubDirectory(const QString &directory)
bool isEffectSupported(const QString &name) const override
Whether the Effect with the given name is supported by the compositing backend.
bool loadEffect(const QString &name) override
Synchronous loading of the Effect with the given name.
void clear() override
Clears the load queue, that is all scheduled Effects are discarded from loading.
bool hasEffect(const QString &name) const override
Whether this Effect Loader can load the Effect with the given name.
void queryAndLoadAll() override
The Effect Loader should query its store for all available effects and try to load them.
QStringList listOfKnownEffects() const override
All the Effects this loader knows of.
PluginEffectLoader(QObject *parent=nullptr)
~PluginEffectLoader() override
static ScriptedEffect * create(const QString &effectName, const QString &pathToScript, int chainPosition, const QString &exclusiveCategory)
Can load scripted Effects.
bool hasEffect(const QString &name) const override
Whether this Effect Loader can load the Effect with the given name.
bool loadEffect(const QString &name) override
Synchronous loading of the Effect with the given name.
bool isEffectSupported(const QString &name) const override
Whether the Effect with the given name is supported by the compositing backend.
void clear() override
Clears the load queue, that is all scheduled Effects are discarded from loading.
void queryAndLoadAll() override
The Effect Loader should query its store for all available effects and try to load them.
ScriptedEffectLoader(QObject *parent=nullptr)
~ScriptedEffectLoader() override
QStringList listOfKnownEffects() const override
All the Effects this loader knows of.
static Scripting * self()
QQmlEngine * qmlEngine() const
virtual bool isSupported() const
#define EffectPluginFactory_iid
virtual KWin::Effect * createEffect() const =0
virtual bool enabledByDefault() const
@ CheckDefaultFunction
The Check Default Function needs to be invoked if the Effect provides it.
@ Load
Effect should be loaded.
#define PluginFactory_iid