/***************************************************************************** * luaEngine Lua Engine for Qt * Copyright (C) 2018-2019 Syping * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *****************************************************************************/ #define LUA_LIB #include "LuaEngine.h" #include "LuaEngineRegistry.h" #include #include #include LuaEngine::LuaEngine(QObject *parent, bool loadBaseLibraries) : QObject(parent) { L = luaL_newstate(); if (loadBaseLibraries) luaL_openlibs(L); engineRegistry->registerEngine((void*)L, (void*)this); pushVariant("DeleteInstant", 0); pushVariant("DeleteLater", 1); pushVariant("CLIExecuted", "CLIExecuted"); pushFunction("delete", luaObjectDelete_p); pushFunction("connect", luaTriggerConnect_p); pushFunction("disconnect", luaTriggerDisconnect_p); pushFunction("getObjectParent", luaObjectGetParent_p); pushFunction("setObjectParent", luaObjectSetParent_p); pushFunction("luaEngineQuit", luaEngineQuit_p); pushFunction("luaEngineVersion", luaEngineVersion_p); pushFunction("luaEnginePlatform", luaEnginePlatform_p); } LuaEngine::~LuaEngine() { engineRegistry->unregisterEngine(L); lua_close(L); } lua_State* LuaEngine::luaState() { return L; } void LuaEngine::loadBaseLibraries() { luaL_openlibs(L); } int LuaEngine::luaEngineWriter_p(lua_State *L_p, const void *buffer, size_t size, void *array) { Q_UNUSED(L_p) ((QByteArray*)array)->append(QByteArray(static_cast(buffer), (int)size)); return 0; } int LuaEngine::luaEngineQuit_p(lua_State *L_p) { int argumentCount = getArgumentCount(L_p); if (argumentCount == 1) { bool ok; int retcode = getVariant(L_p, 1).toInt(&ok); if (ok) { QCoreApplication::exit(retcode); return 0; } } QCoreApplication::quit(); return 0; } int LuaEngine::luaEngineVersion_p(lua_State *L_p) { pushVariant(L_p, "0.1"); return 1; } int LuaEngine::luaEnginePlatform_p(lua_State *L_p) { #ifdef Q_OS_ANDROID pushVariant(L_p, "Android"); #elif defined(Q_OS_LINUX) pushVariant(L_p, "Linux"); #elif defined(Q_OS_FREEBSD) pushVariant(L_p, "FreeBSD"); #elif defined(Q_OS_OPENBSD) pushVariant(L_p, "OpenBSD"); #elif defined(Q_OS_MACOS) pushVariant(L_p, "macOS"); #elif defined(Q_OS_DARWIN) pushVariant(L_p, "Darwin"); #elif defined(Q_OS_UNIX) pushVariant(L_p, "Unix"); #elif defined(Q_OS_WIN) pushVariant(L_p, "Windows"); #else pushVariant(L_p, "Unknown"); #endif return 1; } QByteArray LuaEngine::dumpLuaScript() { QByteArray array; lua_lock(L); lua_dump(L, luaEngineWriter_p, (void*)&array, 1); lua_unlock(L); return array; } bool LuaEngine::loadLuaScript(const QByteArray &data) { int result = luaL_loadbuffer(L, data.data(), data.size(), "script"); return (result == 0) ? true : false; } bool LuaEngine::loadLuaScript(QIODevice *device, bool closeDevice) { QByteArray data; if (!device->isOpen()) { if (device->open(QIODevice::ReadOnly)) { data = device->readAll(); if (closeDevice) device->close(); return loadLuaScript(data); } } else { data = device->readAll(); if (closeDevice) device->close(); return loadLuaScript(data); } return false; } bool LuaEngine::executeLuaScript(const QByteArray &data) { if (loadLuaScript(data)) return (lua_pcall(L, 0, LUA_MULTRET, 0) == 0) ? true : false; return false; } bool LuaEngine::executeLuaScript(QIODevice *device, bool closeDevice) { QByteArray data; if (!device->isOpen()) { if (device->open(QIODevice::ReadOnly)) { data = device->readAll(); if (closeDevice) device->close(); return executeLuaScript(data); } } else { data = device->readAll(); if (closeDevice) device->close(); return executeLuaScript(data); } return false; } bool LuaEngine::executeLuaFunction(const char *name, bool requireReturn) { return executeLuaFunction(L, name, requireReturn); } bool LuaEngine::executeLuaFunction(lua_State *L_p, const char *name, bool requireReturn) { int returnCount = (requireReturn) ? LUA_MULTRET : 0; lua_getglobal(L_p, name); return (lua_pcall(L_p, 0, returnCount, 0) == 0) ? true : false; } bool LuaEngine::executeLuaFunction(const char *name, const QVariant &argument, bool requireReturn) { return executeLuaFunction(L, name, argument, requireReturn); } bool LuaEngine::executeLuaFunction(lua_State *L_p, const char *name, const QVariant &argument, bool requireReturn) { int returnCount = (requireReturn) ? LUA_MULTRET : 0; lua_getglobal(L_p, name); pushVariant(L_p, argument); return (lua_pcall(L_p, 1, returnCount, 0) == 0) ? true : false; } bool LuaEngine::executeLuaFunction(const char *name, const QVariantList &args, bool requireReturn) { return executeLuaFunction(L, name, args, requireReturn); } bool LuaEngine::executeLuaFunction(lua_State *L_p, const char *name, const QVariantList &args, bool requireReturn) { int returnCount = (requireReturn) ? LUA_MULTRET : 0; lua_getglobal(L_p, name); for (const QVariant &argument : args) { pushVariant(L_p, argument); } return (lua_pcall(L_p, args.count(), returnCount, 0) == 0) ? true : false; } void LuaEngine::pushFunction(const char *name, lua_CFunction function) { pushFunction(L, name, function); } void LuaEngine::pushFunction(lua_State *L_p, const char *name, lua_CFunction function) { lua_pushcfunction(L_p, function); lua_setglobal(L_p, name); } void LuaEngine::pushPointer(const char *name, void *pointer) { pushPointer(L, name, pointer); } void LuaEngine::pushPointer(lua_State *L_p, const char *name, void *pointer) { pushPointer(L_p, pointer); lua_setglobal(L_p, name); } void LuaEngine::pushPointer(void *pointer) { pushPointer(L, pointer); } void LuaEngine::pushPointer(lua_State *L_p, void *pointer) { lua_pushlightuserdata(L_p, pointer); } void LuaEngine::pushVariant(const char *name, const QVariant &variant) { pushVariant(L, name, variant); } void LuaEngine::pushVariant(lua_State *L_p, const char *name, const QVariant &variant) { pushVariant(L_p, variant); lua_setglobal(L_p, name); } void LuaEngine::pushVariant(const QVariant &variant) { pushVariant(L, variant); } void LuaEngine::pushVariant(lua_State *L_p, const QVariant &variant) { if (variant.type() == QVariant::Bool) { lua_pushboolean(L_p, (int)variant.toBool()); } else if (variant.type() == QVariant::Int) { lua_pushinteger(L_p, variant.toInt()); } else if (variant.type() == QVariant::Double) { lua_pushnumber(L_p, variant.toDouble()); } else if (variant.type() == QVariant::String) { lua_pushstring(L_p, variant.toString().toUtf8().data()); } else if (variant.type() == QVariant::StringList) { QStringList stringList = variant.toStringList(); lua_createtable(L_p, 0, stringList.count()); int currentId = 1; for (const QString &string : stringList) { lua_pushinteger(L_p, currentId); lua_pushstring(L_p, string.toUtf8().data()); lua_settable(L_p, -3); currentId++; } } else if ((QMetaType::Type)variant.type() == QMetaType::QVariantList) { QVariantList variantList = variant.toList(); lua_createtable(L_p, 0, variantList.count()); int currentId = 1; for (const QVariant &variant : variantList) { lua_pushinteger(L_p, currentId); pushVariant(L_p, variant); lua_settable(L_p, -3); currentId++; } } else if ((QMetaType::Type)variant.type() == QMetaType::QVariantMap) { QVariantMap variantMap = variant.toMap(); lua_createtable(L_p, 0, variantMap.count()); QVariantMap::const_iterator it = variantMap.constBegin(); QVariantMap::const_iterator end = variantMap.constEnd(); while (it != end) { lua_pushstring(L_p, it.key().toUtf8().data()); pushVariant(L_p, it.value()); lua_settable(L_p, -3); it++; } } else if ((QMetaType::Type)variant.type() == QMetaType::Void || (QMetaType::Type)variant.type() == QMetaType::VoidStar) { lua_pushlightuserdata(L_p, variant.value()); } else { lua_pushnil(L_p); } } QVariant LuaEngine::getVariant(const char *name) { lua_getglobal(L, name); return returnVariant(); } QVariant LuaEngine::getVariant(lua_State *L_p, const char *name) { lua_getglobal(L_p, name); return returnVariant(L_p); } QVariant LuaEngine::getVariant(int index) { return getVariant(L, index); } QVariant LuaEngine::getVariant(lua_State *L_p, int index) { if (lua_isboolean(L_p, index)) { return QVariant::fromValue((bool)lua_toboolean(L_p, index)); } else if (lua_isinteger(L_p, index)) { return QVariant::fromValue(lua_tointeger(L_p, index)); } else if (lua_isnumber(L_p, index)) { return QVariant::fromValue(lua_tonumber(L_p, index)); } else if (lua_isstring(L_p, index)) { return QVariant::fromValue(QString(lua_tostring(L_p, index))); } else if (lua_istable(L_p, index)) { QVariantMap variantMap; lua_pushvalue(L_p, index); lua_pushnil(L_p); while (lua_next(L_p, -2) != 0) { lua_pushvalue(L_p, -2); QString key = QString(lua_tostring(L_p, -1)); QVariant value = getVariant(L_p, -2); variantMap.insert(key, value); lua_pop(L_p, 2); } lua_pop(L_p, 1); return QVariant::fromValue(variantMap); } else if (lua_isuserdata(L_p, index)) { return QVariant::fromValue(lua_touserdata(L_p, index)); } else if (lua_isnoneornil(L_p, index)) { return QVariant(); } QTextStream(stderr) << "Warning: Didn't catch lua_isnoneornil before empty QVariant got returned" << endl; return QVariant(); } void* LuaEngine::returnPointer() { return returnPointer(L); } void* LuaEngine::returnPointer(lua_State *L_p) { return getPointer(L_p, -1); } void* LuaEngine::getPointer(const char *name) { lua_getglobal(L, name); return returnPointer(); } void* LuaEngine::getPointer(lua_State *L_p, const char *name) { lua_getglobal(L_p, name); return returnPointer(L_p); } void* LuaEngine::getPointer(int index) { return getPointer(L, index); } void* LuaEngine::getPointer(lua_State *L_p, int index) { return lua_touserdata(L_p, index); } QVariant LuaEngine::returnVariant() { return returnVariant(L); } QVariant LuaEngine::returnVariant(lua_State *L_p) { return getVariant(L_p, -1); } QVariantList LuaEngine::getArguments(lua_State *L_p) { QVariantList arguments; int argumentCount = getArgumentCount(L_p); for (int i = 1; i < (argumentCount + 1); i++) { arguments << getVariant(L_p, i); } return arguments; } int LuaEngine::getArgumentCount(lua_State *L_p) { return lua_gettop(L_p); } int LuaEngine::luaObjectDelete_p(lua_State *L_p) { if (getArgumentCount(L_p) >= 1) { void *pointer = getPointer(L_p, 1); if (pointer != NULL) { switch (getVariant(L_p, 2).toInt()) { case 1: ((QObject*)pointer)->deleteLater(); break; default: delete ((QObject*)pointer); } } } return 0; } int LuaEngine::luaObjectGetParent_p(lua_State *L_p) { if (getArgumentCount(L_p) >= 1) { void *pointer = getPointer(L_p, 1); if (pointer != NULL) { pushPointer(L_p, ((QObject*)pointer)->parent()); return 1; } } return 0; } int LuaEngine::luaObjectSetParent_p(lua_State *L_p) { if (getArgumentCount(L_p) >= 2) { void *o_pointer = getPointer(L_p, 1); void *p_pointer = getPointer(L_p, 2); if (o_pointer != NULL && p_pointer != NULL && ((QObject*)o_pointer)->inherits("QObject") && ((QObject*)p_pointer)->inherits("QObject")) { ((QObject*)o_pointer)->setParent((QObject*)p_pointer); } } return 0; } int LuaEngine::luaTriggerConnect_p(lua_State *L_p) { if (getArgumentCount(L_p) >= 3) { void *pointer = getPointer(L_p, 1); if (pointer != NULL) { QObject *object = (QObject*)pointer; QString signalString = getVariant(L_p, 2).toString(); int signalIndex = object->metaObject()->indexOfSignal(signalString.toUtf8().data()); if (signalIndex != -1) { LuaEngine *engine = (LuaEngine*)engineRegistry->getEngine(L_p); int slotIndex = engine->metaObject()->indexOfSlot("luaTriggerSlot_p()"); if (slotIndex != -1) { QMetaMethod signal = object->metaObject()->method(signalIndex); QMetaMethod slot = engine->metaObject()->method(slotIndex); QString funcStorage; QTextStream(&funcStorage) << "__ConnectFunc_" << object << "_" << signal.name(); engine->setProperty(funcStorage.toUtf8().data(), getVariant(L_p, 3)); QObject::connect(object, signal, engine, slot); } } } } return 0; } int LuaEngine::luaTriggerDisconnect_p(lua_State *L_p) { if (getArgumentCount(L_p) >= 2) { void *pointer = getPointer(L_p, 1); if (pointer != NULL) { QObject *object = (QObject*)pointer; QString signalString = getVariant(L_p, 2).toString(); int signalIndex = object->metaObject()->indexOfSignal(signalString.toUtf8().data()); if (signalIndex != -1) { LuaEngine *engine = (LuaEngine*)engineRegistry->getEngine(L_p); int slotIndex = engine->metaObject()->indexOfSlot("luaTriggerSlot_p()"); if (slotIndex != -1) { QMetaMethod signal = object->metaObject()->method(signalIndex); QMetaMethod slot = engine->metaObject()->method(slotIndex); QString funcStorage; QTextStream(&funcStorage) << "__ConnectFunc_" << object << "_" << signal.name(); engine->setProperty(funcStorage.toUtf8().data(), QVariant()); QObject::disconnect(object, signal, engine, slot); } } } } return 0; } void LuaEngine::luaTriggerSlot_p() { QMetaMethod signal = sender()->metaObject()->method(senderSignalIndex()); QString funcStorage; QTextStream(&funcStorage) << "__ConnectFunc_" << sender() << "_" << signal.name(); QString luaConnectFunc = property(funcStorage.toUtf8().data()).toString(); executeLuaFunction(luaConnectFunc.toUtf8().data(), QVariant::fromValue((void*)sender())); }