#include "scriptadapter.h" #include "optionsitem.h" #include #include #include #include #include #include #include #include template static void scriptToEnum(const QScriptValue &obj, Enum &e) { e = static_cast(obj.toInt32()); } template static QScriptValue enumToScript(QScriptEngine *engine, const Enum &e) { QScriptValue obj = engine->newVariant(e); return obj; } // Adds a property 'name' to 'to' that contains all the values of the enum 'name', // which must be registered in 'mo'. Also registers the type 'Enum' to the QScriptEngine. template static void exposeEnum(QScriptEngine *engine, QScriptValue *to, const char *name, const QMetaObject *mo) { qScriptRegisterMetaType(engine, &enumToScript, &scriptToEnum); QScriptValue enumvals = engine->newObject(); int enumIndex = mo->indexOfEnumerator(name); Q_ASSERT(enumIndex != -1 && "enum not found in meta object"); QMetaEnum metaEnum = mo->enumerator(enumIndex); for (int i = 0; i < metaEnum.keyCount(); ++i) enumvals.setProperty(metaEnum.key(i), metaEnum.value(i)); to->setProperty(name, enumvals); } ScriptAdapter::ScriptAdapter(QObject *parent) : QObject(parent) { mScriptRunner = new QTimer(this); mScriptRunner->setSingleShot(true); mScriptRunner->setInterval(100); connect(mScriptRunner, SIGNAL(timeout()), this, SLOT(continueScripts())); mScriptRunner->start(); mMessageBox = new QMessageBox; } ScriptAdapter::~ScriptAdapter() { delete mMessageBox; } void ScriptAdapter::addScriptInterface(const QString &name, QObject *interface) { mScriptInterfaces.insert(name, interface); } ScriptFiber *ScriptAdapter::script(int index) { if (index >= 0 && index < mActiveScripts.length()) return mActiveScripts[index]; else return 0; } void ScriptAdapter::runAutostartScripts() { QDir dir(QCoreApplication::applicationDirPath()); dir.cd(QLatin1String("scripts")); dir.cd(QLatin1String("autostart")); QStringList scriptPatterns; scriptPatterns << "*.js" << "*.qs"; foreach (const QFileInfo &file, dir.entryInfoList(scriptPatterns, QDir::Files)) { run(file.filePath()); } } ScriptFiber *ScriptAdapter::run(const QString &filePath) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "Could not read script file: " << filePath; return 0; } QFileInfo fileInfo(file); ScriptFiber *script = new ScriptFiber(fileInfo.baseName(), file.readAll(), this); mActiveScripts += script; emit scriptStart(script); return script; } void ScriptAdapter::continueScripts() { for (int i = 0; i < mActiveScripts.length(); ++i) { ScriptFiber *script = mActiveScripts[i]; if (script->status() == Fiber::Terminated) { emit scriptStop(i); mActiveScripts.removeOne(script); delete script; break; } if (!script->isPaused() || script->isTerminating()) { script->cont(); } } mScriptRunner->start(); } void ScriptAdapter::yield(int ms) { ScriptFiber *scriptFiber = dynamic_cast(Fiber::currentFiber()); if (!scriptFiber) return; QTime timer; timer.start(); while (timer.elapsed() < ms && !scriptFiber->isTerminating()) { scriptFiber->suspend(); } } void ScriptAdapter::messageBox(const QString &text) { mMessageBox->setText(text); // message boxes should block ScriptFiber *fiber = dynamic_cast(Fiber::currentFiber()); fiber->beginBlocking(); mMessageBox->exec(); fiber->endBlocking(); } ScriptFiber::ScriptFiber(const QString &name, const QString &scriptCode, ScriptAdapter *adapter) : Fiber(524228) // FIXME: 131072 was not big enough... , mName(name) , mScriptCode(scriptCode) , mAdapter(adapter) , mPaused(false) , mTerminate(false) { mStopTimer.setInterval(10); connect(&mStopTimer, SIGNAL(timeout()), this, SLOT(suspend())); // trigger event processing during script evaluation mEngine.setProcessEventsInterval(10); // inject the ScriptAdapters functions into the global scope QScriptValue adapterValue = mEngine.newQObject(adapter); adapterValue.setPrototype(mEngine.globalObject()); mEngine.setGlobalObject(adapterValue); QHashIterator iter(mAdapter->mScriptInterfaces); while (iter.hasNext()) { iter.next(); QScriptValue interface = mEngine.newQObject(iter.value()); mEngine.globalObject().setProperty(iter.key(), interface); } } void ScriptFiber::run() { mStopTimer.start(); QScriptValue value = mEngine.evaluate(mScriptCode); if (value.isError()) { qWarning() << "Script execution resulted in an error:" << value.toString(); } mStopTimer.stop(); } void ScriptFiber::requestTerminate() { mTerminate = true; } void ScriptFiber::togglePause() { mPaused = !mPaused; } void ScriptFiber::suspend() { if (Fiber::currentFiber() == this) { Fiber::yield(); if (mTerminate) { mEngine.abortEvaluation(mEngine.currentContext()->throwError("Aborted")); mStopTimer.stop(); } } } void ScriptFiber::beginBlocking() { mStopTimer.stop(); } void ScriptFiber::endBlocking() { mStopTimer.start(); }