From c9c4e020e249638a851fe7c7812f8ea81bbc5c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Thu, 10 May 2012 16:09:36 +0200 Subject: [PATCH] Screen Edge bindings for Scripts and Scripted Effects ScreenEdge is changed to emit a signal whenever a screen edge got activated without an action or effect taking care of it. A Script can reserve one to many callbacks for an edge and the callback get's triggered whenever the signal is emitted. On deconstruction of the Script the edge is unreserved again. FEATURE: 299275 FIXED-IN: 4.9.0 REVIEW: 104904 --- screenedge.cpp | 7 ++-- screenedge.h | 9 ++++- scripting/documentation-global.xml | 8 +++++ scripting/scriptedeffect.cpp | 27 +++++++++++++++ scripting/scriptedeffect.h | 6 ++++ scripting/scripting.cpp | 22 +++++++++++++ scripting/scripting.h | 7 ++++ scripting/scriptingutils.h | 53 ++++++++++++++++++++++++++++++ scripting/workspace_wrapper.h | 13 ++++++++ 9 files changed, 149 insertions(+), 3 deletions(-) diff --git a/screenedge.cpp b/screenedge.cpp index 8bdc1c17f0..9f714946f5 100644 --- a/screenedge.cpp +++ b/screenedge.cpp @@ -289,8 +289,11 @@ void ScreenEdge::check(const QPoint& pos, Time now) if (effects && static_cast(effects)->borderActivated(border)) {} // Handled by effects else { - switchDesktop(border, pos); - return; // Don't reset cursor position + if (options->electricBorders() == Options::ElectricAlways) { + switchDesktop(border, pos); + return; // Don't reset cursor position + } + emit activated(border); } } } diff --git a/screenedge.h b/screenedge.h index 16a2542d71..37645e498f 100644 --- a/screenedge.h +++ b/screenedge.h @@ -44,7 +44,7 @@ namespace KWin { * @author Arthur Arlt * @since 4.8 */ -class ScreenEdge : QObject { +class ScreenEdge : public QObject { Q_OBJECT public: ScreenEdge(); @@ -108,6 +108,13 @@ public Q_SLOTS: * actions or disabled desktop switching. */ void update(bool force=false); +Q_SIGNALS: + /** + * Emitted when the @p border got activated and there is neither an effect nor a global + * action configured for this @p border. + * @param border The border which got activated + **/ + void activated(ElectricBorder border); private: /** * Switch the desktop if desktop switching is enabled and a screen edge diff --git a/scripting/documentation-global.xml b/scripting/documentation-global.xml index 5c39b1fdd9..cfe6355829 100644 --- a/scripting/documentation-global.xml +++ b/scripting/documentation-global.xml @@ -41,6 +41,14 @@ Reads the config value for key in the Script's configuration with the optional default value. If not providing a default value and no value stored in the configuration an undefined value is returned. + + Q_SCRIPTABLE bool + bool KWin::Scripting::registerScreenEdge + (ElectricBorder border, QScriptValue callback) + registerScreenEdge + + Registers the callback for the screen edge. When the mouse gets pushed against the given edge the callback will be invoked. + Q_SCRIPTABLE bool bool KWin::Scripting::registerShortcut diff --git a/scripting/scriptedeffect.cpp b/scripting/scriptedeffect.cpp index 1fb2c28cf1..44cea28054 100644 --- a/scripting/scriptedeffect.cpp +++ b/scripting/scriptedeffect.cpp @@ -21,6 +21,7 @@ along with this program. If not, see . #include "scriptedeffect.h" #include "meta.h" #include "scriptingutils.h" +#include "workspace_wrapper.h" // KDE #include #include @@ -83,6 +84,11 @@ QScriptValue kwinScriptGlobalShortcut(QScriptContext *context, QScriptEngine *en return globalShortcut(context, engine); } +QScriptValue kwinScriptScreenEdge(QScriptContext *context, QScriptEngine *engine) +{ + return registerScreenEdge(context, engine); +} + QScriptValue effectWindowToScriptValue(QScriptEngine *eng, const KEffectWindowRef &window) { return eng->newQObject(window, QScriptEngine::QtOwnership, @@ -140,6 +146,20 @@ ScriptedEffect::ScriptedEffect() , m_scriptFile(QString()) { connect(m_engine, SIGNAL(signalHandlerException(QScriptValue)), SLOT(signalHandlerException(QScriptValue))); +#ifdef KWIN_BUILD_SCREENEDGES + connect(Workspace::self()->screenEdge(), SIGNAL(activated(ElectricBorder)), SLOT(borderActivated(ElectricBorder))); +#endif +} + +ScriptedEffect::~ScriptedEffect() +{ +#ifdef KWIN_BUILD_SCREENEDGES + for (QHash >::const_iterator it = m_screenEdgeCallbacks.constBegin(); + it != m_screenEdgeCallbacks.constEnd(); + ++it) { + KWin::Workspace::self()->screenEdge()->unreserve(static_cast(it.key())); + } +#endif } bool ScriptedEffect::init(const QString &effectName, const QString &pathToScript) @@ -155,6 +175,7 @@ bool ScriptedEffect::init(const QString &effectName, const QString &pathToScript QScriptValue effectsObject = m_engine->newQObject(effects, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater); m_engine->globalObject().setProperty("effects", effectsObject, QScriptValue::Undeletable); m_engine->globalObject().setProperty("Effect", m_engine->newQMetaObject(&ScriptedEffect::staticMetaObject)); + m_engine->globalObject().setProperty("KWin", m_engine->newQMetaObject(&WorkspaceWrapper::staticMetaObject)); m_engine->globalObject().setProperty("effect", m_engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater), QScriptValue::Undeletable); m_engine->globalObject().setProperty("AnimationData", m_engine->scriptValueFromQMetaObject()); MetaScripting::registration(m_engine); @@ -176,6 +197,7 @@ bool ScriptedEffect::init(const QString &effectName, const QString &pathToScript m_engine->globalObject().setProperty("displayHeight", displayHeightFunc); // add global Shortcut registerGlobalShortcutFunction(this, m_engine, kwinScriptGlobalShortcut); + registerScreenEdgeFunction(this, m_engine, kwinScriptScreenEdge); QScriptValue ret = m_engine->evaluate(scriptFile.readAll()); @@ -257,6 +279,11 @@ void ScriptedEffect::globalShortcutTriggered() callGlobalShortcutCallback(this, sender()); } +void ScriptedEffect::slotBorderActivated(ElectricBorder edge) +{ + screenEdgeActivated(this, edge); +} + QVariant ScriptedEffect::readConfig(const QString &key, const QVariant defaultValue) { KConfigGroup cg = effects->effectConfig(m_effectName); diff --git a/scripting/scriptedeffect.h b/scripting/scriptedeffect.h index 85306d26e6..b080d75fa7 100644 --- a/scripting/scriptedeffect.h +++ b/scripting/scriptedeffect.h @@ -103,6 +103,7 @@ public: QString activeConfig() const; void setActiveConfig(const QString &name); static ScriptedEffect *create(const QString &effectName, const QString &pathToScript); + virtual ~ScriptedEffect(); /** * Whether another effect has grabbed the @p w with the given @p grabRole. * @param w The window to check @@ -121,6 +122,9 @@ public: const QHash &shortcutCallbacks() const { return m_shortcutCallbacks; } + QHash > &screenEdgeCallbacks() { + return m_screenEdgeCallbacks; + } public Q_SLOTS: void animate(KWin::EffectWindow *w, Attribute a, int ms, KWin::FPx2 to, KWin::FPx2 from = KWin::FPx2(), KWin::AnimationData *data = NULL, QEasingCurve curve = QEasingCurve(), int delay = 0); @@ -134,6 +138,7 @@ Q_SIGNALS: private Q_SLOTS: void signalHandlerException(const QScriptValue &value); void globalShortcutTriggered(); + void slotBorderActivated(ElectricBorder edge); private: ScriptedEffect(); bool init(const QString &effectName, const QString &pathToScript); @@ -141,6 +146,7 @@ private: QString m_effectName; QString m_scriptFile; QHash m_shortcutCallbacks; + QHash > m_screenEdgeCallbacks; }; } diff --git a/scripting/scripting.cpp b/scripting/scripting.cpp index 9761bed753..9df75642ab 100644 --- a/scripting/scripting.cpp +++ b/scripting/scripting.cpp @@ -139,6 +139,11 @@ QScriptValue kwinAssertNotNull(QScriptContext *context, QScriptEngine *engine) return true; } +QScriptValue kwinRegisterScreenEdge(QScriptContext *context, QScriptEngine *engine) +{ + return KWin::registerScreenEdge(context, engine); +} + KWin::AbstractScript::AbstractScript(int id, QString scriptName, QString pluginName, QObject *parent) : QObject(parent) , m_scriptId(id) @@ -150,10 +155,20 @@ KWin::AbstractScript::AbstractScript(int id, QString scriptName, QString pluginN if (m_pluginName.isNull()) { m_pluginName = scriptName; } +#ifdef KWIN_BUILD_SCREENEDGES + connect(KWin::Workspace::self()->screenEdge(), SIGNAL(activated(ElectricBorder)), SLOT(borderActivated(ElectricBorder))); +#endif } KWin::AbstractScript::~AbstractScript() { +#ifdef KWIN_BUILD_SCREENEDGES + for (QHash >::const_iterator it = m_screenEdgeCallbacks.constBegin(); + it != m_screenEdgeCallbacks.constEnd(); + ++it) { + KWin::Workspace::self()->screenEdge()->unreserve(static_cast(it.key())); + } +#endif } KConfigGroup KWin::AbstractScript::config() const @@ -183,6 +198,11 @@ void KWin::AbstractScript::globalShortcutTriggered() callGlobalShortcutCallback(this, sender()); } +void KWin::AbstractScript::borderActivated(KWin::ElectricBorder edge) +{ + screenEdgeActivated(this, edge); +} + void KWin::AbstractScript::installScriptFunctions(QScriptEngine* engine) { // add our print @@ -195,6 +215,8 @@ void KWin::AbstractScript::installScriptFunctions(QScriptEngine* engine) engine->globalObject().setProperty("readConfig", configFunc); // add global Shortcut registerGlobalShortcutFunction(this, engine, kwinScriptGlobalShortcut); + // add screen edge + registerScreenEdgeFunction(this, engine, kwinRegisterScreenEdge); // add assertions QScriptValue assertTrueFunc = engine->newFunction(kwinAssertTrue); engine->globalObject().setProperty("assertTrue", assertTrueFunc); diff --git a/scripting/scripting.h b/scripting/scripting.h index b2b08ac7b3..8d243d6979 100644 --- a/scripting/scripting.h +++ b/scripting/scripting.h @@ -22,6 +22,8 @@ along with this program. If not, see . #ifndef KWIN_SCRIPTING_H #define KWIN_SCRIPTING_H +#include + #include #include #include @@ -62,6 +64,9 @@ public: const QHash &shortcutCallbacks() const { return m_shortcutCallbacks; } + QHash > &screenEdgeCallbacks() { + return m_screenEdgeCallbacks; + } public Q_SLOTS: Q_SCRIPTABLE void stop(); @@ -69,6 +74,7 @@ public Q_SLOTS: private Q_SLOTS: void globalShortcutTriggered(); + void borderActivated(ElectricBorder edge); Q_SIGNALS: Q_SCRIPTABLE void print(const QString &text); @@ -100,6 +106,7 @@ private: bool m_running; WorkspaceWrapper *m_workspace; QHash m_shortcutCallbacks; + QHash > m_screenEdgeCallbacks; }; class Script : public AbstractScript diff --git a/scripting/scriptingutils.h b/scripting/scriptingutils.h index e6c9e0472a..74c145ff67 100644 --- a/scripting/scriptingutils.h +++ b/scripting/scriptingutils.h @@ -21,6 +21,8 @@ along with this program. If not, see . #ifndef KWIN_SCRIPTINGUTILS_H #define KWIN_SCRIPTINGUTILS_H +#include "workspace.h" + #include #include #include @@ -132,6 +134,50 @@ void callGlobalShortcutCallback(T script, QObject *sender) value.call(); }; +template +QScriptValue registerScreenEdge(QScriptContext *context, QScriptEngine *engine) +{ + T script = qobject_cast(context->callee().data().toQObject()); + if (!script) { + return engine->undefinedValue(); + } + if (!validateParameters(context, 2, 2)) { + return engine->undefinedValue(); + } + if (!validateArgumentType(context)) { + return engine->undefinedValue(); + } + if (!context->argument(1).isFunction()) { + context->throwError(QScriptContext::SyntaxError, i18nc("KWin Scripting error thrown due to incorrect argument", + "Second argument to registerScreenEdge needs to be a callback")); + } + + const int edge = context->argument(0).toVariant().toInt(); + QHash >::iterator it = script->screenEdgeCallbacks().find(edge); + if (it == script->screenEdgeCallbacks().end()) { + // not yet registered +#ifdef KWIN_BUILD_SCREENEDGES + KWin::Workspace::self()->screenEdge()->reserve(static_cast(edge)); +#endif + script->screenEdgeCallbacks().insert(edge, QList() << context->argument(1)); + } else { + it->append(context->argument(1)); + } + return engine->newVariant(true); +}; + +template +void screenEdgeActivated(T *script, int edge) +{ + QHash >::iterator it = script->screenEdgeCallbacks().find(edge); + if (it != script->screenEdgeCallbacks().end()) { + foreach (const QScriptValue &value, it.value()) { + QScriptValue callback(value); + callback.call(); + } + } +}; + template QScriptValue scriptingAssert(QScriptContext *context, QScriptEngine *engine, int min, int max, T defaultVal = T()) { @@ -194,6 +240,13 @@ inline void registerGlobalShortcutFunction(QObject *parent, QScriptEngine *engin shortcutFunc.setData(engine->newQObject(parent)); engine->globalObject().setProperty("registerShortcut", shortcutFunc); }; + +inline void registerScreenEdgeFunction(QObject *parent, QScriptEngine *engine, QScriptEngine::FunctionSignature function) +{ + QScriptValue shortcutFunc = engine->newFunction(function); + shortcutFunc.setData(engine->newQObject(parent)); + engine->globalObject().setProperty("registerScreenEdge", shortcutFunc); +}; } // namespace KWin #endif // KWIN_SCRIPTINGUTILS_H diff --git a/scripting/workspace_wrapper.h b/scripting/workspace_wrapper.h index f9f91cbd28..a080d7809e 100644 --- a/scripting/workspace_wrapper.h +++ b/scripting/workspace_wrapper.h @@ -35,6 +35,7 @@ class WorkspaceWrapper : public QObject { Q_OBJECT Q_ENUMS(ClientAreaOption) + Q_ENUMS(ElectricBorder) Q_PROPERTY(int currentDesktop READ currentDesktop WRITE setCurrentDesktop NOTIFY currentDesktopChanged) Q_PROPERTY(KWin::Client *activeClient READ activeClient WRITE setActiveClient NOTIFY clientActivated) // TODO: write and notify? @@ -125,6 +126,18 @@ public: ///< one whole screen, ignore struts ScreenArea }; + enum ElectricBorder { + ElectricTop, + ElectricTopRight, + ElectricRight, + ElectricBottomRight, + ElectricBottom, + ElectricBottomLeft, + ElectricLeft, + ElectricTopLeft, + ELECTRIC_COUNT, + ElectricNone + }; WorkspaceWrapper(QObject* parent = 0); #define GETTERSETTERDEF( rettype, getter, setter ) \