File indexing completed on 2024-12-01 13:37:24

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #pragma once
0010 #include <kwin_export.h>
0011 // KDE
0012 #include <KPluginMetaData>
0013 #include <KSharedConfig>
0014 // Qt
0015 #include <QFlags>
0016 #include <QMap>
0017 #include <QObject>
0018 #include <QPair>
0019 #include <QQueue>
0020 #include <QStaticPlugin>
0021 
0022 namespace KWin
0023 {
0024 class Effect;
0025 class EffectPluginFactory;
0026 
0027 /**
0028  * @brief Flags defining how a Loader should load an Effect.
0029  *
0030  * These Flags are only used internally when querying the configuration on whether
0031  * an Effect should be loaded.
0032  *
0033  * @see AbstractEffectLoader::readConfig()
0034  */
0035 enum class LoadEffectFlag {
0036     Load = 1 << 0, ///< Effect should be loaded
0037     CheckDefaultFunction = 1 << 2 ///< The Check Default Function needs to be invoked if the Effect provides it
0038 };
0039 Q_DECLARE_FLAGS(LoadEffectFlags, LoadEffectFlag)
0040 
0041 /**
0042  * @brief Interface to describe how an effect loader has to function.
0043  *
0044  * The AbstractEffectLoader specifies the methods a concrete loader has to implement and how
0045  * those methods are expected to perform. Also it provides an interface to the outside world
0046  * (that is EffectsHandlerImpl).
0047  *
0048  * The abstraction is used because there are multiple types of Effects which need to be loaded:
0049  * @li Built-In Effects
0050  * @li Scripted Effects
0051  * @li Binary Plugin Effects
0052  *
0053  * Serving all of them with one Effect Loader is rather complex given that different stores need
0054  * to be queried at the same time. Thus the idea is to have one implementation per type and one
0055  * implementation which makes use of all of them and combines the loading.
0056  */
0057 class KWIN_EXPORT AbstractEffectLoader : public QObject
0058 {
0059     Q_OBJECT
0060 public:
0061     ~AbstractEffectLoader() override;
0062 
0063     /**
0064      * @brief The KSharedConfig this EffectLoader should operate on.
0065      *
0066      * Important: a valid KSharedConfig must be provided before trying to load any effects!
0067      *
0068      * @param config
0069      * @internal
0070      */
0071     virtual void setConfig(KSharedConfig::Ptr config);
0072 
0073     /**
0074      * @brief Whether this Effect Loader can load the Effect with the given @p name.
0075      *
0076      * The Effect Loader determines whether it knows or can find an Effect called @p name,
0077      * and thus whether it can attempt to load the Effect.
0078      *
0079      * @param name The name of the Effect to look for.
0080      * @return bool @c true if the Effect Loader knows this effect, false otherwise
0081      */
0082     virtual bool hasEffect(const QString &name) const = 0;
0083 
0084     /**
0085      * @brief All the Effects this loader knows of.
0086      *
0087      * The implementation should re-query its store whenever this method is invoked.
0088      * It's possible that the store of effects changed (e.g. a new one got installed)
0089      *
0090      * @return QStringList The internal names of the known Effects
0091      */
0092     virtual QStringList listOfKnownEffects() const = 0;
0093 
0094     /**
0095      * @brief Synchronous loading of the Effect with the given @p name.
0096      *
0097      * Loads the Effect without checking any configuration value or any enabled by default
0098      * function provided by the Effect.
0099      *
0100      * The loader is expected to apply the following checks:
0101      * If the Effect is already loaded, the Effect should not get loaded again. Thus the loader
0102      * is expected to track which Effects it has loaded, and which of those have been destroyed.
0103      * The loader should check whether the Effect is supported. If the Effect indicates it is
0104      * not supported, it should not get loaded.
0105      *
0106      * If the Effect loaded successfully the signal effectLoaded(KWin::Effect*,const QString&)
0107      * must be emitted. Otherwise the user of the loader is not able to get the loaded Effect.
0108      * It's not returning the Effect as queryAndLoadAll() is working async and thus the users
0109      * of the loader are expected to be prepared for async loading.
0110      *
0111      * @param name The internal name of the Effect which should be loaded
0112      * @return bool @c true if the effect could be loaded, @c false in error case
0113      * @see queryAndLoadAll()
0114      * @see effectLoaded(KWin::Effect*,const QString&)
0115      */
0116     virtual bool loadEffect(const QString &name) = 0;
0117 
0118     /**
0119      * @brief The Effect Loader should query its store for all available effects and try to load them.
0120      *
0121      * The Effect  Loader is supposed to perform this operation in a highly async way. If there is
0122      * IO which needs to be performed this should be done in a background thread and a queue should
0123      * be used to load the effects. The loader should make sure to not load more than one Effect
0124      * in one event cycle. Loading the Effect has to be performed in the Compositor thread and
0125      * thus blocks the Compositor. Therefore after loading one Effect all events should get
0126      * processed first, so that the Compositor can perform a painting pass if needed. To simplify
0127      * this operation one can use the EffectLoadQueue. This requires to add another loadEffect
0128      * method with the custom loader specific type to refer to an Effect and LoadEffectFlags.
0129      *
0130      * The LoadEffectFlags have to be determined by querying the configuration with readConfig().
0131      * If the Load flag is set the loading can proceed and all the checks from
0132      * loadEffect(const QString &) have to be applied.
0133      * In addition if the CheckDefaultFunction flag is set and the Effect provides such a method,
0134      * it should be queried to determine whether the Effect is enabled by default. If such a method
0135      * returns @c false the Effect should not get loaded. If the Effect does not provide a way to
0136      * query whether it's enabled by default at runtime the flag can get ignored.
0137      *
0138      * If the Effect loaded successfully the signal effectLoaded(KWin::Effect*,const QString&)
0139      * must be emitted.
0140      *
0141      * @see loadEffect(const QString &)
0142      * @see effectLoaded(KWin::Effect*,const QString&)
0143      */
0144     virtual void queryAndLoadAll() = 0;
0145 
0146     /**
0147      * @brief Whether the Effect with the given @p name is supported by the compositing backend.
0148      *
0149      * @param name The name of the Effect to check.
0150      * @return bool @c true if it is supported, @c false otherwise
0151      */
0152     virtual bool isEffectSupported(const QString &name) const = 0;
0153 
0154     /**
0155      * @brief Clears the load queue, that is all scheduled Effects are discarded from loading.
0156      */
0157     virtual void clear() = 0;
0158 
0159 Q_SIGNALS:
0160     /**
0161      * @brief The loader emits this signal when it successfully loaded an effect.
0162      *
0163      * @param effect The created Effect
0164      * @param name The internal name of the loaded Effect
0165      * @return void
0166      */
0167     void effectLoaded(KWin::Effect *effect, const QString &name);
0168 
0169 protected:
0170     explicit AbstractEffectLoader(QObject *parent = nullptr);
0171     /**
0172      * @brief Checks the configuration for the Effect identified by @p effectName.
0173      *
0174      * For each Effect there could be a key called "<effectName>Enabled". If there is such a key
0175      * the returned flags will contain Load in case it's @c true. If the key does not exist the
0176      * @p defaultValue determines whether the Effect should be loaded. A value of @c true means
0177      * that Load | CheckDefaultFunction is returned, in case of @c false no Load flags are returned.
0178      *
0179      * @param effectName The name of the Effect to look for in the configuration
0180      * @param defaultValue Whether the Effect is enabled by default or not.
0181      * @returns Flags indicating whether the Effect should be loaded and how it should be loaded
0182      */
0183     LoadEffectFlags readConfig(const QString &effectName, bool defaultValue) const;
0184 
0185 private:
0186     KSharedConfig::Ptr m_config;
0187 };
0188 
0189 template<typename Loader, typename QueueType>
0190 class EffectLoadQueue;
0191 /**
0192  * @brief Helper class to queue the loading of Effects.
0193  *
0194  * Loading an Effect has to be done in the compositor thread and thus the Compositor is blocked
0195  * while the Effect loads. To not block the compositor for several frames the loading of all
0196  * Effects need to be queued. By invoking the slot dequeue() through a QueuedConnection the queue
0197  * can ensure that events are processed between the loading of two Effects and thus the compositor
0198  * doesn't block.
0199  *
0200  * As it needs to be a slot, the queue must subclass QObject, but it also needs to be templated as
0201  * the information to load an Effect is specific to the Effect Loader. Thus there is the
0202  * AbstractEffectLoadQueue providing the slots as pure virtual functions and the templated
0203  * EffectLoadQueue inheriting from AbstractEffectLoadQueue.
0204  *
0205  * The queue operates like a normal queue providing enqueue and a scheduleDequeue instead of dequeue.
0206  *
0207  */
0208 class AbstractEffectLoadQueue : public QObject
0209 {
0210     Q_OBJECT
0211 public:
0212     explicit AbstractEffectLoadQueue(QObject *parent = nullptr)
0213         : QObject(parent)
0214     {
0215     }
0216 protected Q_SLOTS:
0217     virtual void dequeue() = 0;
0218 
0219 private:
0220     template<typename Loader, typename QueueType>
0221     friend class EffectLoadQueue;
0222 };
0223 
0224 template<typename Loader, typename QueueType>
0225 class EffectLoadQueue : public AbstractEffectLoadQueue
0226 {
0227 public:
0228     explicit EffectLoadQueue(Loader *parent)
0229         : AbstractEffectLoadQueue(parent)
0230         , m_effectLoader(parent)
0231         , m_dequeueScheduled(false)
0232     {
0233     }
0234     void enqueue(const QPair<QueueType, LoadEffectFlags> value)
0235     {
0236         m_queue.enqueue(value);
0237         scheduleDequeue();
0238     }
0239     void clear()
0240     {
0241         m_queue.clear();
0242         m_dequeueScheduled = false;
0243     }
0244 
0245 protected:
0246     void dequeue() override
0247     {
0248         if (m_queue.isEmpty()) {
0249             return;
0250         }
0251         m_dequeueScheduled = false;
0252         const auto pair = m_queue.dequeue();
0253         m_effectLoader->loadEffect(pair.first, pair.second);
0254         scheduleDequeue();
0255     }
0256 
0257 private:
0258     void scheduleDequeue()
0259     {
0260         if (m_queue.isEmpty() || m_dequeueScheduled) {
0261             return;
0262         }
0263         m_dequeueScheduled = true;
0264         QMetaObject::invokeMethod(this, &AbstractEffectLoadQueue::dequeue, Qt::QueuedConnection);
0265     }
0266     Loader *m_effectLoader;
0267     bool m_dequeueScheduled;
0268     QQueue<QPair<QueueType, LoadEffectFlags>> m_queue;
0269 };
0270 
0271 /**
0272  * @brief Can load scripted Effects
0273  */
0274 class KWIN_EXPORT ScriptedEffectLoader : public AbstractEffectLoader
0275 {
0276     Q_OBJECT
0277 public:
0278     explicit ScriptedEffectLoader(QObject *parent = nullptr);
0279     ~ScriptedEffectLoader() override;
0280 
0281     bool hasEffect(const QString &name) const override;
0282     bool isEffectSupported(const QString &name) const override;
0283     QStringList listOfKnownEffects() const override;
0284 
0285     void clear() override;
0286     void queryAndLoadAll() override;
0287     bool loadEffect(const QString &name) override;
0288     bool loadEffect(const KPluginMetaData &effect, LoadEffectFlags flags);
0289 
0290 private:
0291     QList<KPluginMetaData> findAllEffects() const;
0292     KPluginMetaData findEffect(const QString &name) const;
0293     QStringList m_loadedEffects;
0294     EffectLoadQueue<ScriptedEffectLoader, KPluginMetaData> *m_queue;
0295     QMetaObject::Connection m_queryConnection;
0296 };
0297 
0298 class PluginEffectLoader : public AbstractEffectLoader
0299 {
0300     Q_OBJECT
0301 public:
0302     explicit PluginEffectLoader(QObject *parent = nullptr);
0303     ~PluginEffectLoader() override;
0304 
0305     bool hasEffect(const QString &name) const override;
0306     bool isEffectSupported(const QString &name) const override;
0307     QStringList listOfKnownEffects() const override;
0308 
0309     void clear() override;
0310     void queryAndLoadAll() override;
0311     bool loadEffect(const QString &name) override;
0312     bool loadEffect(const KPluginMetaData &info, LoadEffectFlags flags);
0313 
0314     void setPluginSubDirectory(const QString &directory);
0315 
0316 private:
0317     QVector<KPluginMetaData> findAllEffects() const;
0318     KPluginMetaData findEffect(const QString &name) const;
0319     EffectPluginFactory *factory(const KPluginMetaData &info) const;
0320     QStringList m_loadedEffects;
0321     QString m_pluginSubDirectory;
0322 };
0323 
0324 class KWIN_EXPORT EffectLoader : public AbstractEffectLoader
0325 {
0326     Q_OBJECT
0327 public:
0328     explicit EffectLoader(QObject *parent = nullptr);
0329     ~EffectLoader() override;
0330     bool hasEffect(const QString &name) const override;
0331     bool isEffectSupported(const QString &name) const override;
0332     QStringList listOfKnownEffects() const override;
0333     bool loadEffect(const QString &name) override;
0334     void queryAndLoadAll() override;
0335     void setConfig(KSharedConfig::Ptr config) override;
0336     void clear() override;
0337 
0338 private:
0339     QList<AbstractEffectLoader *> m_loaders;
0340 };
0341 
0342 }
0343 Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::LoadEffectFlags)