From ab253cd17856d63ccfbc689f8d0515f03ae8e45b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 23 Dec 2011 11:52:06 +0100 Subject: [PATCH] Scripting meets D-Bus Each KWin Script is also exported as a D-Bus object and can be stopped (destroyed) and started through D-Bus. Output and errors are emitted as D-Bus signals. That allows external applications (e.g. Plasma desktop scripting console) to load a script and print out the output. The general interface is exported as /Scripting and allows to load a new script by file. The script is not directly executed but only loaded. To execute it the run method on the script object has to be invoked. --- scripting/scripting.cpp | 75 +++++++++++++++++++++++++++++++++++++++-- scripting/scripting.h | 48 ++++++++++++++------------ 2 files changed, 99 insertions(+), 24 deletions(-) diff --git a/scripting/scripting.cpp b/scripting/scripting.cpp index 8423ab57e3..a6abaf0eec 100644 --- a/scripting/scripting.cpp +++ b/scripting/scripting.cpp @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2010 Rohan Prabhu +Copyright (C) 2011 Martin Gräßlin 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 @@ -19,25 +20,63 @@ along with this program. If not, see . *********************************************************************/ #include "scripting.h" +// own +#include "chelate.h" +#include "meta.h" +#include "s_clientgroup.h" +// KDE #include +// Qt +#include +#include +#include +#include + +QScriptValue kwinScriptPrint(QScriptContext *context, QScriptEngine *engine) +{ + KWin::Script *script = qobject_cast(context->callee().data().toQObject()); + QString result; + for (int i = 0; i < context->argumentCount(); ++i) { + if (i > 0) { + result.append(" "); + } + result.append(context->argument(i).toString()); + } + script->printMessage(result); + + return engine->undefinedValue(); +} -KWin::Script::Script(QString scriptName, QDir dir, QObject *parent) +KWin::Script::Script(int scriptId, QString scriptName, QDir dir, QObject *parent) : QObject(parent) + , m_scriptId(scriptId) , m_engine(new QScriptEngine(this)) , m_scriptDir(dir) , m_configFile(QFileInfo(m_scriptFile).completeBaseName() + QString(".kwscfg")) , m_workspace(new SWrapper::Workspace(m_engine)) + , m_running(false) { m_scriptFile.setFileName(dir.filePath(scriptName)); + QDBusConnection::sessionBus().registerObject("/" + QString::number(m_scriptId), this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportScriptableInvokables); } KWin::Script::~Script() { + QDBusConnection::sessionBus().unregisterObject("/" + QString::number(m_scriptId)); +} + +void KWin::Script::printMessage(const QString &message) +{ + kDebug(1212) << m_scriptFile.fileName() << ":" << message; + emit print(message); } void KWin::Script::run() { + if (m_running) { + return; + } if (m_scriptFile.open(QIODevice::ReadOnly)) { m_workspace->attach(m_engine); m_engine->globalObject().setProperty("QTimer", constructTimerClass(m_engine)); @@ -60,13 +99,19 @@ void KWin::Script::run() } else { KWin::MetaScripting::supplyConfig(m_engine); } + // add our print + QScriptValue printFunc = m_engine->newFunction(kwinScriptPrint); + printFunc.setData(m_engine->newQObject(this)); + m_engine->globalObject().setProperty("print", printFunc); QScriptValue ret = m_engine->evaluate(m_scriptFile.readAll()); if (ret.isError()) { sigException(ret); + deleteLater(); } } + m_running = true; } void KWin::Script::sigException(const QScriptValue& exception) @@ -83,12 +128,20 @@ void KWin::Script::sigException(const QScriptValue& exception) qDebug() << " " << iter.name() << ": " << iter.value().toString(); } } + emit printError(exception.toString()); +} + +void KWin::Script::stop() +{ + deleteLater(); } KWin::Scripting::Scripting(QObject *parent) : QObject(parent) { + QDBusConnection::sessionBus().registerObject("/Scripting", this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportScriptableInvokables); + QDBusConnection::sessionBus().registerService("org.kde.kwin.Scripting"); } void KWin::Scripting::start() @@ -106,7 +159,7 @@ void KWin::Scripting::start() scriptList = scriptsDir.entryList(scriptFilters, QDir::Files | QDir::Readable | QDir::Executable); for (int i = 0; i < scriptList.size(); i++) { - scripts.append(new KWin::Script(scriptsDir.filePath(scriptList.at(i)), scriptsDir, this)); + loadScript(scriptsDir.filePath(scriptList.at(i))); } // Initialize singletons. Currently, only KWin::Workspace. @@ -122,6 +175,24 @@ void KWin::Scripting::runScripts() } } +void KWin::Scripting::scriptDestroyed(QObject *object) +{ + scripts.removeAll(static_cast(object)); +} + +int KWin::Scripting::loadScript(const QString &filePath) +{ + const int id = scripts.size(); + KWin::Script *script = new KWin::Script(id, filePath, scriptsDir, this); + connect(script, SIGNAL(destroyed(QObject*)), SLOT(scriptDestroyed(QObject*))); + scripts.append(script); + return id; +} + KWin::Scripting::~Scripting() { + QDBusConnection::sessionBus().unregisterObject("/Scripting"); + QDBusConnection::sessionBus().unregisterService("org.kde.kwin.Scripting"); } + +#include "scripting.moc" diff --git a/scripting/scripting.h b/scripting/scripting.h index 6dbee6a05c..fee3c7b04f 100644 --- a/scripting/scripting.h +++ b/scripting/scripting.h @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2010 Rohan Prabhu +Copyright (C) 2011 Martin Gräßlin 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 @@ -21,37 +22,38 @@ along with this program. If not, see . #ifndef KWIN_SCRIPTING_H #define KWIN_SCRIPTING_H -#include -#include #include -#include -#include -#include -#include "chelate.h" #include "workspace.h" #include "workspaceproxy.h" -#include "s_clientgroup.h" -#include "./../workspace.h" - -#include "meta.h" +class QScriptEngine; +class QScriptValue; namespace KWin { -/** This mostly behaves like a struct. Is used to store - * the scriptfile, the configfile and the QScriptEngine - * that will run this script - */ class Script : public QObject { Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Scripting") public: - Script(QString scriptName, QDir dir, QObject *parent = NULL); + Script(int id, QString scriptName, QDir dir, QObject *parent = NULL); virtual ~Script(); - void run(); + 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: + Q_SCRIPTABLE void print(const QString &text); + Q_SCRIPTABLE void printError(const QString &text); private slots: /** @@ -61,11 +63,13 @@ private slots: void sigException(const QScriptValue &exception); private: + int m_scriptId; QScriptEngine *m_engine; QDir m_scriptDir; QFile m_scriptFile; QString m_configFile; SWrapper::Workspace *m_workspace; + bool m_running; }; /** @@ -74,19 +78,15 @@ private: class Scripting : public QObject { Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Scripting") private: QStringList scriptList; QDir scriptsDir; - QVector scripts; + QList scripts; // Preferably call ONLY at load time void runScripts(); - // NOTE: Runtime script running is not yet tested. - // Proceed with caution. - // An interface to run scripts at runtime - void runScript(KWin::Script*); - public: Scripting(QObject *parent = NULL); /** @@ -95,6 +95,10 @@ public: */ void start(); ~Scripting(); + Q_SCRIPTABLE Q_INVOKABLE int loadScript(const QString &filePath); + +public Q_SLOTS: + void scriptDestroyed(QObject *object); }; }