/******************************************************************** KWin - the KDE window manager This file is part of the KDE project. SPDX-FileCopyrightText: 2012 Martin Gräßlin SPDX-License-Identifier: GPL-2.0-or-later *********************************************************************/ #ifndef KWIN_SCRIPTINGUTILS_H #define KWIN_SCRIPTINGUTILS_H #include "input.h" #include "workspace.h" #include "screenedge.h" #include "scripting_logging.h" #include #include #include #include namespace KWin { /** * Validates that argument at @p index of given @p context is of required type. * Throws a type error in the scripting context if there is a type mismatch. * @param context The scripting context in which the argument type needs to be validated. * @param index The argument index to validate * @returns @c true if the argument is of required type, @c false otherwise */ template bool validateArgumentType(QScriptContext *context, int index) { const bool result = context->argument(index).toVariant().canConvert(); if (!result) { context->throwError(QScriptContext::TypeError, i18nc("KWin Scripting function received incorrect value for an expected type", "%1 is not of required type", context->argument(index).toString())); } return result; } /** * Validates that the argument of @p context is of specified type. * Throws a type error in the scripting context if there is a type mismatch. * @param context The scripting context in which the argument type needs to be validated. * @returns @c true if the argument is of required type, @c false otherwise */ template bool validateArgumentType(QScriptContext *context) { return validateArgumentType(context, 0); } template bool validateArgumentType(QScriptContext *context) { if (!validateArgumentType(context)) { return false; } return validateArgumentType(context, 1); } template bool validateArgumentType(QScriptContext *context) { if (!validateArgumentType(context)) { return false; } return validateArgumentType(context, 2); } template bool validateArgumentType(QScriptContext *context) { if (!validateArgumentType(context)) { return false; } return validateArgumentType(context, 3); } /** * Validates that the argument count of @p context is at least @p min and @p max. * Throws a syntax error in the script context if argument count mismatch. * @param context The ScriptContext for which the argument count needs to be validated * @param min The minimum number of arguments. * @param max The maximum number of arguments * @returns @c true if the argument count is correct, otherwise @c false */ bool validateParameters(QScriptContext *context, int min, int max); template QScriptValue globalShortcut(QScriptContext *context, QScriptEngine *engine) { T script = qobject_cast(context->callee().data().toQObject()); if (!script) { return engine->undefinedValue(); } if (context->argumentCount() != 4) { qCDebug(KWIN_SCRIPTING) << "Incorrect number of arguments! Expected: title, text, keySequence, callback"; return engine->undefinedValue(); } QAction* a = new QAction(script); a->setObjectName(context->argument(0).toString()); a->setText(context->argument(1).toString()); const QKeySequence shortcut = QKeySequence(context->argument(2).toString()); KGlobalAccel::self()->setShortcut(a, QList() << shortcut); script->registerShortcut(a, context->argument(3)); input()->registerShortcut(shortcut, a); return engine->newVariant(true); } template void callGlobalShortcutCallback(T script, QObject *sender) { QAction *a = qobject_cast(sender); if (!a) { return; } QHash::const_iterator it = script->shortcutCallbacks().find(a); if (it == script->shortcutCallbacks().end()) { return; } QScriptValue value(it.value()); QScriptValueList arguments; arguments << value.engine()->newQObject(a); value.call(QScriptValue(), arguments); } 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 ScreenEdges::self()->reserve(static_cast(edge), script, "borderActivated"); script->screenEdgeCallbacks().insert(edge, QList() << context->argument(1)); } else { it->append(context->argument(1)); } return engine->newVariant(true); } template QScriptValue unregisterScreenEdge(QScriptContext *context, QScriptEngine *engine) { T script = qobject_cast(context->callee().data().toQObject()); if (!script) { return engine->undefinedValue(); } if (!validateParameters(context, 1, 1)) { return engine->undefinedValue(); } if (!validateArgumentType(context)) { return engine->undefinedValue(); } const int edge = context->argument(0).toVariant().toInt(); QHash >::iterator it = script->screenEdgeCallbacks().find(edge); if (it == script->screenEdgeCallbacks().end()) { //not previously registered return engine->newVariant(false); } ScreenEdges::self()->unreserve(static_cast(edge), script); script->screenEdgeCallbacks().erase(it); return engine->newVariant(true); } template QScriptValue registerTouchScreenEdge(QScriptContext *context, QScriptEngine *engine) { auto 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 registerTouchScreenEdge needs to be a callback")); } const int edge = context->argument(0).toVariant().toInt(); const auto ret = script->registerTouchScreenCallback(edge, context->argument(1)); return engine->newVariant(ret); } template QScriptValue unregisterTouchScreenEdge(QScriptContext *context, QScriptEngine *engine) { auto script = qobject_cast(context->callee().data().toQObject()); if (!script) { return engine->undefinedValue(); } if (!validateParameters(context, 1, 1)) { return engine->undefinedValue(); } if (!validateArgumentType(context)) { return engine->undefinedValue(); } const int edge = context->argument(0).toVariant().toInt(); const auto ret = script->unregisterTouchScreenCallback(edge); return engine->newVariant(ret); } template QScriptValue registerUserActionsMenu(QScriptContext *context, QScriptEngine *engine) { T script = qobject_cast(context->callee().data().toQObject()); if (!script) { return engine->undefinedValue(); } if (!validateParameters(context, 1, 1)) { return engine->undefinedValue(); } if (!context->argument(0).isFunction()) { context->throwError(QScriptContext::SyntaxError, i18nc("KWin Scripting error thrown due to incorrect argument", "Argument for registerUserActionsMenu needs to be a callback")); return engine->undefinedValue(); } script->registerUseractionsMenuCallback(context->argument(0)); 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()) { if (!validateParameters(context, min, max)) { return engine->undefinedValue(); } switch (context->argumentCount()) { case 1: if (!validateArgumentType(context)) { return engine->undefinedValue(); } break; case 2: if (max == 2) { if (!validateArgumentType(context)) { return engine->undefinedValue(); } } else { if (!validateArgumentType(context)) { return engine->undefinedValue(); } } break; case 3: if (!validateArgumentType(context)) { return engine->undefinedValue(); } break; } if (max == 2) { if (context->argument(0).toVariant().value() != defaultVal) { if (context->argumentCount() == max) { context->throwError(QScriptContext::UnknownError, context->argument(max - 1).toString()); } else { context->throwError(QScriptContext::UnknownError, i18nc("Assertion failed in KWin script with given value", "Assertion failed: %1", context->argument(0).toString())); } return engine->undefinedValue(); } } else { if (context->argument(0).toVariant().value() != context->argument(1).toVariant().value()) { if (context->argumentCount() == max) { context->throwError(QScriptContext::UnknownError, context->argument(max - 1).toString()); } else { context->throwError(QScriptContext::UnknownError, i18nc("Assertion failed in KWin script with expected value and actual value", "Assertion failed: Expected %1, got %2", context->argument(0).toString(), context->argument(1).toString())); } return engine->undefinedValue(); } } return engine->newVariant(true); } inline void registerGlobalShortcutFunction(QObject *parent, QScriptEngine *engine, QScriptEngine::FunctionSignature function) { QScriptValue shortcutFunc = engine->newFunction(function); shortcutFunc.setData(engine->newQObject(parent)); engine->globalObject().setProperty(QStringLiteral("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(QStringLiteral("registerScreenEdge"), shortcutFunc); } inline void unregisterScreenEdgeFunction(QObject *parent, QScriptEngine *engine, QScriptEngine::FunctionSignature function) { QScriptValue shortcutFunc = engine->newFunction(function); shortcutFunc.setData(engine->newQObject(parent)); engine->globalObject().setProperty(QStringLiteral("unregisterScreenEdge"), shortcutFunc); } inline void registerTouchScreenEdgeFunction(QObject *parent, QScriptEngine *engine, QScriptEngine::FunctionSignature function) { QScriptValue touchScreenFunc = engine->newFunction(function); touchScreenFunc.setData(engine->newQObject(parent)); engine->globalObject().setProperty(QStringLiteral("registerTouchScreenEdge"), touchScreenFunc); } inline void unregisterTouchScreenEdgeFunction(QObject *parent, QScriptEngine *engine, QScriptEngine::FunctionSignature function) { QScriptValue touchScreenFunc = engine->newFunction(function); touchScreenFunc.setData(engine->newQObject(parent)); engine->globalObject().setProperty(QStringLiteral("unregisterTouchScreenEdge"), touchScreenFunc); } inline void registerUserActionsMenuFunction(QObject *parent, QScriptEngine *engine, QScriptEngine::FunctionSignature function) { QScriptValue shortcutFunc = engine->newFunction(function); shortcutFunc.setData(engine->newQObject(parent)); engine->globalObject().setProperty(QStringLiteral("registerUserActionsMenu"), shortcutFunc); } } // namespace KWin #endif // KWIN_SCRIPTINGUTILS_H