-
Notifications
You must be signed in to change notification settings - Fork 0
/
effectloader.h
379 lines (343 loc) · 13.7 KB
/
effectloader.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2014 Martin Gräßlin <[email protected]>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#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
};
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:
virtual ~AbstractEffectLoader();
/**
* @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 effecName 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