kwin/effectloader.h

367 lines
13 KiB
C
Raw Normal View History

2020-08-02 22:22:19 +00:00
/*
KWin - the KDE window manager
This file is part of the KDE project.
2020-08-02 22:22:19 +00:00
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
2020-08-02 22:22:19 +00:00
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KWIN_EFFECT_LOADER_H
#define KWIN_EFFECT_LOADER_H
#include <kwin_export.h>
// KDE
#include <KPluginMetaData>
#include <KSharedConfig>
// Qt
#include <QObject>
#include <QFlags>
#include <QMap>
#include <QPair>
#include <QQueue>
namespace KWin
{
class Effect;
class EffectPluginFactory;
enum class BuiltInEffect;
/**
* @brief Flags defining how a Loader should load an Effect.
*
* These Flags are only used internally when querying the configuration on whether
* an Effect should be loaded.
*
* @see AbstractEffectLoader::readConfig()
*/
enum class LoadEffectFlag {
Load = 1 << 0, ///< Effect should be loaded
CheckDefaultFunction = 1 << 2 ///< The Check Default Function needs to be invoked if the Effect provides it
};
2016-07-16 17:14:44 +00:00
Q_DECLARE_FLAGS(LoadEffectFlags, LoadEffectFlag)
/**
* @brief Interface to describe how an effect loader has to function.
*
* The AbstractEffectLoader specifies the methods a concrete loader has to implement and how
* those methods are expected to perform. Also it provides an interface to the outside world
* (that is EffectsHandlerImpl).
*
* The abstraction is used because there are multiple types of Effects which need to be loaded:
* @li Built-In Effects
* @li Scripted Effects
* @li Binary Plugin Effects
*
* Serving all of them with one Effect Loader is rather complex given that different stores need
* to be queried at the same time. Thus the idea is to have one implementation per type and one
* implementation which makes use of all of them and combines the loading.
*/
class KWIN_EXPORT AbstractEffectLoader : public QObject
{
Q_OBJECT
public:
Run clang-tidy with modernize-use-override check Summary: Currently code base of kwin can be viewed as two pieces. One is very ancient, and the other one is more modern, which uses new C++ features. The main problem with the ancient code is that it was written before C++11 era. So, no override or final keywords, lambdas, etc. Quite recently, KDE compiler settings were changed to show a warning if a virtual method has missing override keyword. As you might have already guessed, this fired back at us because of that ancient code. We had about 500 new compiler warnings. A "solution" was proposed to that problem - disable -Wno-suggest-override and the other similar warning for clang. It's hard to call a solution because those warnings are disabled not only for the old code, but also for new. This is not what we want! The main argument for not actually fixing the problem was that git history will be screwed as well because of human factor. While good git history is a very important thing, we should not go crazy about it and block every change that somehow alters git history. git blame allows to specify starting revision for a reason. The other argument (human factor) can be easily solved by using tools such as clang-tidy. clang-tidy is a clang-based linter for C++. It can be used for various things, e.g. fixing coding style(e.g. add missing braces to if statements, readability-braces-around-statements check), or in our case add missing override keywords. Test Plan: Compiles. Reviewers: #kwin, davidedmundson Reviewed By: #kwin, davidedmundson Subscribers: davidedmundson, apol, romangg, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D22371
2019-07-22 16:52:26 +00:00
~AbstractEffectLoader() override;
/**
* @brief The KSharedConfig this EffectLoader should operate on.
*
* Important: a valid KSharedConfig must be provided before trying to load any effects!
*
* @param config
* @internal
*/
virtual void setConfig(KSharedConfig::Ptr config);
/**
* @brief Whether this Effect Loader can load the Effect with the given @p name.
*
* The Effect Loader determines whether it knows or can find an Effect called @p name,
* and thus whether it can attempt to load the Effect.
*
* @param name The name of the Effect to look for.
* @return bool @c true if the Effect Loader knows this effect, false otherwise
*/
virtual bool hasEffect(const QString &name) const = 0;
/**
* @brief All the Effects this loader knows of.
*
* The implementation should re-query its store whenever this method is invoked.
* It's possible that the store of effects changed (e.g. a new one got installed)
*
* @return QStringList The internal names of the known Effects
*/
virtual QStringList listOfKnownEffects() const = 0;
/**
* @brief Synchronous loading of the Effect with the given @p name.
*
* Loads the Effect without checking any configuration value or any enabled by default
* function provided by the Effect.
*
* The loader is expected to apply the following checks:
* If the Effect is already loaded, the Effect should not get loaded again. Thus the loader
* is expected to track which Effects it has loaded, and which of those have been destroyed.
* The loader should check whether the Effect is supported. If the Effect indicates it is
* not supported, it should not get loaded.
*
* If the Effect loaded successfully the signal effectLoaded(KWin::Effect*,const QString&)
* must be emitted. Otherwise the user of the loader is not able to get the loaded Effect.
* It's not returning the Effect as queryAndLoadAll() is working async and thus the users
* of the loader are expected to be prepared for async loading.
*
* @param name The internal name of the Effect which should be loaded
* @return bool @c true if the effect could be loaded, @c false in error case
* @see queryAndLoadAll()
* @see effectLoaded(KWin::Effect*,const QString&)
*/
virtual bool loadEffect(const QString &name) = 0;
/**
* @brief The Effect Loader should query its store for all available effects and try to load them.
*
* The Effect Loader is supposed to perform this operation in a highly async way. If there is
* IO which needs to be performed this should be done in a background thread and a queue should
* be used to load the effects. The loader should make sure to not load more than one Effect
* in one event cycle. Loading the Effect has to be performed in the Compositor thread and
* thus blocks the Compositor. Therefore after loading one Effect all events should get
* processed first, so that the Compositor can perform a painting pass if needed. To simplify
* this operation one can use the EffectLoadQueue. This requires to add another loadEffect
* method with the custom loader specific type to refer to an Effect and LoadEffectFlags.
*
* The LoadEffectFlags have to be determined by querying the configuration with readConfig().
* If the Load flag is set the loading can proceed and all the checks from
* loadEffect(const QString &) have to be applied.
* In addition if the CheckDefaultFunction flag is set and the Effect provides such a method,
* it should be queried to determine whether the Effect is enabled by default. If such a method
* returns @c false the Effect should not get loaded. If the Effect does not provide a way to
* query whether it's enabled by default at runtime the flag can get ignored.
*
* If the Effect loaded successfully the signal effectLoaded(KWin::Effect*,const QString&)
* must be emitted.
*
* @see loadEffect(const QString &)
* @see effectLoaded(KWin::Effect*,const QString&)
*/
virtual void queryAndLoadAll() = 0;
/**
* @brief Whether the Effect with the given @p name is supported by the compositing backend.
*
* @param name The name of the Effect to check.
* @return bool @c true if it is supported, @c false otherwise
*/
virtual bool isEffectSupported(const QString &name) const = 0;
/**
* @brief Clears the load queue, that is all scheduled Effects are discarded from loading.
*/
virtual void clear() = 0;
Q_SIGNALS:
/**
* @brief The loader emits this signal when it successfully loaded an effect.
*
* @param effect The created Effect
* @param name The internal name of the loaded Effect
* @return void
*/
void effectLoaded(KWin::Effect *effect, const QString &name);
protected:
explicit AbstractEffectLoader(QObject *parent = nullptr);
/**
* @brief Checks the configuration for the Effect identified by @p effectName.
*
* For each Effect there could be a key called "<effectName>Enabled". If there is such a key
* the returned flags will contain Load in case it's @c true. If the key does not exist the
* @p defaultValue determines whether the Effect should be loaded. A value of @c true means
* that Load | CheckDefaultFunction is returned, in case of @c false no Load flags are returned.
*
* @param effectName The name of the Effect to look for in the configuration
* @param defaultValue Whether the Effect is enabled by default or not.
* @returns Flags indicating whether the Effect should be loaded and how it should be loaded
*/
LoadEffectFlags readConfig(const QString &effectName, bool defaultValue) const;
private:
KSharedConfig::Ptr m_config;
};
/**
* @brief Helper class to queue the loading of Effects.
*
* Loading an Effect has to be done in the compositor thread and thus the Compositor is blocked
* while the Effect loads. To not block the compositor for several frames the loading of all
* Effects need to be queued. By invoking the slot dequeue() through a QueuedConnection the queue
* can ensure that events are processed between the loading of two Effects and thus the compositor
* doesn't block.
*
* As it needs to be a slot, the queue must subclass QObject, but it also needs to be templated as
* the information to load an Effect is specific to the Effect Loader. Thus there is the
* AbstractEffectLoadQueue providing the slots as pure virtual functions and the templated
* EffectLoadQueue inheriting from AbstractEffectLoadQueue.
*
* The queue operates like a normal queue providing enqueue and a scheduleDequeue instead of dequeue.
*
*/
class AbstractEffectLoadQueue : public QObject
{
Q_OBJECT
public:
explicit AbstractEffectLoadQueue(QObject *parent = nullptr)
: QObject(parent)
{
}
protected Q_SLOTS:
virtual void dequeue() = 0;
};
template <typename Loader, typename QueueType>
class EffectLoadQueue : public AbstractEffectLoadQueue
{
public:
explicit EffectLoadQueue(Loader *parent)
: AbstractEffectLoadQueue(parent)
, m_effectLoader(parent)
, m_dequeueScheduled(false)
{
}
void enqueue(const QPair<QueueType, LoadEffectFlags> value)
{
m_queue.enqueue(value);
scheduleDequeue();
}
void clear()
{
m_queue.clear();
m_dequeueScheduled = false;
}
protected:
void dequeue() override
{
if (m_queue.isEmpty()) {
return;
}
m_dequeueScheduled = false;
const auto pair = m_queue.dequeue();
m_effectLoader->loadEffect(pair.first, pair.second);
scheduleDequeue();
}
private:
void scheduleDequeue()
{
if (m_queue.isEmpty() || m_dequeueScheduled) {
return;
}
m_dequeueScheduled = true;
QMetaObject::invokeMethod(this, "dequeue", Qt::QueuedConnection);
}
Loader *m_effectLoader;
bool m_dequeueScheduled;
QQueue<QPair<QueueType, LoadEffectFlags>> m_queue;
};
/**
* @brief Can load the Built-In-Effects
*/
class BuiltInEffectLoader : public AbstractEffectLoader
{
Q_OBJECT
public:
explicit BuiltInEffectLoader(QObject *parent = nullptr);
~BuiltInEffectLoader() override;
bool hasEffect(const QString &name) const override;
bool isEffectSupported(const QString &name) const override;
QStringList listOfKnownEffects() const override;
void clear() override;
void queryAndLoadAll() override;
bool loadEffect(const QString& name) override;
bool loadEffect(BuiltInEffect effect, LoadEffectFlags flags);
private:
bool loadEffect(const QString &name, BuiltInEffect effect, LoadEffectFlags flags);
QString internalName(const QString &name) const;
EffectLoadQueue<BuiltInEffectLoader, BuiltInEffect> *m_queue;
QMap<BuiltInEffect, Effect*> m_loadedEffects;
};
/**
* @brief Can load scripted Effects
*/
class KWIN_EXPORT 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 clear() override;
void queryAndLoadAll() override;
bool loadEffect(const QString &name) override;
bool loadEffect(const KPluginMetaData &effect, LoadEffectFlags flags);
private:
QList<KPluginMetaData> findAllEffects() const;
KPluginMetaData findEffect(const QString &name) const;
QStringList m_loadedEffects;
EffectLoadQueue< ScriptedEffectLoader, KPluginMetaData > *m_queue;
QMetaObject::Connection m_queryConnection;
};
class PluginEffectLoader : public AbstractEffectLoader
{
Q_OBJECT
public:
explicit PluginEffectLoader(QObject *parent = nullptr);
~PluginEffectLoader() override;
bool hasEffect(const QString &name) const override;
bool isEffectSupported(const QString &name) const override;
QStringList listOfKnownEffects() const override;
void clear() override;
void queryAndLoadAll() override;
bool loadEffect(const QString &name) override;
bool loadEffect(const KPluginMetaData &info, LoadEffectFlags flags);
void setPluginSubDirectory(const QString &directory);
private:
QVector<KPluginMetaData> findAllEffects() const;
KPluginMetaData findEffect(const QString &name) const;
EffectPluginFactory *factory(const KPluginMetaData &info) const;
QStringList m_loadedEffects;
EffectLoadQueue< PluginEffectLoader, KPluginMetaData> *m_queue;
QString m_pluginSubDirectory;
QMetaObject::Connection m_queryConnection;
};
class EffectLoader : public AbstractEffectLoader
{
Q_OBJECT
public:
explicit EffectLoader(QObject *parent = nullptr);
~EffectLoader() override;
bool hasEffect(const QString &name) const override;
bool isEffectSupported(const QString &name) const override;
QStringList listOfKnownEffects() const override;
bool loadEffect(const QString &name) override;
void queryAndLoadAll() override;
void setConfig(KSharedConfig::Ptr config) override;
void clear() override;
private:
QList<AbstractEffectLoader*> m_loaders;
};
}
Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::LoadEffectFlags)
#endif