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.
This commit is contained in:
parent
f9ad0621d5
commit
6a8b79f699
2 changed files with 142 additions and 27 deletions
|
@ -23,15 +23,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
// own
|
// own
|
||||||
#include "meta.h"
|
#include "meta.h"
|
||||||
#include "workspace_wrapper.h"
|
#include "workspace_wrapper.h"
|
||||||
|
#include "../thumbnailitem.h"
|
||||||
// KDE
|
// KDE
|
||||||
#include <kstandarddirs.h>
|
#include <kstandarddirs.h>
|
||||||
#include <KDE/KConfigGroup>
|
#include <KDE/KConfigGroup>
|
||||||
#include <KDE/KDebug>
|
#include <KDE/KDebug>
|
||||||
#include <KDE/KPluginInfo>
|
#include <KDE/KPluginInfo>
|
||||||
#include <KDE/KServiceTypeTrader>
|
#include <KDE/KServiceTypeTrader>
|
||||||
|
#include <kdeclarative.h>
|
||||||
// Qt
|
// Qt
|
||||||
#include <QtDBus/QDBusConnection>
|
#include <QtDBus/QDBusConnection>
|
||||||
#include <QtCore/QSettings>
|
#include <QtCore/QSettings>
|
||||||
|
#include <QtDeclarative/QDeclarativeContext>
|
||||||
|
#include <QtDeclarative/QDeclarativeEngine>
|
||||||
|
#include <QtDeclarative/QDeclarativeView>
|
||||||
|
#include <QtDeclarative/qdeclarative.h>
|
||||||
#include <QtScript/QScriptEngine>
|
#include <QtScript/QScriptEngine>
|
||||||
#include <QtScript/QScriptValue>
|
#include <QtScript/QScriptValue>
|
||||||
|
|
||||||
|
@ -50,36 +56,49 @@ QScriptValue kwinScriptPrint(QScriptContext *context, QScriptEngine *engine)
|
||||||
return engine->undefinedValue();
|
return engine->undefinedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KWin::AbstractScript::AbstractScript (int id, QString scriptName, QObject *parent)
|
||||||
KWin::Script::Script(int scriptId, QString scriptName, QObject *parent)
|
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_scriptId(scriptId)
|
, m_scriptId(id)
|
||||||
, m_engine(new QScriptEngine(this))
|
|
||||||
, m_workspace(new WorkspaceWrapper(m_engine))
|
|
||||||
, m_running(false)
|
, m_running(false)
|
||||||
|
, m_workspace(new WorkspaceWrapper(this))
|
||||||
{
|
{
|
||||||
m_scriptFile.setFileName(scriptName);
|
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()
|
KWin::Script::~Script()
|
||||||
{
|
{
|
||||||
QDBusConnection::sessionBus().unregisterObject('/' + QString::number(m_scriptId));
|
QDBusConnection::sessionBus().unregisterObject('/' + QString::number(scriptId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void KWin::Script::printMessage(const QString &message)
|
void KWin::Script::printMessage(const QString &message)
|
||||||
{
|
{
|
||||||
kDebug(1212) << m_scriptFile.fileName() << ":" << message;
|
kDebug(1212) << scriptFile().fileName() << ":" << message;
|
||||||
emit print(message);
|
emit print(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KWin::Script::run()
|
void KWin::Script::run()
|
||||||
{
|
{
|
||||||
if (m_running) {
|
if (running()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (m_scriptFile.open(QIODevice::ReadOnly)) {
|
if (scriptFile().open(QIODevice::ReadOnly)) {
|
||||||
QScriptValue workspace = m_engine->newQObject(m_workspace, QScriptEngine::QtOwnership,
|
QScriptValue workspace = m_engine->newQObject(AbstractScript::workspace(), QScriptEngine::QtOwnership,
|
||||||
QScriptEngine::ExcludeSuperClassContents | QScriptEngine::ExcludeDeleteLater);
|
QScriptEngine::ExcludeSuperClassContents | QScriptEngine::ExcludeDeleteLater);
|
||||||
m_engine->globalObject().setProperty("workspace", workspace, QScriptValue::Undeletable);
|
m_engine->globalObject().setProperty("workspace", workspace, QScriptValue::Undeletable);
|
||||||
m_engine->globalObject().setProperty("QTimer", constructTimerClass(m_engine));
|
m_engine->globalObject().setProperty("QTimer", constructTimerClass(m_engine));
|
||||||
|
@ -92,14 +111,14 @@ void KWin::Script::run()
|
||||||
printFunc.setData(m_engine->newQObject(this));
|
printFunc.setData(m_engine->newQObject(this));
|
||||||
m_engine->globalObject().setProperty("print", printFunc);
|
m_engine->globalObject().setProperty("print", printFunc);
|
||||||
|
|
||||||
QScriptValue ret = m_engine->evaluate(m_scriptFile.readAll());
|
QScriptValue ret = m_engine->evaluate(scriptFile().readAll());
|
||||||
|
|
||||||
if (ret.isError()) {
|
if (ret.isError()) {
|
||||||
sigException(ret);
|
sigException(ret);
|
||||||
deleteLater();
|
deleteLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_running = true;
|
setRunning(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KWin::Script::sigException(const QScriptValue& exception)
|
void KWin::Script::sigException(const QScriptValue& exception)
|
||||||
|
@ -119,11 +138,44 @@ void KWin::Script::sigException(const QScriptValue& exception)
|
||||||
emit printError(exception.toString());
|
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)
|
KWin::Scripting::Scripting(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
@ -142,7 +194,9 @@ void KWin::Scripting::start()
|
||||||
foreach (const KService::Ptr & service, offers) {
|
foreach (const KService::Ptr & service, offers) {
|
||||||
KPluginInfo plugininfo(service);
|
KPluginInfo plugininfo(service);
|
||||||
plugininfo.load(conf);
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +210,11 @@ void KWin::Scripting::start()
|
||||||
kDebug(1212) << "Could not find script file for " << pluginName;
|
kDebug(1212) << "Could not find script file for " << pluginName;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (javaScript) {
|
||||||
loadScript(file);
|
loadScript(file);
|
||||||
|
} else if (declarativeScript) {
|
||||||
|
loadDeclarativeScript(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runScripts();
|
runScripts();
|
||||||
|
@ -183,6 +241,15 @@ int KWin::Scripting::loadScript(const QString &filePath)
|
||||||
return id;
|
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()
|
KWin::Scripting::~Scripting()
|
||||||
{
|
{
|
||||||
QDBusConnection::sessionBus().unregisterObject("/Scripting");
|
QDBusConnection::sessionBus().unregisterObject("/Scripting");
|
||||||
|
|
|
@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include <QtCore/QFile>
|
#include <QtCore/QFile>
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
|
|
||||||
|
class QDeclarativeView;
|
||||||
class QScriptEngine;
|
class QScriptEngine;
|
||||||
class QScriptValue;
|
class QScriptValue;
|
||||||
|
|
||||||
|
@ -32,7 +33,46 @@ namespace KWin
|
||||||
{
|
{
|
||||||
class WorkspaceWrapper;
|
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_OBJECT
|
||||||
Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Scripting")
|
Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Scripting")
|
||||||
|
@ -40,14 +80,10 @@ public:
|
||||||
|
|
||||||
Script(int id, QString scriptName, QObject *parent = NULL);
|
Script(int id, QString scriptName, QObject *parent = NULL);
|
||||||
virtual ~Script();
|
virtual ~Script();
|
||||||
QString fileName() const {
|
|
||||||
return m_scriptFile.fileName();
|
|
||||||
}
|
|
||||||
|
|
||||||
void printMessage(const QString &message);
|
void printMessage(const QString &message);
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
Q_SCRIPTABLE void stop();
|
|
||||||
Q_SCRIPTABLE void run();
|
Q_SCRIPTABLE void run();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
@ -62,11 +98,22 @@ private slots:
|
||||||
void sigException(const QScriptValue &exception);
|
void sigException(const QScriptValue &exception);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_scriptId;
|
|
||||||
QScriptEngine *m_engine;
|
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")
|
Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Scripting")
|
||||||
private:
|
private:
|
||||||
QStringList scriptList;
|
QStringList scriptList;
|
||||||
QList<KWin::Script*> scripts;
|
QList<KWin::AbstractScript*> scripts;
|
||||||
|
|
||||||
// Preferably call ONLY at load time
|
// Preferably call ONLY at load time
|
||||||
void runScripts();
|
void runScripts();
|
||||||
|
@ -92,6 +139,7 @@ public:
|
||||||
void start();
|
void start();
|
||||||
~Scripting();
|
~Scripting();
|
||||||
Q_SCRIPTABLE Q_INVOKABLE int loadScript(const QString &filePath);
|
Q_SCRIPTABLE Q_INVOKABLE int loadScript(const QString &filePath);
|
||||||
|
Q_SCRIPTABLE Q_INVOKABLE int loadDeclarativeScript(const QString &filePath);
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void scriptDestroyed(QObject *object);
|
void scriptDestroyed(QObject *object);
|
||||||
|
|
Loading…
Reference in a new issue