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)