kwin/effectloader.cpp

300 lines
9.3 KiB
C++
Raw Normal View History

/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org>
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/>.
*********************************************************************/
// own
#include "effectloader.h"
// KWin
#include <config-kwin.h>
#include <kwineffects.h>
#include "effects/effect_builtins.h"
#include "scripting/scriptedeffect.h"
// KDE
#include <KConfigGroup>
#include <KServiceTypeTrader>
// Qt
#include <QtConcurrentRun>
#include <QDebug>
#include <QFutureWatcher>
#include <QMap>
#include <QStringList>
namespace KWin
{
AbstractEffectLoader::AbstractEffectLoader(QObject *parent)
: QObject(parent)
{
}
AbstractEffectLoader::~AbstractEffectLoader()
{
}
void AbstractEffectLoader::setConfig(KSharedConfig::Ptr config)
{
m_config = config;
}
LoadEffectFlags AbstractEffectLoader::readConfig(const QString &effectName, bool defaultValue) const
{
Q_ASSERT(m_config);
KConfigGroup plugins(m_config, QStringLiteral("Plugins"));
const QString key = effectName + QStringLiteral("Enabled");
// do we have a key for the effect?
if (plugins.hasKey(key)) {
// we have a key in the config, so read the enabled state
const bool load = plugins.readEntry(key, defaultValue);
return load ? LoadEffectFlags(LoadEffectFlag::Load) : LoadEffectFlags();
}
// we don't have a key, so we just use the enabled by default value
if (defaultValue) {
return LoadEffectFlag::Load | LoadEffectFlag::CheckDefaultFunction;
}
return LoadEffectFlags();
}
BuiltInEffectLoader::BuiltInEffectLoader(QObject *parent)
: AbstractEffectLoader(parent)
, m_queue(new EffectLoadQueue<BuiltInEffectLoader, BuiltInEffect>(this))
{
}
BuiltInEffectLoader::~BuiltInEffectLoader()
{
}
bool BuiltInEffectLoader::hasEffect(const QString &name) const
{
return BuiltInEffects::available(internalName(name));
}
bool BuiltInEffectLoader::isEffectSupported(const QString &name) const
{
return BuiltInEffects::supported(internalName(name));
}
QStringList BuiltInEffectLoader::listOfKnownEffects() const
{
const QList<QByteArray> availableEffects = BuiltInEffects::availableEffectNames();
QStringList result;
for (const QByteArray name : availableEffects) {
result << QString::fromUtf8(name);
}
return result;
}
bool BuiltInEffectLoader::loadEffect(const QString &name)
{
return loadEffect(name, BuiltInEffects::builtInForName(internalName(name)), LoadEffectFlag::Load);
}
void BuiltInEffectLoader::queryAndLoadAll()
{
const QList<BuiltInEffect> effects = BuiltInEffects::availableEffects();
for (BuiltInEffect effect : effects) {
// check whether it is already loaded
if (m_loadedEffects.contains(effect)) {
continue;
}
// as long as the KCM uses kwin4_effect_ we need to add it, TODO remove
const QString key = QStringLiteral("kwin4_effect_") +
QString::fromUtf8(BuiltInEffects::nameForEffect(effect));
const LoadEffectFlags flags = readConfig(key, BuiltInEffects::enabledByDefault(effect));
if (flags.testFlag(LoadEffectFlag::Load)) {
m_queue->enqueue(qMakePair(effect, flags));
}
}
}
bool BuiltInEffectLoader::loadEffect(BuiltInEffect effect, LoadEffectFlags flags)
{
return loadEffect(QString::fromUtf8(BuiltInEffects::nameForEffect(effect)), effect, flags);
}
bool BuiltInEffectLoader::loadEffect(const QString &name, BuiltInEffect effect, LoadEffectFlags flags)
{
if (effect == BuiltInEffect::Invalid) {
return false;
}
if (!flags.testFlag(LoadEffectFlag::Load)) {
qDebug() << "Loading flags disable effect: " << name;
return false;
}
// check that it is not already loaded
if (m_loadedEffects.contains(effect)) {
return false;
}
// supported might need a context
#ifndef KWIN_UNIT_TEST
effects->makeOpenGLContextCurrent();
#endif
if (!BuiltInEffects::supported(effect)) {
qDebug() << "Effect is not supported: " << name;
return false;
}
if (flags.testFlag(LoadEffectFlag::CheckDefaultFunction)) {
if (!BuiltInEffects::checkEnabledByDefault(effect)) {
qDebug() << "Enabled by default function disables effect: " << name;
return false;
}
}
// ok, now we can try to create the Effect
Effect *e = BuiltInEffects::create(effect);
if (!e) {
qDebug() << "Failed to create effect: " << name;
return false;
}
// insert in our loaded effects
m_loadedEffects.insert(effect, e);
connect(e, &Effect::destroyed, this,
[this, effect]() {
m_loadedEffects.remove(effect);
}
);
qDebug() << "Successfully loaded built-in effect: " << name;
emit effectLoaded(e, name);
return true;
}
QByteArray BuiltInEffectLoader::internalName(const QString& name) const
{
QString internalName = name.toLower();
// as long as the KCM uses kwin4_effect_ we need to add it, TODO remove
if (internalName.startsWith(QStringLiteral("kwin4_effect_"))) {
internalName = internalName.mid(13);
}
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<ScriptedEffectLoader, KService::Ptr>(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;
}
ScriptedEffect *e = ScriptedEffect::create(effect);
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<KService::List> *watcher = new QFutureWatcher<KService::List>(this);
connect(watcher, &QFutureWatcher<KService::List>::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