Commit 566d4aa2 authored by Vlad Zahorodnii's avatar Vlad Zahorodnii
Browse files

scripting: Port Script to QJSEngine

QtScript is not well maintained and deprecated in favor of QJSEngine.
parent 28d2650f
registerScreenEdge(readConfig("Edge", 1), function() { workspace.slotToggleShowDesktop(); });
registerScreenEdge(readConfig("Edge", 1), () => workspace.slotToggleShowDesktop());
function init() {
var edge = readConfig("Edge", 1);
let edge = readConfig("Edge", 1);
if (readConfig("mode", "") == "unregister") {
unregisterScreenEdge(edge);
} else {
registerScreenEdge(edge, function() { workspace.slotToggleShowDesktop(); });
registerScreenEdge(edge, () => workspace.slotToggleShowDesktop());
}
}
options.configChanged.connect(init);
......
registerTouchScreenEdge(readConfig("Edge", 1), function() { workspace.slotToggleShowDesktop(); });
registerTouchScreenEdge(readConfig("Edge", 1), () => workspace.slotToggleShowDesktop());
......@@ -106,7 +106,6 @@ set(kwin_SRCS
scripting/scripting_logging.cpp
scripting/scripting_model.cpp
scripting/scriptingutils.cpp
scripting/timer.cpp
scripting/workspace_wrapper.cpp
shadow.cpp
sm.cpp
......
......@@ -7,6 +7,7 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "dbuscall.h"
#include "scriptingutils.h"
#include <QDBusConnection>
#include <QDBusMessage>
......@@ -35,7 +36,11 @@ void DBusCall::call()
emit failed();
return;
}
emit finished(watcher->reply().arguments());
QVariantList reply = watcher->reply().arguments();
std::for_each(reply.begin(), reply.end(), [](QVariant &variant) {
variant = dbusToVariant(variant);
});
emit finished(reply);
});
}
......
......@@ -8,8 +8,8 @@
*/
#include "meta.h"
#include "x11client.h"
#include <QRect>
#include <QtScript/QScriptEngine>
using namespace KWin::MetaScripting;
......@@ -85,161 +85,12 @@ void Rect::fromScriptValue(const QScriptValue& obj, QRect &rect)
}
// End of meta for QRect object
QScriptValue AbstractClient::toScriptValue(QScriptEngine *engine, const KAbstractClientRef &client)
{
return engine->newQObject(client, QScriptEngine::QtOwnership,
QScriptEngine::ExcludeChildObjects |
QScriptEngine::ExcludeDeleteLater |
QScriptEngine::PreferExistingWrapperObject |
QScriptEngine::AutoCreateDynamicProperties);
}
void AbstractClient::fromScriptValue(const QScriptValue &value, KWin::AbstractClient *&client)
{
client = qobject_cast<KWin::AbstractClient *>(value.toQObject());
}
QScriptValue X11Client::toScriptValue(QScriptEngine *eng, const KClientRef &client)
{
return eng->newQObject(client, QScriptEngine::QtOwnership,
QScriptEngine::ExcludeChildObjects |
QScriptEngine::ExcludeDeleteLater |
QScriptEngine::PreferExistingWrapperObject |
QScriptEngine::AutoCreateDynamicProperties);
}
void X11Client::fromScriptValue(const QScriptValue &value, KWin::X11Client *&client)
{
client = qobject_cast<KWin::X11Client *>(value.toQObject());
}
QScriptValue Toplevel::toScriptValue(QScriptEngine *eng, const KToplevelRef &client)
{
return eng->newQObject(client, QScriptEngine::QtOwnership,
QScriptEngine::ExcludeChildObjects |
QScriptEngine::ExcludeDeleteLater |
QScriptEngine::PreferExistingWrapperObject |
QScriptEngine::AutoCreateDynamicProperties);
}
void Toplevel::fromScriptValue(const QScriptValue &value, KToplevelRef &client)
{
client = qobject_cast<KWin::Toplevel*>(value.toQObject());
}
// Other helper functions
void KWin::MetaScripting::registration(QScriptEngine* eng)
{
qScriptRegisterMetaType<QPoint>(eng, Point::toScriptValue, Point::fromScriptValue);
qScriptRegisterMetaType<QSize>(eng, Size::toScriptValue, Size::fromScriptValue);
qScriptRegisterMetaType<QRect>(eng, Rect::toScriptValue, Rect::fromScriptValue);
qScriptRegisterMetaType<KAbstractClientRef>(eng, AbstractClient::toScriptValue, AbstractClient::fromScriptValue);
qScriptRegisterMetaType<KClientRef>(eng, X11Client::toScriptValue, X11Client::fromScriptValue);
qScriptRegisterMetaType<KToplevelRef>(eng, Toplevel::toScriptValue, Toplevel::fromScriptValue);
qScriptRegisterSequenceMetaType<QStringList>(eng);
qScriptRegisterSequenceMetaType< QList<KWin::AbstractClient*> >(eng);
qScriptRegisterSequenceMetaType< QList<KWin::X11Client *> >(eng);
}
QScriptValue KWin::MetaScripting::configExists(QScriptContext* ctx, QScriptEngine* eng)
{
QHash<QString, QVariant> scriptConfig = (((ctx->thisObject()).data()).toVariant()).toHash();
QVariant val = scriptConfig.value((ctx->argument(0)).toString(), QVariant());
return eng->toScriptValue<bool>(val.isValid());
}
QScriptValue KWin::MetaScripting::getConfigValue(QScriptContext* ctx, QScriptEngine* eng)
{
int num = ctx->argumentCount();
QHash<QString, QVariant> scriptConfig = (((ctx->thisObject()).data()).toVariant()).toHash();
/*
* Handle config.get() separately. Compute and return immediately.
*/
if (num == 0) {
QHash<QString, QVariant>::const_iterator i;
QScriptValue ret = eng->newArray();
for (i = scriptConfig.constBegin(); i != scriptConfig.constEnd(); ++i) {
ret.setProperty(i.key(), eng->newVariant(i.value()));
}
return ret;
}
if ((num == 1) && !((ctx->argument(0)).isArray())) {
QVariant val = scriptConfig.value((ctx->argument(0)).toString(), QVariant());
if (val.isValid()) {
return eng->newVariant(val);
} else {
return QScriptValue();
}
} else {
QScriptValue ret = eng->newArray();
int j = 0;
if ((ctx->argument(0)).isArray()) {
bool simple = (num == 1) ? 0 : (ctx->argument(1)).toBool();
QScriptValue array = (ctx->argument(0));
int len = (array.property(QStringLiteral("length")).isValid()) ? array.property(QStringLiteral("length")).toNumber() : 0;
for (int i = 0; i < len; i++) {
QVariant val = scriptConfig.value(array.property(i).toString(), QVariant());
if (val.isValid()) {
if (simple) {
ret.setProperty(j, eng->newVariant(val));
} else {
ret.setProperty(array.property(i).toString(), eng->newVariant(val));
}
j++;
}
}
} else {
for (int i = 0; i < num; i++) {
QVariant val = scriptConfig.value((ctx->argument(i)).toString(), QVariant());
if (val.isValid()) {
ret.setProperty((ctx->argument(i)).toString(), eng->newVariant(val));
j = 1;
}
}
}
if (j == 0) {
return QScriptValue();
} else {
return ret;
}
}
}
void KWin::MetaScripting::supplyConfig(QScriptEngine* eng, const QVariant& scriptConfig)
{
QScriptValue configObject = eng->newObject();
configObject.setData(eng->newVariant(scriptConfig));
configObject.setProperty(QStringLiteral("get"), eng->newFunction(getConfigValue, 0), QScriptValue::Undeletable);
configObject.setProperty(QStringLiteral("exists"), eng->newFunction(configExists, 0), QScriptValue::Undeletable);
configObject.setProperty(QStringLiteral("loaded"), ((scriptConfig.toHash().empty()) ? eng->newVariant((bool)0) : eng->newVariant((bool)1)), QScriptValue::Undeletable);
(eng->globalObject()).setProperty(QStringLiteral("config"), configObject);
}
void KWin::MetaScripting::supplyConfig(QScriptEngine* eng)
{
KWin::MetaScripting::supplyConfig(eng, QVariant(QHash<QString, QVariant>()));
}
void KWin::MetaScripting::valueMerge(QScriptValue& first, QScriptValue second)
{
QScriptValueIterator value_it(second);
while (value_it.hasNext()) {
value_it.next();
first.setProperty(value_it.name(), value_it.value());
}
}
......@@ -15,19 +15,8 @@
// forward declarations
class QPoint;
class QRect;
class QScriptContext;
class QSize;
namespace KWin {
class AbstractClient;
class Toplevel;
class X11Client;
}
typedef KWin::AbstractClient *KAbstractClientRef;
typedef KWin::X11Client *KClientRef;
typedef KWin::Toplevel* KToplevelRef;
namespace KWin
{
namespace MetaScripting
......@@ -66,59 +55,12 @@ QScriptValue toScriptValue(QScriptEngine*, const QRect&);
void fromScriptValue(const QScriptValue&, QRect&);
}
namespace AbstractClient
{
QScriptValue toScriptValue(QScriptEngine *engine, const KAbstractClientRef &client);
void fromScriptValue(const QScriptValue &value, KAbstractClientRef &client);
}
namespace X11Client
{
QScriptValue toScriptValue(QScriptEngine *eng, const KClientRef &client);
void fromScriptValue(const QScriptValue &value, KClientRef& client);
}
namespace Toplevel
{
QScriptValue toScriptValue(QScriptEngine *eng, const KToplevelRef &client);
void fromScriptValue(const QScriptValue &value, KToplevelRef& client);
}
/**
* Merges the second QScriptValue in the first one.
*/
void valueMerge(QScriptValue&, QScriptValue);
/**
* Registers all the meta conversion to the provided QScriptEngine
*/
void registration(QScriptEngine* eng);
/**
* Functions for the JS function objects, config.exists and config.get.
* Read scripting/IMPLIST for details on how they work
*/
QScriptValue configExists(QScriptContext*, QScriptEngine*);
QScriptValue getConfigValue(QScriptContext*, QScriptEngine*);
/**
* Provide a config object to the given QScriptEngine depending
* on the keys provided in the QVariant. The provided QVariant
* MUST returns (true) on isHash()
*/
void supplyConfig(QScriptEngine*, const QVariant&);
/**
* For engines whose scripts have no associated configuration.
*/
void supplyConfig(QScriptEngine*);
}
}
/**
* Code linked from plasma for QTimer.
*/
QScriptValue constructTimerClass(QScriptEngine *eng);
#endif
This diff is collapsed.
......@@ -4,6 +4,7 @@
SPDX-FileCopyrightText: 2010 Rohan Prabhu <rohan@rohanprabhu.com>
SPDX-FileCopyrightText: 2011 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -13,10 +14,9 @@
#include <kwinglobals.h>
#include <QFile>
#include <QHash>
#include <QStringList>
#include <QtScript/QScriptEngineAgent>
#include <QJSEngine>
#include <QJSValue>
#include <QDBusContext>
......@@ -26,12 +26,8 @@ class QQmlComponent;
class QQmlContext;
class QQmlEngine;
class QAction;
class QDBusPendingCallWatcher;
class QGraphicsScene;
class QMenu;
class QMutex;
class QScriptEngine;
class QScriptValue;
class QQuickWindow;
class KConfigGroup;
......@@ -41,9 +37,7 @@ typedef QList< QPair<bool, QPair<QString, QString > > > LoadScriptList;
namespace KWin
{
class AbstractClient;
class ScriptUnloaderAgent;
class QtScriptWorkspaceWrapper;
class X11Client;
class KWIN_EXPORT AbstractScript : public QObject
{
......@@ -51,15 +45,76 @@ class KWIN_EXPORT AbstractScript : public QObject
public:
AbstractScript(int id, QString scriptName, QString pluginName, QObject *parent = nullptr);
~AbstractScript() override;
int scriptId() const {
return m_scriptId;
}
QString fileName() const {
return m_fileName;
}
const QString &pluginName() {
return m_pluginName;
}
bool running() const {
return m_running;
}
KConfigGroup config() const;
public Q_SLOTS:
void stop();
virtual void run() = 0;
Q_SIGNALS:
void runningChanged(bool);
protected:
void setRunning(bool running) {
if (m_running == running) {
return;
}
m_running = running;
emit runningChanged(m_running);
}
private:
int m_scriptId;
QString m_fileName;
QString m_pluginName;
bool m_running;
};
class Script : public AbstractScript, QDBusContext
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Scripting")
public:
Script(int id, QString scriptName, QString pluginName, QObject *parent = nullptr);
virtual ~Script();
Q_INVOKABLE QVariant readConfig(const QString &key, const QVariant &defaultValue = QVariant());
Q_INVOKABLE void callDBus(const QString &service, const QString &path,
const QString &interface, const QString &method,
const QJSValue &arg1 = QJSValue(),
const QJSValue &arg2 = QJSValue(),
const QJSValue &arg3 = QJSValue(),
const QJSValue &arg4 = QJSValue(),
const QJSValue &arg5 = QJSValue(),
const QJSValue &arg6 = QJSValue(),
const QJSValue &arg7 = QJSValue(),
const QJSValue &arg8 = QJSValue(),
const QJSValue &arg9 = QJSValue());
Q_INVOKABLE bool registerShortcut(const QString &objectName, const QString &text,
const QString &keySequence, const QJSValue &callback);
Q_INVOKABLE bool registerScreenEdge(int edge, const QJSValue &callback);
Q_INVOKABLE bool unregisterScreenEdge(int edge);
Q_INVOKABLE bool registerTouchScreenEdge(int edge, const QJSValue &callback);
Q_INVOKABLE bool unregisterTouchScreenEdge(int edge);
void printMessage(const QString &message);
void registerShortcut(QAction *a, QScriptValue callback);
/**
* @brief Registers the given @p callback to be invoked whenever the UserActionsMenu is about
* to be showed. In the callback the script can create a further sub menu or menu entry to be
......@@ -69,7 +124,8 @@ public:
* @return void
* @see actionsForUserActionMenu
*/
void registerUseractionsMenuCallback(QScriptValue callback);
Q_INVOKABLE void registerUserActionsMenu(const QJSValue &callback);
/**
* @brief Creates actions for the UserActionsMenu by invoking the registered callbacks.
*
......@@ -112,54 +168,29 @@ public:
* @return QList< QAction* > List of QActions obtained from asking the registered callbacks
* @see registerUseractionsMenuCallback
*/
QList<QAction*> actionsForUserActionMenu(AbstractClient *c, QMenu *parent);
KConfigGroup config() const;
const QHash<QAction*, QScriptValue> &shortcutCallbacks() const {
return m_shortcutCallbacks;
}
QHash<int, QList<QScriptValue > > &screenEdgeCallbacks() {
return m_screenEdgeCallbacks;
}
int registerCallback(QScriptValue value);
QList<QAction *> actionsForUserActionMenu(AbstractClient *client, QMenu *parent);
public Q_SLOTS:
Q_SCRIPTABLE void stop();
Q_SCRIPTABLE virtual void run() = 0;
void slotPendingDBusCall(QDBusPendingCallWatcher *watcher);
void run() override;
private Q_SLOTS:
void globalShortcutTriggered();
bool borderActivated(ElectricBorder edge);
/**
* @brief Slot invoked when a menu action is destroyed. Used to remove the action and callback
* from the map of actions.
*
* @param object The destroyed action
* Callback for when loadScriptFromFile has finished.
*/
void actionDestroyed(QObject *object);
Q_SIGNALS:
Q_SCRIPTABLE void print(const QString &text);
void runningChanged(bool);
void slotScriptLoadedFromFile();
protected:
bool running() const {
return m_running;
}
void setRunning(bool running) {
if (m_running == running) {
return;
}
m_running = running;
emit runningChanged(m_running);
}
int scriptId() const {
return m_scriptId;
}
/**
* Called when any reserve screen edge is triggered.
*/
bool slotBorderActivated(ElectricBorder border);
private:
/**
* Read the script from file into a byte array.
* If file cannot be read an empty byte array is returned.
*/
QByteArray loadScriptFromFile(const QString &fileName);
/**
* @brief Parses the @p value to either a QMenu or QAction.
*
......@@ -167,7 +198,8 @@ private:
* @param parent The parent to use for the created menu or action
* @return QAction* The parsed action or menu action, if parsing fails returns @c null.
*/
QAction *scriptValueToAction(QScriptValue &value, QMenu *parent);
QAction *scriptValueToAction(const QJSValue &value, QMenu *parent);
/**
* @brief Creates a new QAction from the provided data and registers it for invoking the
* @p callback when the action is triggered.
......@@ -182,7 +214,8 @@ private:
* @param parent The parent to be used for the new created action
* @return QAction* The created action
*/
QAction *createAction(const QString &title, bool checkable, bool checked, QScriptValue &callback, QMenu *parent);
QAction *createAction(const QString &title, const QJSValue &item, QMenu *parent);
/**
* @brief Parses the @p items and creates a QMenu from it.
*
......@@ -191,75 +224,14 @@ private:
* @param parent The parent to use for the new created menu
* @return QAction* The menu action for the new Menu
*/
QAction *createMenu(const QString &title, QScriptValue &items, QMenu *parent);
int m_scriptId;
QString m_fileName;
QString m_pluginName;
bool m_running;
QHash<QAction*, QScriptValue> m_shortcutCallbacks;
QHash<int, QList<QScriptValue> > m_screenEdgeCallbacks;
QHash<int, QScriptValue> m_callbacks;
/**
* @brief List of registered functions to call when the UserActionsMenu is about to show
* to add further entries.
*/
QList<QScriptValue> m_userActionsMenuCallbacks;
};
class Script : public AbstractScript, QDBusContext
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Scripting")
public:
Script(int id, QString scriptName, QString pluginName, QObject *parent = nullptr);
~Script() override;
QScriptEngine *engine() {
return m_engine;
}
bool registerTouchScreenCallback(int edge, QScriptValue callback);
bool unregisterTouchScreenCallback(int edge);
public Q_SLOTS:
Q_SCRIPTABLE void run() override;
Q_SIGNALS:
Q_SCRIPTABLE void printError(const QString &text);
private Q_SLOTS:
/**
* A nice clean way to handle exceptions in scripting.
* TODO: Log to file, show from notifier..
*/
void sigException(const QScriptValue &exception);
/**
* Callback for when loadScriptFromFile has finished.
*/
void slotScriptLoadedFromFile();
QAction *createMenu(const QString &title, const QJSValue &items, QMenu *parent);
private:
void installScriptFunctions(QScriptEngine *engine);
/**
* Read the script from file into a byte array.
* If file cannot be read an empty byte array is returned.
*/
QByteArray loadScriptFromFile(const QString &fileName);
QScriptEngine *m_engine;
QJSEngine *m_engine;
QDBusMessage m_invocationContext;
bool m_starting;
QScopedPointer<ScriptUnloaderAgent> m_agent;
QHash<int, QAction*> m_touchScreenEdgeCallbacks;
};
class ScriptUnloaderAgent : public QScriptEngineAgent
{
public:
explicit ScriptUnloaderAgent(Script *script);
void scriptUnload(qint64 id) override;
private:
Script *m_script;
QHash<int, QJSValueList> m_screenEdgeCallbacks;
QHash<int, QAction *> m_touchScreenEdgeCallbacks;
QJSValueList m_userActionsMenuCallbacks;
};
class DeclarativeScript : public AbstractScript
......
......@@ -8,6 +8,9 @@
*/
#include "scriptingutils.h"
#include <QDBusObjectPath>
#include <QDBusSignature>
namespace KWin
{
bool validateParameters(QScriptContext *context, int min, int max)
......@@ -32,4 +35,58 @@ bool validateArgumentType<QVariant>(QScriptContext *context, int argument)
return result;
}
QVariant dbusToVariant(const QVariant &variant)
{