33#include "virtualdesktops.h"
37#include <KConfigGroup>
38#include <KConfigPropertyMap>
39#include <KGlobalAccel>
40#include <KLocalizedContext>
41#include <KPackage/PackageLoader>
43#include <QDBusConnection>
44#include <QDBusPendingCallWatcher>
46#include <QFutureWatcher>
50#include <QQmlExpression>
51#include <QQuickWindow>
53#include <QStandardPaths>
54#include <QtConcurrentRun>
56#include "scriptadaptor.h"
58static QRect scriptValueToRect(
const QJSValue &value)
60 return QRect(value.property(QStringLiteral(
"x")).toInt(),
61 value.property(QStringLiteral(
"y")).toInt(),
62 value.property(QStringLiteral(
"width")).toInt(),
63 value.property(QStringLiteral(
"height")).toInt());
66static QRectF scriptValueToRectF(
const QJSValue &value)
68 return QRectF(value.property(QStringLiteral(
"x")).toNumber(),
69 value.property(QStringLiteral(
"y")).toNumber(),
70 value.property(QStringLiteral(
"width")).toNumber(),
71 value.property(QStringLiteral(
"height")).toNumber());
74static QPoint scriptValueToPoint(
const QJSValue &value)
76 return QPoint(value.property(QStringLiteral(
"x")).toInt(),
77 value.property(QStringLiteral(
"y")).toInt());
80static QPointF scriptValueToPointF(
const QJSValue &value)
82 return QPointF(value.property(QStringLiteral(
"x")).toNumber(),
83 value.property(QStringLiteral(
"y")).toNumber());
86static QSize scriptValueToSize(
const QJSValue &value)
88 return QSize(value.property(QStringLiteral(
"width")).toInt(),
89 value.property(QStringLiteral(
"height")).toInt());
92static QSizeF scriptValueToSizeF(
const QJSValue &value)
94 return QSizeF(value.property(QStringLiteral(
"width")).toNumber(),
95 value.property(QStringLiteral(
"height")).toNumber());
101 , m_fileName(scriptName)
102 , m_pluginName(pluginName)
105 if (m_pluginName.isNull()) {
106 m_pluginName = scriptName;
109 new ScriptAdaptor(
this);
110 QDBusConnection::sessionBus().registerObject(QStringLiteral(
"/Scripting/Script") + QString::number(
scriptId()),
this, QDBusConnection::ExportAdaptors);
119 return kwinApp()->config()->group(QLatin1String(
"Script-") + m_pluginName);
134 , m_engine(new QJSEngine(this))
138 if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QRect>()) {
139 QMetaType::registerConverter<QJSValue, QRect>(scriptValueToRect);
141 if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QRectF>()) {
142 QMetaType::registerConverter<QJSValue, QRectF>(scriptValueToRectF);
145 if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QPoint>()) {
146 QMetaType::registerConverter<QJSValue, QPoint>(scriptValueToPoint);
148 if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QPointF>()) {
149 QMetaType::registerConverter<QJSValue, QPointF>(scriptValueToPointF);
152 if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QSize>()) {
153 QMetaType::registerConverter<QJSValue, QSize>(scriptValueToSize);
155 if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QSizeF>()) {
156 QMetaType::registerConverter<QJSValue, QSizeF>(scriptValueToSizeF);
166 if (running() || m_starting) {
170 if (calledFromDBus()) {
171 m_invocationContext = message();
172 setDelayedReply(
true);
177 connect(watcher, &QFutureWatcherBase::finished,
this, &Script::slotScriptLoadedFromFile);
178 watcher->setFuture(QtConcurrent::run(&KWin::Script::loadScriptFromFile,
this, fileName()));
181QByteArray KWin::Script::loadScriptFromFile(
const QString &fileName)
183 QFile file(fileName);
184 if (!file.open(QIODevice::ReadOnly)) {
187 QByteArray result(file.readAll());
191void KWin::Script::slotScriptLoadedFromFile()
198 if (watcher->result().isNull()) {
201 watcher->deleteLater();
203 if (m_invocationContext.type() == QDBusMessage::MethodCallMessage) {
204 auto reply = m_invocationContext.createErrorReply(
"org.kde.kwin.Scripting.FileError", QString(
"Could not open %1").arg(fileName()));
205 QDBusConnection::sessionBus().send(reply);
206 m_invocationContext = QDBusMessage();
213 m_engine->installExtensions(QJSEngine::ConsoleExtension);
216 QJSValue timerMetaObject = m_engine->newQMetaObject(&ScriptTimer::staticMetaObject);
217 m_engine->globalObject().setProperty(
"QTimer", timerMetaObject);
220 m_engine->globalObject().setProperty(QStringLiteral(
"KWin"), m_engine->newQMetaObject(&QtScriptWorkspaceWrapper::staticMetaObject));
223 QJSValue optionsObject = m_engine->newQObject(
options);
224 QQmlEngine::setObjectOwnership(
options, QQmlEngine::CppOwnership);
225 m_engine->globalObject().setProperty(QStringLiteral(
"options"), optionsObject);
228 QJSValue workspaceObject = m_engine->newQObject(
Scripting::self()->workspaceWrapper());
229 QQmlEngine::setObjectOwnership(
Scripting::self()->workspaceWrapper(), QQmlEngine::CppOwnership);
230 m_engine->globalObject().setProperty(QStringLiteral(
"workspace"), workspaceObject);
232 QJSValue self = m_engine->newQObject(
this);
233 QQmlEngine::setObjectOwnership(
this, QQmlEngine::CppOwnership);
235 static const QStringList globalProperties{
236 QStringLiteral(
"readConfig"),
237 QStringLiteral(
"callDBus"),
239 QStringLiteral(
"registerShortcut"),
240 QStringLiteral(
"registerScreenEdge"),
241 QStringLiteral(
"unregisterScreenEdge"),
242 QStringLiteral(
"registerTouchScreenEdge"),
243 QStringLiteral(
"unregisterTouchScreenEdge"),
244 QStringLiteral(
"registerUserActionsMenu"),
247 for (
const QString &propertyName : globalProperties) {
248 m_engine->globalObject().setProperty(propertyName, self.property(propertyName));
253 QJSValue result = m_engine->evaluate(QStringLiteral(R
"(
254 function assert(condition, message) {
255 console.assert(condition, message || 'Assertion failed');
257 function assertTrue(condition, message) {
258 console.assert(condition, message || 'Assertion failed');
260 function assertFalse(condition, message) {
261 console.assert(!condition, message || 'Assertion failed');
263 function assertNull(value, message) {
264 console.assert(value === null, message || 'Assertion failed');
266 function assertNotNull(value, message) {
267 console.assert(value !== null, message || 'Assertion failed');
269 function assertEquals(expected, actual, message) {
270 console.assert(expected === actual, message || 'Assertion failed');
273 Q_ASSERT(!result.isError());
275 result = m_engine->evaluate(QString::fromUtf8(watcher->result()), fileName());
276 if (result.isError()) {
277 qCWarning(KWIN_SCRIPTING,
"%s:%d: error: %s", qPrintable(fileName()),
278 result.property(QStringLiteral(
"lineNumber")).toInt(),
279 qPrintable(result.property(QStringLiteral(
"message")).toString()));
283 if (m_invocationContext.type() == QDBusMessage::MethodCallMessage) {
284 auto reply = m_invocationContext.createReply();
285 QDBusConnection::sessionBus().send(reply);
286 m_invocationContext = QDBusMessage();
289 watcher->deleteLater();
296 return config().readEntry(key, defaultValue);
300 const QString &method,
const QJSValue &arg1,
const QJSValue &arg2,
301 const QJSValue &arg3,
const QJSValue &arg4,
const QJSValue &arg5,
302 const QJSValue &arg6,
const QJSValue &arg7,
const QJSValue &arg8,
303 const QJSValue &arg9)
305 QJSValueList jsArguments;
306 jsArguments.reserve(9);
308 if (!arg1.isUndefined()) {
311 if (!arg2.isUndefined()) {
314 if (!arg3.isUndefined()) {
317 if (!arg4.isUndefined()) {
320 if (!arg5.isUndefined()) {
323 if (!arg6.isUndefined()) {
326 if (!arg7.isUndefined()) {
329 if (!arg8.isUndefined()) {
332 if (!arg9.isUndefined()) {
337 if (!jsArguments.isEmpty() && jsArguments.last().isCallable()) {
338 callback = jsArguments.takeLast();
341 QVariantList dbusArguments;
342 dbusArguments.reserve(jsArguments.count());
343 for (
const QJSValue &jsArgument : std::as_const(jsArguments)) {
344 dbusArguments << jsArgument.toVariant();
347 QDBusMessage message = QDBusMessage::createMethodCall(service, path, interface, method);
348 message.setArguments(dbusArguments);
350 const QDBusPendingCall call = QDBusConnection::sessionBus().asyncCall(message);
351 if (callback.isUndefined()) {
355 QDBusPendingCallWatcher *watcher =
new QDBusPendingCallWatcher(call,
this);
356 connect(watcher, &QDBusPendingCallWatcher::finished,
this, [
this, callback](QDBusPendingCallWatcher *self) {
359 if (self->isError()) {
360 qCWarning(KWIN_SCRIPTING) <<
"Received D-Bus message is error:" << self->error().message();
364 QJSValueList arguments;
365 const QVariantList reply = self->reply().arguments();
366 for (
const QVariant &variant : reply) {
367 arguments << m_engine->toScriptValue(
dbusToVariant(variant));
370 QJSValue(callback).call(arguments);
376 if (!callback.isCallable()) {
377 m_engine->throwError(QStringLiteral(
"Shortcut handler must be callable"));
381 QAction *action =
new QAction(
this);
382 action->setObjectName(objectName);
383 action->setText(text);
385 const QKeySequence shortcut = keySequence;
386 KGlobalAccel::self()->setShortcut(action, {shortcut});
388 connect(action, &QAction::triggered,
this, [
this, action, callback]() {
389 QJSValue(callback).call({m_engine->toScriptValue(action)});
397 if (!callback.isCallable()) {
398 m_engine->throwError(QStringLiteral(
"Screen edge handler must be callable"));
402 QJSValueList &callbacks = m_screenEdgeCallbacks[edge];
403 if (callbacks.isEmpty()) {
407 callbacks << callback;
414 auto it = m_screenEdgeCallbacks.find(edge);
415 if (it == m_screenEdgeCallbacks.end()) {
420 m_screenEdgeCallbacks.erase(it);
427 if (!callback.isCallable()) {
428 m_engine->throwError(QStringLiteral(
"Touch screen edge handler must be callable"));
431 if (m_touchScreenEdgeCallbacks.contains(edge)) {
435 QAction *action =
new QAction(
this);
437 m_touchScreenEdgeCallbacks.insert(edge, action);
439 connect(action, &QAction::triggered,
this, [callback]() {
440 QJSValue(callback).call();
448 auto it = m_touchScreenEdgeCallbacks.find(edge);
449 if (it == m_touchScreenEdgeCallbacks.end()) {
454 m_touchScreenEdgeCallbacks.erase(it);
461 if (!callback.isCallable()) {
462 m_engine->throwError(QStringLiteral(
"User action handler must be callable"));
465 m_userActionsMenuCallbacks.append(callback);
470 QList<QAction *> actions;
471 actions.reserve(m_userActionsMenuCallbacks.count());
473 for (QJSValue callback : std::as_const(m_userActionsMenuCallbacks)) {
474 const QJSValue result = callback.call({m_engine->toScriptValue(client)});
475 if (result.isError()) {
478 if (!result.isObject()) {
481 if (QAction *action = scriptValueToAction(result, parent)) {
491 const QJSValueList callbacks = m_screenEdgeCallbacks.value(border);
492 if (callbacks.isEmpty()) {
495 std::for_each(callbacks.begin(), callbacks.end(), [](QJSValue callback) {
501QAction *KWin::Script::scriptValueToAction(
const QJSValue &value, QMenu *parent)
503 const QString title = value.property(QStringLiteral(
"text")).toString();
504 if (title.isEmpty()) {
509 const QJSValue itemsValue = value.property(QStringLiteral(
"items"));
510 if (!itemsValue.isUndefined()) {
511 return createMenu(title, itemsValue, parent);
514 return createAction(title, value, parent);
517QAction *KWin::Script::createAction(
const QString &title,
const QJSValue &item, QMenu *parent)
519 const QJSValue callback = item.property(QStringLiteral(
"triggered"));
520 if (!callback.isCallable()) {
524 const bool checkable = item.property(QStringLiteral(
"checkable")).toBool();
525 const bool checked = item.property(QStringLiteral(
"checked")).toBool();
527 QAction *action =
new QAction(title, parent);
528 action->setCheckable(checkable);
529 action->setChecked(checked);
531 connect(action, &QAction::triggered,
this, [
this, action, callback]() {
532 QJSValue(callback).call({m_engine->toScriptValue(action)});
538QAction *KWin::Script::createMenu(
const QString &title,
const QJSValue &items, QMenu *parent)
540 if (!items.isArray()) {
544 const int length = items.property(QStringLiteral(
"length")).toInt();
549 QMenu *menu =
new QMenu(title, parent);
550 for (
int i = 0; i < length; ++i) {
551 const QJSValue value = items.property(QString::number(i));
552 if (!value.isObject()) {
555 if (QAction *action = scriptValueToAction(value, menu)) {
556 menu->addAction(action);
560 return menu->menuAction();
565 , m_context(new QQmlContext(
Scripting::self()->declarativeScriptSharedContext(), this))
566 , m_component(new QQmlComponent(
Scripting::self()->qmlEngine(), this))
581 m_component->loadUrl(QUrl::fromLocalFile(fileName()));
582 if (m_component->isLoading()) {
583 connect(m_component, &QQmlComponent::statusChanged,
this, &DeclarativeScript::createComponent);
589void KWin::DeclarativeScript::createComponent()
591 if (m_component->isError()) {
592 qCWarning(KWIN_SCRIPTING) <<
"Component failed to load: " << m_component->errors();
594 if (QObject *
object = m_component->create(m_context)) {
595 object->setParent(
this);
613 return m_script->config().readEntry(key, defaultValue);
625KWin::Scripting::Scripting(QObject *parent)
627 , m_scriptsLock(new QRecursiveMutex)
628 , m_qmlEngine(new QQmlEngine(this))
629 , m_declarativeScriptSharedContext(new QQmlContext(m_qmlEngine, this))
632 m_qmlEngine->setProperty(
"_kirigamiTheme", QStringLiteral(
"KirigamiPlasmaStyle"));
633 m_qmlEngine->rootContext()->setContextObject(
new KLocalizedContext(m_qmlEngine));
635 QDBusConnection::sessionBus().registerObject(QStringLiteral(
"/Scripting"),
this, QDBusConnection::ExportScriptableContents | QDBusConnection::ExportScriptableInvokables);
640void KWin::Scripting::init()
642 qRegisterMetaType<QList<KWin::Output *>>();
643 qRegisterMetaType<QList<KWin::Window *>>();
644 qRegisterMetaType<QList<KWin::VirtualDesktop *>>();
646 qmlRegisterType<DesktopBackgroundItem>(
"org.kde.kwin", 3, 0,
"DesktopBackground");
647 qmlRegisterType<WindowThumbnailItem>(
"org.kde.kwin", 3, 0,
"WindowThumbnail");
648 qmlRegisterType<DBusCall>(
"org.kde.kwin", 3, 0,
"DBusCall");
649 qmlRegisterType<ScreenEdgeHandler>(
"org.kde.kwin", 3, 0,
"ScreenEdgeHandler");
650 qmlRegisterType<ShortcutHandler>(
"org.kde.kwin", 3, 0,
"ShortcutHandler");
651 qmlRegisterType<SwipeGestureHandler>(
"org.kde.kwin", 3, 0,
"SwipeGestureHandler");
652 qmlRegisterType<PinchGestureHandler>(
"org.kde.kwin", 3, 0,
"PinchGestureHandler");
653 qmlRegisterType<WindowModel>(
"org.kde.kwin", 3, 0,
"WindowModel");
654 qmlRegisterType<WindowFilterModel>(
"org.kde.kwin", 3, 0,
"WindowFilterModel");
655 qmlRegisterType<VirtualDesktopModel>(
"org.kde.kwin", 3, 0,
"VirtualDesktopModel");
656 qmlRegisterUncreatableType<KWin::QuickSceneView>(
"org.kde.kwin", 3, 0,
"SceneView", QStringLiteral(
"Can't instantiate an object of type SceneView"));
657 qmlRegisterType<ScriptedQuickSceneEffect>(
"org.kde.kwin", 3, 0,
"SceneEffect");
659 qmlRegisterSingletonType<DeclarativeScriptWorkspaceWrapper>(
"org.kde.kwin", 3, 0,
"Workspace", [](QQmlEngine *qmlEngine, QJSEngine *jsEngine) {
660 return new DeclarativeScriptWorkspaceWrapper();
662 qmlRegisterSingletonInstance(
"org.kde.kwin", 3, 0,
"Options",
options);
664 qmlRegisterAnonymousType<KConfigPropertyMap>(
"org.kde.kwin", 3);
665 qmlRegisterAnonymousType<KWin::Output>(
"org.kde.kwin", 3);
666 qmlRegisterAnonymousType<KWin::Window>(
"org.kde.kwin", 3);
667 qmlRegisterAnonymousType<KWin::VirtualDesktop>(
"org.kde.kwin", 3);
668 qmlRegisterAnonymousType<QAbstractItemModel>(
"org.kde.kwin", 3);
669 qmlRegisterAnonymousType<KWin::TileManager>(
"org.kde.kwin", 3);
671 qmlRegisterUncreatableType<KWin::CustomTile>(
"org.kde.kwin", 3, 0,
"CustomTile", QStringLiteral(
"Cannot create objects of type Tile"));
672 qmlRegisterUncreatableType<KWin::Tile>(
"org.kde.kwin", 3, 0,
"Tile", QStringLiteral(
"Cannot create objects of type AbstractTile"));
682 watcher->setFuture(QtConcurrent::run(
this, &KWin::Scripting::queryScriptsToLoad, pluginStates, offers));
685 for (LoadScriptList::const_iterator it = scriptsToLoad.constBegin();
686 it != scriptsToLoad.constEnd();
689 loadScript(it->second.first, it->second.second);
691 loadDeclarativeScript(it->second.first, it->second.second);
701 KSharedConfig::Ptr _config = kwinApp()->config();
702 static bool s_started =
false;
704 _config->reparseConfiguration();
708 QMap<QString, QString> pluginStates = KConfigGroup(_config, QStringLiteral(
"Plugins")).entryMap();
709 const QString scriptFolder = QStringLiteral(
"kwin/scripts/");
710 const auto offers = KPackage::PackageLoader::self()->listPackages(QStringLiteral(
"KWin/Script"), scriptFolder);
714 for (
const KPluginMetaData &service : offers) {
715 const QString value = pluginStates.value(service.pluginId() + QLatin1String(
"Enabled"), QString());
716 const bool enabled = value.isNull() ? service.isEnabledByDefault() : QVariant(value).toBool();
717 const bool javaScript = service.value(QStringLiteral(
"X-Plasma-API")) == QLatin1String(
"javascript");
718 const bool declarativeScript = service.value(QStringLiteral(
"X-Plasma-API")) == QLatin1String(
"declarativescript");
719 if (!javaScript && !declarativeScript) {
724 if (isScriptLoaded(service.pluginId())) {
726 unloadScript(service.pluginId());
730 const QString pluginName = service.pluginId();
732 const QString relScriptPath = scriptFolder + pluginName + QLatin1String(
"/contents/") + (javaScript ? QLatin1String(
"code/main.js") : QLatin1String(
"ui/main.qml"));
733 const QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, relScriptPath);
734 if (file.isEmpty()) {
735 qCDebug(KWIN_SCRIPTING) <<
"Could not find script file for " << pluginName;
738 scriptsToLoad << qMakePair(javaScript, qMakePair(file, pluginName));
740 return scriptsToLoad;
743void KWin::Scripting::slotScriptsQueried()
752 for (LoadScriptList::const_iterator it = scriptsToLoad.constBegin();
753 it != scriptsToLoad.constEnd();
756 loadScript(it->second.first, it->second.second);
758 loadDeclarativeScript(it->second.first, it->second.second);
763 watcher->deleteLater();
768 return findScript(pluginName) !=
nullptr;
773 QMutexLocker locker(m_scriptsLock.get());
775 if (script->pluginName() == pluginName) {
784 QMutexLocker locker(m_scriptsLock.get());
786 if (script->pluginName() == pluginName) {
787 script->deleteLater();
794void KWin::Scripting::runScripts()
796 QMutexLocker locker(m_scriptsLock.get());
797 for (
int i = 0; i < scripts.size(); i++) {
798 scripts.at(i)->run();
804 QMutexLocker locker(m_scriptsLock.get());
810 QMutexLocker locker(m_scriptsLock.get());
811 if (isScriptLoaded(pluginName)) {
814 const int id = scripts.size();
817 scripts.append(script);
823 QMutexLocker locker(m_scriptsLock.get());
824 if (isScriptLoaded(pluginName)) {
827 const int id = scripts.size();
830 scripts.append(script);
836 QDBusConnection::sessionBus().unregisterObject(QStringLiteral(
"/Scripting"));
842 QList<QAction *> actions;
845 if (
Script *script = qobject_cast<Script *>(s)) {
846 actions << script->actionsForUserActionMenu(c, parent);
852#include "moc_scripting.cpp"
AbstractScript(int id, QString scriptName, QString pluginName, QObject *parent=nullptr)
KConfigGroup config() const
~AbstractScript() override
Q_SCRIPTABLE void run() override
~DeclarativeScript() override
DeclarativeScript(int id, QString scriptName, QString pluginName, QObject *parent=nullptr)
~JSEngineGlobalMethodsWrapper() override
JSEngineGlobalMethodsWrapper(DeclarativeScript *parent)
QVariant readConfig(const QString &key, QVariant defaultValue=QVariant())
void reserve(ElectricBorder border, QObject *object, const char *callback)
void unreserve(ElectricBorder border, QObject *object)
void reserveTouch(ElectricBorder border, QAction *action, TouchCallback::CallbackFunction callback=nullptr)
Q_INVOKABLE QVariant readConfig(const QString &key, const QVariant &defaultValue=QVariant())
Q_INVOKABLE bool unregisterTouchScreenEdge(int edge)
Script(int id, QString scriptName, QString pluginName, QObject *parent=nullptr)
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 registerTouchScreenEdge(int edge, const QJSValue &callback)
Q_INVOKABLE bool registerShortcut(const QString &objectName, const QString &text, const QString &keySequence, const QJSValue &callback)
QList< QAction * > actionsForUserActionMenu(Window *client, QMenu *parent)
Creates actions for the UserActionsMenu by invoking the registered callbacks.
Q_INVOKABLE void registerUserActionsMenu(const QJSValue &callback)
Registers the given callback to be invoked whenever the UserActionsMenu is about to be showed....
Q_INVOKABLE bool unregisterScreenEdge(int edge)
Q_INVOKABLE bool registerScreenEdge(int edge, const QJSValue &callback)
Q_INVOKABLE ScriptTimer(QObject *parent=nullptr)
AbstractScript * findScript(const QString &pluginName) const
static Scripting * create(QObject *parent)
Q_SCRIPTABLE Q_INVOKABLE bool isScriptLoaded(const QString &pluginName) const
Q_SCRIPTABLE Q_INVOKABLE bool unloadScript(const QString &pluginName)
QList< QAction * > actionsForUserActionMenu(Window *c, QMenu *parent)
Invokes all registered callbacks to add actions to the UserActionsMenu.
Q_SCRIPTABLE Q_INVOKABLE int loadDeclarativeScript(const QString &filePath, const QString &pluginName=QString())
Q_SCRIPTABLE Q_INVOKABLE int loadScript(const QString &filePath, const QString &pluginName=QString())
static Scripting * self()
void scriptDestroyed(QObject *object)
Q_SCRIPTABLE void start()
ScreenEdges * screenEdges() const
void workspaceInitialized()
static Workspace * self()
QVariant dbusToVariant(const QVariant &variant)
QList< QPair< bool, QPair< QString, QString > > > LoadScriptList
true == javascript, false == qml