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