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
icc-effect-5.14.5
Martin Gräßlin 2012-05-10 16:09:36 +02:00
parent 7c8aa660d1
commit c9c4e020e2
9 changed files with 149 additions and 3 deletions

View File

@ -289,8 +289,11 @@ void ScreenEdge::check(const QPoint& pos, Time now)
if (effects && static_cast<EffectsHandlerImpl*>(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);
}
}
}

View File

@ -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

View File

@ -41,6 +41,14 @@
<read></read>
<detaileddescription>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.</detaileddescription>
</memberdef>
<memberdef kind="function">
<type>Q_SCRIPTABLE bool</type>
<definition>bool KWin::Scripting::registerScreenEdge</definition>
<argsstring>(ElectricBorder border, QScriptValue callback)</argsstring>
<name>registerScreenEdge</name>
<read></read>
<detaileddescription>Registers the callback for the screen edge. When the mouse gets pushed against the given edge the callback will be invoked.</detaileddescription>
</memberdef>
<memberdef kind="function">
<type>Q_SCRIPTABLE bool</type>
<definition>bool KWin::Scripting::registerShortcut</definition>

View File

@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "scriptedeffect.h"
#include "meta.h"
#include "scriptingutils.h"
#include "workspace_wrapper.h"
// KDE
#include <KDE/KConfigGroup>
#include <KDE/KDebug>
@ -83,6 +84,11 @@ QScriptValue kwinScriptGlobalShortcut(QScriptContext *context, QScriptEngine *en
return globalShortcut<KWin::ScriptedEffect*>(context, engine);
}
QScriptValue kwinScriptScreenEdge(QScriptContext *context, QScriptEngine *engine)
{
return registerScreenEdge<KWin::ScriptedEffect*>(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<int, QList<QScriptValue> >::const_iterator it = m_screenEdgeCallbacks.constBegin();
it != m_screenEdgeCallbacks.constEnd();
++it) {
KWin::Workspace::self()->screenEdge()->unreserve(static_cast<KWin::ElectricBorder>(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<AnimationData>());
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<KWin::ScriptedEffect*>(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);

View File

@ -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<QAction*, QScriptValue> &shortcutCallbacks() const {
return m_shortcutCallbacks;
}
QHash<int, QList<QScriptValue > > &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<QAction*, QScriptValue> m_shortcutCallbacks;
QHash<int, QList<QScriptValue> > m_screenEdgeCallbacks;
};
}

View File

@ -139,6 +139,11 @@ QScriptValue kwinAssertNotNull(QScriptContext *context, QScriptEngine *engine)
return true;
}
QScriptValue kwinRegisterScreenEdge(QScriptContext *context, QScriptEngine *engine)
{
return KWin::registerScreenEdge<KWin::AbstractScript*>(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<int, QList<QScriptValue> >::const_iterator it = m_screenEdgeCallbacks.constBegin();
it != m_screenEdgeCallbacks.constEnd();
++it) {
KWin::Workspace::self()->screenEdge()->unreserve(static_cast<KWin::ElectricBorder>(it.key()));
}
#endif
}
KConfigGroup KWin::AbstractScript::config() const
@ -183,6 +198,11 @@ void KWin::AbstractScript::globalShortcutTriggered()
callGlobalShortcutCallback<KWin::AbstractScript*>(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);

View File

@ -22,6 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef KWIN_SCRIPTING_H
#define KWIN_SCRIPTING_H
#include <kwinglobals.h>
#include <QtCore/QFile>
#include <QtCore/QHash>
#include <QtCore/QStringList>
@ -62,6 +64,9 @@ public:
const QHash<QAction*, QScriptValue> &shortcutCallbacks() const {
return m_shortcutCallbacks;
}
QHash<int, QList<QScriptValue > > &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<QAction*, QScriptValue> m_shortcutCallbacks;
QHash<int, QList<QScriptValue> > m_screenEdgeCallbacks;
};
class Script : public AbstractScript

View File

@ -21,6 +21,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef KWIN_SCRIPTINGUTILS_H
#define KWIN_SCRIPTINGUTILS_H
#include "workspace.h"
#include <KDE/KAction>
#include <KDE/KActionCollection>
#include <KDE/KDebug>
@ -132,6 +134,50 @@ void callGlobalShortcutCallback(T script, QObject *sender)
value.call();
};
template<class T>
QScriptValue registerScreenEdge(QScriptContext *context, QScriptEngine *engine)
{
T script = qobject_cast<T>(context->callee().data().toQObject());
if (!script) {
return engine->undefinedValue();
}
if (!validateParameters(context, 2, 2)) {
return engine->undefinedValue();
}
if (!validateArgumentType<int>(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<int, QList<QScriptValue> >::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<KWin::ElectricBorder>(edge));
#endif
script->screenEdgeCallbacks().insert(edge, QList<QScriptValue>() << context->argument(1));
} else {
it->append(context->argument(1));
}
return engine->newVariant(true);
};
template<class T>
void screenEdgeActivated(T *script, int edge)
{
QHash<int, QList<QScriptValue> >::iterator it = script->screenEdgeCallbacks().find(edge);
if (it != script->screenEdgeCallbacks().end()) {
foreach (const QScriptValue &value, it.value()) {
QScriptValue callback(value);
callback.call();
}
}
};
template<class T>
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

View File

@ -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 ) \