diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index 1cb80b9b86..ce9ccafa5d 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -111,6 +111,7 @@ set( testBuiltInEffectLoader_SRCS add_executable( testBuiltInEffectLoader ${testBuiltInEffectLoader_SRCS}) target_link_libraries(testBuiltInEffectLoader + Qt5::Concurrent Qt5::Test kwineffects kwin4_effect_builtins @@ -118,3 +119,30 @@ target_link_libraries(testBuiltInEffectLoader add_test(kwin-testBuiltInEffectLoader testBuiltInEffectLoader) ecm_mark_as_test(testBuiltInEffectLoader) + +######################################################## +# Test ScriptedEffectLoader +######################################################## +include_directories(${KWIN_SOURCE_DIR}) +set( testScriptedEffectLoader_SRCS + test_scripted_effectloader.cpp + mock_effectshandler.cpp + ../effectloader.cpp + ../scripting/scriptedeffect.cpp + ../scripting/scriptingutils.cpp +) +add_executable( testScriptedEffectLoader ${testScriptedEffectLoader_SRCS}) + +target_link_libraries(testScriptedEffectLoader + Qt5::Concurrent + Qt5::Script + Qt5::Test + KF5::ConfigGui + KF5::GlobalAccel + KF5::I18n + kwineffects + kwin4_effect_builtins +) + +add_test(kwin-testScriptedEffectLoader testScriptedEffectLoader) +ecm_mark_as_test(testScriptedEffectLoader) diff --git a/autotests/test_builtin_effectloader.cpp b/autotests/test_builtin_effectloader.cpp index ad70781ecf..56156496c6 100644 --- a/autotests/test_builtin_effectloader.cpp +++ b/autotests/test_builtin_effectloader.cpp @@ -20,6 +20,7 @@ along with this program. If not, see . #include "../effectloader.h" #include "../effects/effect_builtins.h" #include "mock_effectshandler.h" +#include "../scripting/scriptedeffect.h" // for mocking ScriptedEffect::create // KDE #include #include @@ -32,6 +33,16 @@ Q_DECLARE_METATYPE(KWin::LoadEffectFlags) Q_DECLARE_METATYPE(KWin::BuiltInEffect) Q_DECLARE_METATYPE(KWin::Effect*) +namespace KWin +{ + +ScriptedEffect *ScriptedEffect::create(const QString &, const QString &) +{ + return nullptr; +} + +} + class TestBuiltInEffectLoader : public QObject { Q_OBJECT diff --git a/autotests/test_scripted_effectloader.cpp b/autotests/test_scripted_effectloader.cpp new file mode 100644 index 0000000000..f5921072f5 --- /dev/null +++ b/autotests/test_scripted_effectloader.cpp @@ -0,0 +1,383 @@ +/******************************************************************** +KWin - the KDE window manager +This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "../effectloader.h" +#include "mock_effectshandler.h" +#include "../scripting/scriptedeffect.h" +// for mocking +#include "../input.h" +#include "../screenedge.h" +// KDE +#include +#include +#include +// Qt +#include +#include +Q_DECLARE_METATYPE(KWin::LoadEffectFlag) +Q_DECLARE_METATYPE(KWin::LoadEffectFlags) +Q_DECLARE_METATYPE(KWin::Effect*) + +namespace KWin +{ +ScreenEdges *ScreenEdges::s_self = nullptr; + +void ScreenEdges::reserve(ElectricBorder, QObject *, const char *) +{ +} + +InputRedirection *InputRedirection::s_self = nullptr; + +void InputRedirection::registerShortcut(const QKeySequence &, QAction *) +{ +} + +namespace MetaScripting +{ +void registration(QScriptEngine *) +{ +} +} + +} + +class TestScriptedEffectLoader : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testHasEffect_data(); + void testHasEffect(); + void testKnownEffects(); + void testLoadEffect_data(); + void testLoadEffect(); + void testLoadScriptedEffect_data(); + void testLoadScriptedEffect(); + void testLoadAllEffects(); +}; + +void TestScriptedEffectLoader::testHasEffect_data() +{ + QTest::addColumn("name"); + QTest::addColumn("expected"); + + // all the built-in effects should fail + QTest::newRow("blur") << QStringLiteral("blur") << false; + QTest::newRow("Contrast") << QStringLiteral("contrast") << false; + QTest::newRow("CoverSwitch") << QStringLiteral("coverswitch") << false; + QTest::newRow("Cube") << QStringLiteral("cube") << false; + QTest::newRow("CubeSlide") << QStringLiteral("cubeslide") << false; + QTest::newRow("Dashboard") << QStringLiteral("dashboard") << false; + QTest::newRow("DesktopGrid") << QStringLiteral("desktopgrid") << false; + QTest::newRow("DimInactive") << QStringLiteral("diminactive") << false; + QTest::newRow("DimScreen") << QStringLiteral("dimscreen") << false; + QTest::newRow("FallApart") << QStringLiteral("fallapart") << false; + QTest::newRow("FlipSwitch") << QStringLiteral("flipswitch") << false; + QTest::newRow("Glide") << QStringLiteral("glide") << false; + QTest::newRow("HighlightWindow") << QStringLiteral("highlightwindow") << false; + QTest::newRow("Invert") << QStringLiteral("invert") << false; + QTest::newRow("Kscreen") << QStringLiteral("kscreen") << false; + QTest::newRow("Logout") << QStringLiteral("logout") << false; + QTest::newRow("LookingGlass") << QStringLiteral("lookingglass") << false; + QTest::newRow("MagicLamp") << QStringLiteral("magiclamp") << false; + QTest::newRow("Magnifier") << QStringLiteral("magnifier") << false; + QTest::newRow("MinimizeAnimation") << QStringLiteral("minimizeanimation") << false; + QTest::newRow("MouseClick") << QStringLiteral("mouseclick") << false; + QTest::newRow("MouseMark") << QStringLiteral("mousemark") << false; + QTest::newRow("PresentWindows") << QStringLiteral("presentwindows") << false; + QTest::newRow("Resize") << QStringLiteral("resize") << false; + QTest::newRow("ScreenEdge") << QStringLiteral("screenedge") << false; + QTest::newRow("ScreenShot") << QStringLiteral("screenshot") << false; + QTest::newRow("Sheet") << QStringLiteral("sheet") << false; + QTest::newRow("ShowFps") << QStringLiteral("showfps") << false; + QTest::newRow("ShowPaint") << QStringLiteral("showpaint") << false; + QTest::newRow("Slide") << QStringLiteral("slide") << false; + QTest::newRow("SlideBack") << QStringLiteral("slideback") << false; + QTest::newRow("SlidingPopups") << QStringLiteral("slidingpopups") << false; + QTest::newRow("SnapHelper") << QStringLiteral("snaphelper") << false; + QTest::newRow("StartupFeedback") << QStringLiteral("startupfeedback") << false; + QTest::newRow("ThumbnailAside") << QStringLiteral("thumbnailaside") << false; + QTest::newRow("TrackMouse") << QStringLiteral("trackmouse") << false; + QTest::newRow("WindowGeometry") << QStringLiteral("windowgeometry") << false; + QTest::newRow("WobblyWindows") << QStringLiteral("wobblywindows") << false; + QTest::newRow("Zoom") << QStringLiteral("zoom") << false; + QTest::newRow("Non Existing") << QStringLiteral("InvalidName") << false; + QTest::newRow("Fade - without kwin4_effect") << QStringLiteral("fade") << false; + QTest::newRow("Fade + kwin4_effect") << QStringLiteral("kwin4_effect_fade") << true; + QTest::newRow("Fade + kwin4_effect + CS") << QStringLiteral("kwin4_eFfect_fAde") << true; + QTest::newRow("FadeDesktop") << QStringLiteral("kwin4_effect_fadedesktop") << true; + QTest::newRow("DialogParent") << QStringLiteral("kwin4_effect_dialogparent") << true; + QTest::newRow("Login") << QStringLiteral("kwin4_effect_login") << true; + QTest::newRow("Maximize") << QStringLiteral("kwin4_effect_maximize") << true; + QTest::newRow("ScaleIn") << QStringLiteral("kwin4_effect_scalein") << true; + QTest::newRow("Translucency") << QStringLiteral("kwin4_effect_translucency") << true; +} + +void TestScriptedEffectLoader::testHasEffect() +{ + QFETCH(QString, name); + QFETCH(bool, expected); + + KWin::ScriptedEffectLoader loader; + QCOMPARE(loader.hasEffect(name), expected); + + // each available effect should also be supported + QCOMPARE(loader.isEffectSupported(name), expected); +} + +void TestScriptedEffectLoader::testKnownEffects() +{ + QStringList expectedEffects; + expectedEffects << QStringLiteral("kwin4_effect_dialogparent") + << QStringLiteral("kwin4_effect_fade") + << QStringLiteral("kwin4_effect_fadedesktop") + << QStringLiteral("kwin4_effect_login") + << QStringLiteral("kwin4_effect_maximize") + << QStringLiteral("kwin4_effect_scalein") + << QStringLiteral("kwin4_effect_translucency"); + + KWin::ScriptedEffectLoader loader; + QStringList result = loader.listOfKnownEffects(); + // at least as many effects as we expect - system running the test could have more effects + QVERIFY(result.size() >= expectedEffects.size()); + for (const QString &effect : expectedEffects) { + QVERIFY(result.contains(effect)); + } +} + +void TestScriptedEffectLoader::testLoadEffect_data() +{ + QTest::addColumn("name"); + QTest::addColumn("expected"); + + QTest::newRow("Non Existing") << QStringLiteral("InvalidName") << false; + QTest::newRow("Fade - without kwin4_effect") << QStringLiteral("fade") << false; + QTest::newRow("Fade + kwin4_effect") << QStringLiteral("kwin4_effect_fade") << true; + QTest::newRow("Fade + kwin4_effect + CS") << QStringLiteral("kwin4_eFfect_fAde") << true; + QTest::newRow("FadeDesktop") << QStringLiteral("kwin4_effect_fadedesktop") << true; + QTest::newRow("DialogParent") << QStringLiteral("kwin4_effect_dialogparent") << true; + QTest::newRow("Login") << QStringLiteral("kwin4_effect_login") << true; + QTest::newRow("Maximize") << QStringLiteral("kwin4_effect_maximize") << true; + QTest::newRow("ScaleIn") << QStringLiteral("kwin4_effect_scalein") << true; + QTest::newRow("Translucency") << QStringLiteral("kwin4_effect_translucency") << true; +} + +void TestScriptedEffectLoader::testLoadEffect() +{ + QFETCH(QString, name); + QFETCH(bool, expected); + + MockEffectsHandler mockHandler(KWin::XRenderCompositing); + KWin::ScriptedEffectLoader loader; + KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); + loader.setConfig(config); + + qRegisterMetaType(); + QSignalSpy spy(&loader, SIGNAL(effectLoaded(KWin::Effect*,QString))); + // connect to signal to ensure that we delete the Effect again as the Effect doesn't have a parent + connect(&loader, &KWin::ScriptedEffectLoader::effectLoaded, + [&name](KWin::Effect *effect, const QString &effectName) { + QCOMPARE(effectName, name.toLower()); + effect->deleteLater(); + } + ); + // try to load the Effect + QCOMPARE(loader.loadEffect(name), expected); + // loading again should fail + QVERIFY(!loader.loadEffect(name)); + + // signal spy should have got the signal if it was expected + QCOMPARE(spy.isEmpty(), !expected); + if (!spy.isEmpty()) { + QCOMPARE(spy.count(), 1); + // if we caught a signal it should have the effect name we passed in + QList arguments = spy.takeFirst(); + QCOMPARE(arguments.count(), 2); + QCOMPARE(arguments.at(1).toString(), name.toLower()); + } + spy.clear(); + QVERIFY(spy.isEmpty()); + + // now if we wait for the events being processed, the effect will get deleted and it should load again + QTest::qWait(1); + QCOMPARE(loader.loadEffect(name), expected); + // signal spy should have got the signal if it was expected + QCOMPARE(spy.isEmpty(), !expected); + if (!spy.isEmpty()) { + QCOMPARE(spy.count(), 1); + // if we caught a signal it should have the effect name we passed in + QList arguments = spy.takeFirst(); + QCOMPARE(arguments.count(), 2); + QCOMPARE(arguments.at(1).toString(), name.toLower()); + } +} + +void TestScriptedEffectLoader::testLoadScriptedEffect_data() +{ + QTest::addColumn("name"); + QTest::addColumn("expected"); + QTest::addColumn("loadFlags"); + + const KWin::LoadEffectFlags checkDefault = KWin::LoadEffectFlag::Load | KWin::LoadEffectFlag::CheckDefaultFunction; + const KWin::LoadEffectFlags forceFlags = KWin::LoadEffectFlag::Load; + const KWin::LoadEffectFlags dontLoadFlags = KWin::LoadEffectFlags(); + + // enabled by default + QTest::newRow("Fade") << QStringLiteral("kwin4_effect_fade") << true << checkDefault; + // not enabled by default + QTest::newRow("Scalein") << QStringLiteral("kwin4_effect_scalein") << true << checkDefault; + // Force an Effect which will load + QTest::newRow("Scalein-Force") << QStringLiteral("kwin4_effect_scalein") << true << forceFlags; + // Enforce no load of effect which is enabled by default + QTest::newRow("Fade-DontLoad") << QStringLiteral("kwin4_effect_fade") << false << dontLoadFlags; + // Enforce no load of effect which is not enabled by default, but enforced + QTest::newRow("Scalein-DontLoad") << QStringLiteral("kwin4_effect_scalein") << false << dontLoadFlags; +} + +void TestScriptedEffectLoader::testLoadScriptedEffect() +{ + QFETCH(QString, name); + QFETCH(bool, expected); + QFETCH(KWin::LoadEffectFlags, loadFlags); + + MockEffectsHandler mockHandler(KWin::XRenderCompositing); + KWin::ScriptedEffectLoader loader; + KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); + loader.setConfig(config); + + const auto services = KServiceTypeTrader::self()->query(QStringLiteral("KWin/Effect"), + QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(name)); + QCOMPARE(services.count(), 1); + + qRegisterMetaType(); + QSignalSpy spy(&loader, SIGNAL(effectLoaded(KWin::Effect*,QString))); + // connect to signal to ensure that we delete the Effect again as the Effect doesn't have a parent + connect(&loader, &KWin::ScriptedEffectLoader::effectLoaded, + [&name](KWin::Effect *effect, const QString &effectName) { + QCOMPARE(effectName, name.toLower()); + effect->deleteLater(); + } + ); + // try to load the Effect + QCOMPARE(loader.loadEffect(services.first(), loadFlags), expected); + // loading again should fail + QVERIFY(!loader.loadEffect(services.first(), loadFlags)); + + // signal spy should have got the signal if it was expected + QCOMPARE(spy.isEmpty(), !expected); + if (!spy.isEmpty()) { + QCOMPARE(spy.count(), 1); + // if we caught a signal it should have the effect name we passed in + QList arguments = spy.takeFirst(); + QCOMPARE(arguments.count(), 2); + QCOMPARE(arguments.at(1).toString(), name.toLower()); + } + spy.clear(); + QVERIFY(spy.isEmpty()); + + // now if we wait for the events being processed, the effect will get deleted and it should load again + QTest::qWait(1); + QCOMPARE(loader.loadEffect(services.first(), loadFlags), expected); + // signal spy should have got the signal if it was expected + QCOMPARE(spy.isEmpty(), !expected); + if (!spy.isEmpty()) { + QCOMPARE(spy.count(), 1); + // if we caught a signal it should have the effect name we passed in + QList arguments = spy.takeFirst(); + QCOMPARE(arguments.count(), 2); + QCOMPARE(arguments.at(1).toString(), name.toLower()); + } +} + +void TestScriptedEffectLoader::testLoadAllEffects() +{ + MockEffectsHandler mockHandler(KWin::XRenderCompositing); + KWin::ScriptedEffectLoader loader; + + KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); + + const QString kwin4 = QStringLiteral("kwin4_effect_"); + + // prepare the configuration to hard enable/disable the effects we want to load + KConfigGroup plugins = config->group("Plugins"); + plugins.writeEntry(kwin4 + QStringLiteral("dialogparentEnabled"), false); + plugins.writeEntry(kwin4 + QStringLiteral("fadeEnabled"), false); + plugins.writeEntry(kwin4 + QStringLiteral("fadedesktopEnabled"), false); + plugins.writeEntry(kwin4 + QStringLiteral("loginEnabled"), false); + plugins.writeEntry(kwin4 + QStringLiteral("maximizeEnabled"), false); + plugins.writeEntry(kwin4 + QStringLiteral("minimizeanimationEnabled"), false); + plugins.writeEntry(kwin4 + QStringLiteral("scaleinEnabled"), false); + plugins.writeEntry(kwin4 + QStringLiteral("translucencyEnabled"), false); + plugins.sync(); + + loader.setConfig(config); + + qRegisterMetaType(); + QSignalSpy spy(&loader, SIGNAL(effectLoaded(KWin::Effect*,QString))); + // connect to signal to ensure that we delete the Effect again as the Effect doesn't have a parent + connect(&loader, &KWin::ScriptedEffectLoader::effectLoaded, + [](KWin::Effect *effect) { + effect->deleteLater(); + } + ); + + // the config is prepared so that no Effect gets loaded! + loader.queryAndLoadAll(); + + // we need to wait some time because it's queued and in a thread + QVERIFY(!spy.wait(100)); + + // now let's prepare a config which has one effect explicitly enabled + plugins.writeEntry(kwin4 + QStringLiteral("scaleinEnabled"), true); + plugins.sync(); + + loader.queryAndLoadAll(); + // should load one effect in first go + QVERIFY(spy.wait(100)); + // and afterwards it should not load another one + QVERIFY(!spy.wait(10)); + + QCOMPARE(spy.size(), 1); + // if we caught a signal it should have the effect name we passed in + QList arguments = spy.takeFirst(); + QCOMPARE(arguments.count(), 2); + QCOMPARE(arguments.at(1).toString(), kwin4 + QStringLiteral("scalein")); + spy.clear(); + + // let's delete one of the default entries + plugins.deleteEntry(kwin4 + QStringLiteral("fadeEnabled")); + plugins.sync(); + + QVERIFY(spy.isEmpty()); + loader.queryAndLoadAll(); + + // let's use qWait as we need to wait for two signals to be emitted + QTest::qWait(100); + QCOMPARE(spy.size(), 2); + QStringList loadedEffects; + for (auto &list : spy) { + QCOMPARE(list.size(), 2); + loadedEffects << list.at(1).toString(); + } + qSort(loadedEffects); + QCOMPARE(loadedEffects.at(0), kwin4 + QStringLiteral("fade")); + QCOMPARE(loadedEffects.at(1), kwin4 + QStringLiteral("scalein")); +} + +QTEST_MAIN(TestScriptedEffectLoader) +#include "test_scripted_effectloader.moc" diff --git a/effectloader.cpp b/effectloader.cpp index afadd9d42e..0d934808ca 100644 --- a/effectloader.cpp +++ b/effectloader.cpp @@ -20,12 +20,17 @@ along with this program. If not, see . // own #include "effectloader.h" // KWin +#include #include #include "effects/effect_builtins.h" +#include "scripting/scriptedeffect.h" // KDE #include +#include // Qt +#include #include +#include #include #include @@ -182,4 +187,124 @@ QByteArray BuiltInEffectLoader::internalName(const QString& name) const return internalName.toUtf8(); } +static const QString s_nameProperty = QStringLiteral("X-KDE-PluginInfo-Name"); +static const QString s_jsConstraint = QStringLiteral("[X-Plasma-API] == 'javascript'"); +static const QString s_serviceType = QStringLiteral("KWin/Effect"); + +ScriptedEffectLoader::ScriptedEffectLoader(QObject *parent) + : AbstractEffectLoader(parent) + , m_queue(new EffectLoadQueue(this)) +{ +} + +ScriptedEffectLoader::~ScriptedEffectLoader() +{ +} + +bool ScriptedEffectLoader::hasEffect(const QString &name) const +{ + return findEffect(name); +} + +bool ScriptedEffectLoader::isEffectSupported(const QString &name) const +{ + // scripted effects are in general supported + return hasEffect(name); +} + +QStringList ScriptedEffectLoader::listOfKnownEffects() const +{ + const KService::List effects = findAllEffects(); + QStringList result; + for (KService::Ptr service : effects) { + result << service->property(s_nameProperty).toString(); + } + return result; +} + +bool ScriptedEffectLoader::loadEffect(const QString &name) +{ + KService::Ptr effect = findEffect(name); + if (!effect) { + return false; + } + return loadEffect(effect, LoadEffectFlag::Load); +} + +bool ScriptedEffectLoader::loadEffect(KService::Ptr effect, LoadEffectFlags flags) +{ + const QString name = effect->property(s_nameProperty).toString(); + if (!flags.testFlag(LoadEffectFlag::Load)) { + qDebug() << "Loading flags disable effect: " << name; + return false; + } + if (m_loadedEffects.contains(name)) { + qDebug() << name << "already loaded"; + return false; + } + + const QString scriptName = effect->property(QStringLiteral("X-Plasma-MainScript")).toString(); + if (scriptName.isEmpty()) { + qDebug() << "X-Plasma-MainScript not set"; + return false; + } + const QString scriptFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, + QStringLiteral(KWIN_NAME) + QStringLiteral("/effects/") + name + QStringLiteral("/contents/") + scriptName); + if (scriptFile.isNull()) { + qDebug() << "Could not locate the effect script"; + return false; + } + ScriptedEffect *e = ScriptedEffect::create(name, scriptFile); + if (!e) { + qDebug() << "Could not initialize scripted effect: " << name; + return false; + } + connect(e, &ScriptedEffect::destroyed, this, + [this, name]() { + m_loadedEffects.removeAll(name); + } + ); + + qDebug() << "Successfully loaded scripted effect: " << name; + emit effectLoaded(e, name); + m_loadedEffects << name; + return true; +} + +void ScriptedEffectLoader::queryAndLoadAll() +{ + // perform querying for the services in a thread + QFutureWatcher *watcher = new QFutureWatcher(this); + connect(watcher, &QFutureWatcher::finished, this, + [this, watcher]() { + const KService::List effects = watcher->result(); + for (KService::Ptr effect : effects) { + const LoadEffectFlags flags = readConfig(effect->property(s_nameProperty).toString(), + effect->property(QStringLiteral("X-KDE-PluginInfo-EnabledByDefault")).toBool()); + if (flags.testFlag(LoadEffectFlag::Load)) { + m_queue->enqueue(qMakePair(effect, flags)); + } + } + watcher->deleteLater(); + }, + Qt::QueuedConnection); + watcher->setFuture(QtConcurrent::run(this, &ScriptedEffectLoader::findAllEffects)); +} + +KService::List ScriptedEffectLoader::findAllEffects() const +{ + return KServiceTypeTrader::self()->query(s_serviceType, s_jsConstraint); +} + +KService::Ptr ScriptedEffectLoader::findEffect(const QString &name) const +{ + const QString constraint = QStringLiteral("%1 and [%2] == '%3'").arg(s_jsConstraint).arg(s_nameProperty).arg(name.toLower()); + const KService::List services = KServiceTypeTrader::self()->query(s_serviceType, + constraint); + if (!services.isEmpty()) { + return services.first(); + } + return KService::Ptr(); +} + } // namespace KWin diff --git a/effectloader.h b/effectloader.h index 2a4a2db172..dd13a0e9d3 100644 --- a/effectloader.h +++ b/effectloader.h @@ -21,6 +21,7 @@ along with this program. If not, see . #define KWIN_EFFECT_LOADER_H // KDE #include +#include // Qt #include #include @@ -283,6 +284,32 @@ private: QMap m_loadedEffects; }; +/** + * @brief Can load scripted Effects + * + */ +class ScriptedEffectLoader : public AbstractEffectLoader +{ + Q_OBJECT +public: + explicit ScriptedEffectLoader(QObject* parent = nullptr); + ~ScriptedEffectLoader() override; + + bool hasEffect(const QString &name) const override; + bool isEffectSupported(const QString &name) const override; + QStringList listOfKnownEffects() const override; + + void queryAndLoadAll() override; + bool loadEffect(const QString &name) override; + bool loadEffect(KService::Ptr effect, LoadEffectFlags flags); + +private: + KService::List findAllEffects() const; + KService::Ptr findEffect(const QString &name) const; + QStringList m_loadedEffects; + EffectLoadQueue< ScriptedEffectLoader, KService::Ptr > *m_queue; +}; + } Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::LoadEffectFlags) diff --git a/scripting/scriptedeffect.cpp b/scripting/scriptedeffect.cpp index a205d1ca22..056e4e8c06 100644 --- a/scripting/scriptedeffect.cpp +++ b/scripting/scriptedeffect.cpp @@ -411,7 +411,9 @@ bool ScriptedEffect::init(const QString &effectName, const QString &pathToScript QScriptValue effectsObject = m_engine->newQObject(effects, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater); m_engine->globalObject().setProperty(QStringLiteral("effects"), effectsObject, QScriptValue::Undeletable); m_engine->globalObject().setProperty(QStringLiteral("Effect"), m_engine->newQMetaObject(&ScriptedEffect::staticMetaObject)); +#ifndef KWIN_UNIT_TEST m_engine->globalObject().setProperty(QStringLiteral("KWin"), m_engine->newQMetaObject(&WorkspaceWrapper::staticMetaObject)); +#endif m_engine->globalObject().setProperty(QStringLiteral("QEasingCurve"), m_engine->newQMetaObject(&QEasingCurve::staticMetaObject)); m_engine->globalObject().setProperty(QStringLiteral("effect"), m_engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater), QScriptValue::Undeletable); m_engine->globalObject().setProperty(QStringLiteral("AnimationData"), m_engine->scriptValueFromQMetaObject());