From 6a8b79f69943b57e52c4d206b5c4f46432045b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= <mgraesslin@kde.org> Date: Sun, 19 Feb 2012 15:43:24 +0100 Subject: [PATCH] Support declarative KWin scripts For this the Script class is slightly refactored to have a common base for JavaScript and QML based scripts. Why QML bindings? This allows to use QML for example for the desktop change OSD or for fullscreen effects like Present Windows. --- scripting/scripting.cpp | 101 +++++++++++++++++++++++++++++++++------- scripting/scripting.h | 68 +++++++++++++++++++++++---- 2 files changed, 142 insertions(+), 27 deletions(-) diff --git a/scripting/scripting.cpp b/scripting/scripting.cpp index c6b6d8ebb7..da3b7037c2 100644 --- a/scripting/scripting.cpp +++ b/scripting/scripting.cpp @@ -23,15 +23,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. // own #include "meta.h" #include "workspace_wrapper.h" +#include "../thumbnailitem.h" // KDE #include <kstandarddirs.h> #include <KDE/KConfigGroup> #include <KDE/KDebug> #include <KDE/KPluginInfo> #include <KDE/KServiceTypeTrader> +#include <kdeclarative.h> // Qt #include <QtDBus/QDBusConnection> #include <QtCore/QSettings> +#include <QtDeclarative/QDeclarativeContext> +#include <QtDeclarative/QDeclarativeEngine> +#include <QtDeclarative/QDeclarativeView> +#include <QtDeclarative/qdeclarative.h> #include <QtScript/QScriptEngine> #include <QtScript/QScriptValue> @@ -50,36 +56,49 @@ QScriptValue kwinScriptPrint(QScriptContext *context, QScriptEngine *engine) return engine->undefinedValue(); } - -KWin::Script::Script(int scriptId, QString scriptName, QObject *parent) +KWin::AbstractScript::AbstractScript (int id, QString scriptName, QObject *parent) : QObject(parent) - , m_scriptId(scriptId) - , m_engine(new QScriptEngine(this)) - , m_workspace(new WorkspaceWrapper(m_engine)) + , m_scriptId(id) , m_running(false) + , m_workspace(new WorkspaceWrapper(this)) { m_scriptFile.setFileName(scriptName); - QDBusConnection::sessionBus().registerObject('/' + QString::number(m_scriptId), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportScriptableInvokables); +} + +KWin::AbstractScript::~AbstractScript() +{ +} + +void KWin::AbstractScript::stop() +{ + deleteLater(); +} + +KWin::Script::Script(int id, QString scriptName, QObject *parent) + : AbstractScript(id, scriptName, parent) + , m_engine(new QScriptEngine(this)) +{ + QDBusConnection::sessionBus().registerObject('/' + QString::number(scriptId()), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportScriptableInvokables); } KWin::Script::~Script() { - QDBusConnection::sessionBus().unregisterObject('/' + QString::number(m_scriptId)); + QDBusConnection::sessionBus().unregisterObject('/' + QString::number(scriptId())); } void KWin::Script::printMessage(const QString &message) { - kDebug(1212) << m_scriptFile.fileName() << ":" << message; + kDebug(1212) << scriptFile().fileName() << ":" << message; emit print(message); } void KWin::Script::run() { - if (m_running) { + if (running()) { return; } - if (m_scriptFile.open(QIODevice::ReadOnly)) { - QScriptValue workspace = m_engine->newQObject(m_workspace, QScriptEngine::QtOwnership, + if (scriptFile().open(QIODevice::ReadOnly)) { + QScriptValue workspace = m_engine->newQObject(AbstractScript::workspace(), QScriptEngine::QtOwnership, QScriptEngine::ExcludeSuperClassContents | QScriptEngine::ExcludeDeleteLater); m_engine->globalObject().setProperty("workspace", workspace, QScriptValue::Undeletable); m_engine->globalObject().setProperty("QTimer", constructTimerClass(m_engine)); @@ -92,14 +111,14 @@ void KWin::Script::run() printFunc.setData(m_engine->newQObject(this)); m_engine->globalObject().setProperty("print", printFunc); - QScriptValue ret = m_engine->evaluate(m_scriptFile.readAll()); + QScriptValue ret = m_engine->evaluate(scriptFile().readAll()); if (ret.isError()) { sigException(ret); deleteLater(); } } - m_running = true; + setRunning(true); } void KWin::Script::sigException(const QScriptValue& exception) @@ -119,11 +138,44 @@ void KWin::Script::sigException(const QScriptValue& exception) emit printError(exception.toString()); } -void KWin::Script::stop() +KWin::DeclarativeScript::DeclarativeScript(int id, QString scriptName, QObject *parent) + : AbstractScript(id, scriptName, parent) + , m_view(new QDeclarativeView()) { - deleteLater(); } +KWin::DeclarativeScript::~DeclarativeScript() +{ +} + +void KWin::DeclarativeScript::run() +{ + if (running()) { + return; + } + m_view->setAttribute(Qt::WA_TranslucentBackground); + m_view->setWindowFlags(Qt::X11BypassWindowManagerHint); + m_view->setResizeMode(QDeclarativeView::SizeViewToRootObject); + QPalette pal = m_view->palette(); + pal.setColor(m_view->backgroundRole(), Qt::transparent); + m_view->setPalette(pal); + + + foreach (const QString &importPath, KGlobal::dirs()->findDirs("module", "imports")) { + m_view->engine()->addImportPath(importPath); + } + KDeclarative kdeclarative; + kdeclarative.setDeclarativeEngine(m_view->engine()); + kdeclarative.initialize(); + kdeclarative.setupBindings(); + qmlRegisterType<ThumbnailItem>("org.kde.kwin", 0, 1, "ThumbnailItem"); + qmlRegisterType<WorkspaceWrapper>("org.kde.kwin", 0, 1, "KWin"); + + m_view->rootContext()->setContextProperty("workspace", workspace()); + + m_view->setSource(QUrl::fromLocalFile(scriptFile().fileName())); + setRunning(true); +} KWin::Scripting::Scripting(QObject *parent) : QObject(parent) @@ -142,7 +194,9 @@ void KWin::Scripting::start() foreach (const KService::Ptr & service, offers) { KPluginInfo plugininfo(service); plugininfo.load(conf); - if (service->property("X-Plasma-API").toString() != "javascript") { + const bool javaScript = service->property("X-Plasma-API").toString() == "javascript"; + const bool declarativeScript = service->property("X-Plasma-API").toString() == "declarativescript"; + if (!javaScript && !declarativeScript) { continue; } @@ -156,7 +210,11 @@ void KWin::Scripting::start() kDebug(1212) << "Could not find script file for " << pluginName; continue; } - loadScript(file); + if (javaScript) { + loadScript(file); + } else if (declarativeScript) { + loadDeclarativeScript(file); + } } runScripts(); @@ -183,6 +241,15 @@ int KWin::Scripting::loadScript(const QString &filePath) return id; } +int KWin::Scripting::loadDeclarativeScript(const QString &filePath) +{ + const int id = scripts.size(); + KWin::DeclarativeScript *script = new KWin::DeclarativeScript(id, filePath, this); + connect(script, SIGNAL(destroyed(QObject*)), SLOT(scriptDestroyed(QObject*))); + scripts.append(script); + return id; +} + KWin::Scripting::~Scripting() { QDBusConnection::sessionBus().unregisterObject("/Scripting"); diff --git a/scripting/scripting.h b/scripting/scripting.h index e9e91e8c5f..d28f06a896 100644 --- a/scripting/scripting.h +++ b/scripting/scripting.h @@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include <QtCore/QFile> #include <QtCore/QStringList> +class QDeclarativeView; class QScriptEngine; class QScriptValue; @@ -32,7 +33,46 @@ namespace KWin { class WorkspaceWrapper; -class Script : public QObject +class AbstractScript : public QObject +{ + Q_OBJECT +public: + AbstractScript(int id, QString scriptName, QObject *parent = NULL); + ~AbstractScript(); + QString fileName() const { + return m_scriptFile.fileName(); + } + +public Q_SLOTS: + Q_SCRIPTABLE void stop(); + Q_SCRIPTABLE virtual void run() = 0; + +protected: + QFile &scriptFile() { + return m_scriptFile; + } + bool running() const { + return m_running; + } + void setRunning(bool running) { + m_running = running; + } + int scriptId() const { + return m_scriptId; + } + + WorkspaceWrapper *workspace() { + return m_workspace; + } + +private: + int m_scriptId; + QFile m_scriptFile; + bool m_running; + WorkspaceWrapper *m_workspace; +}; + +class Script : public AbstractScript { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Scripting") @@ -40,14 +80,10 @@ public: Script(int id, QString scriptName, QObject *parent = NULL); virtual ~Script(); - QString fileName() const { - return m_scriptFile.fileName(); - } void printMessage(const QString &message); public Q_SLOTS: - Q_SCRIPTABLE void stop(); Q_SCRIPTABLE void run(); Q_SIGNALS: @@ -62,11 +98,22 @@ private slots: void sigException(const QScriptValue &exception); private: - int m_scriptId; QScriptEngine *m_engine; - QFile m_scriptFile; - WorkspaceWrapper *m_workspace; - bool m_running; +}; + +class DeclarativeScript : public AbstractScript +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Scripting") +public: + explicit DeclarativeScript(int id, QString scriptName, QObject *parent = 0); + virtual ~DeclarativeScript(); + +public Q_SLOTS: + Q_SCRIPTABLE void run(); + +private: + QDeclarativeView *m_view; }; /** @@ -78,7 +125,7 @@ class Scripting : public QObject Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Scripting") private: QStringList scriptList; - QList<KWin::Script*> scripts; + QList<KWin::AbstractScript*> scripts; // Preferably call ONLY at load time void runScripts(); @@ -92,6 +139,7 @@ public: void start(); ~Scripting(); Q_SCRIPTABLE Q_INVOKABLE int loadScript(const QString &filePath); + Q_SCRIPTABLE Q_INVOKABLE int loadDeclarativeScript(const QString &filePath); public Q_SLOTS: void scriptDestroyed(QObject *object);