// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "../luaengine.h" #include "../luaqttypes.h" #include #include "utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Utils; using namespace std::string_view_literals; namespace Lua::Internal { void setupUtilsModule() { registerProvider( "Utils", [futureSync = FutureSynchronizer()](sol::state_view lua) mutable -> sol::object { const ScriptPluginSpec *pluginSpec = lua.get("PluginSpec"sv); auto async = lua.script("return require('async')", "_utils_").get(); sol::table utils = lua.create_table(); utils.set_function( "waitms_cb", [guard = pluginSpec->connectionGuard.get()](int ms, const sol::function &cb) { QTimer::singleShot(ms, guard, [cb]() { cb(); }); }); auto dirEntries_cb = [&futureSync, guard = pluginSpec->connectionGuard.get()]( const FilePath &p, const sol::table &options, const sol::function &cb) { const QStringList nameFilters = options.get_or("nameFilters"sv, {}); QDir::Filters fileFilters = (QDir::Filters) options.get_or("fileFilters"sv, QDir::NoFilter); QDirIterator::IteratorFlags flags = (QDirIterator::IteratorFlags) options.get_or("flags"sv, QDirIterator::NoIteratorFlags); FileFilter filter(nameFilters, fileFilters, flags); QFuture future = asyncRun([p, filter](QPromise &promise) { p.iterateDirectory( [&promise](const FilePath &item) { if (promise.isCanceled()) return IterationPolicy::Stop; promise.addResult(item); return IterationPolicy::Continue; }, filter); }); futureSync.addFuture(future); onFinished(future, guard, [cb](const QFuture &future) { cb(future.results()); }); }; auto searchInPath_cb = [&futureSync, guard = pluginSpec->connectionGuard .get()](const FilePath &p, const sol::function &cb) { QFuture future = asyncRun( [p](QPromise &promise) { promise.addResult(p.searchInPath()); }); futureSync.addFuture(future); onFinished(future, guard, [cb](const QFuture &future) { cb(future.result()); }); }; utils.set_function("__dirEntries_cb__", dirEntries_cb); utils.set_function("__searchInPath_cb__", searchInPath_cb); utils.set_function("createUuid", []() { return QUuid::createUuid().toString(); }); utils.set_function("getNativeShortcut", [](QString shortcut) { return QKeySequence::fromString(shortcut).toString(QKeySequence::NativeText); }); sol::function wrap = async["wrap"].get(); utils["waitms"] = wrap(utils["waitms_cb"]); utils["pid"] = QCoreApplication::applicationPid(); utils.new_usertype( "Id", sol::no_constructor); struct LuaHostOsInfo { static bool isWindowsHost() { return HostOsInfo::isWindowsHost(); } static bool isMacHost() { return HostOsInfo::isMacHost(); } static bool isLinuxHost() { return HostOsInfo::isLinuxHost(); } static OsArch hostArchitecture() { return HostOsInfo::hostArchitecture(); } }; auto hostOsInfoType = utils.new_usertype("HostOsInfo"); hostOsInfoType["isWindowsHost"] = &LuaHostOsInfo::isWindowsHost; hostOsInfoType["isMacHost"] = &LuaHostOsInfo::isMacHost; hostOsInfoType["isLinuxHost"] = &LuaHostOsInfo::isLinuxHost; hostOsInfoType["os"] = sol::var([]() { if (LuaHostOsInfo::isMacHost()) return "mac"; else if (LuaHostOsInfo::isLinuxHost()) return "linux"; else if (LuaHostOsInfo::isWindowsHost()) return "windows"; else return "unknown"; }()); hostOsInfoType["architecture"] = sol::var([]() { switch (LuaHostOsInfo::hostArchitecture()) { case OsArchUnknown: return "unknown"; case OsArchX86: return "x86"; case OsArchAMD64: return "x86_64"; case OsArchItanium: return "itanium"; case OsArchArm: return "arm"; case OsArchArm64: return "arm64"; default: return "unknown"; } }()); sol::usertype filePathType = utils.new_usertype( "FilePath", sol::call_constructor, sol::constructors(), sol::meta_function::to_string, &FilePath::toUserOutput, "fromUserInput", &FilePath::fromUserInput, "exists", &FilePath::exists, "resolveSymlinks", &FilePath::resolveSymlinks, "isExecutableFile", &FilePath::isExecutableFile, "isDir", &FilePath::isDir, "nativePath", &FilePath::nativePath, "toUserOutput", &FilePath::toUserOutput, "fileName", &FilePath::fileName, "currentWorkingPath", &FilePath::currentWorkingPath, "parentDir", &FilePath::parentDir, "suffix", &FilePath::suffix, "completeSuffix", &FilePath::completeSuffix, "isAbsolutePath", &FilePath::isAbsolutePath, "resolvePath", sol::overload( [](const FilePath &p, const QString &path) { return p.resolvePath(path); }, [](const FilePath &p, const FilePath &path) { return p.resolvePath(path); }), "permissions", [](FilePath& p) { return static_cast(p.permissions().toInt()); }, "setPermissions", [](FilePath& p, QFileDevice::Permission permissions) { p.setPermissions(static_cast(permissions)); }); utils["FilePath"]["dirEntries_cb"] = utils["__dirEntries_cb__"]; utils["FilePath"]["dirEntries"] = wrap(utils["__dirEntries_cb__"]); utils["FilePath"]["searchInPath_cb"] = utils["__searchInPath_cb__"]; utils["FilePath"]["searchInPath"] = wrap(utils["__searchInPath_cb__"]); utils["standardLocations"] = [](QStandardPaths::StandardLocation location) { const auto locationsStrings = QStandardPaths::standardLocations( static_cast(location)); FilePaths locationsPaths; std::transform(locationsStrings.constBegin(), locationsStrings.constEnd(), std::back_inserter(locationsPaths), &FilePath::fromString); return locationsPaths; }; utils["standardLocation"] = [](QStandardPaths::StandardLocation location) -> sol::optional { const auto paths = QStandardPaths::standardLocations( static_cast(location)); if (paths.isEmpty()) return sol::nullopt; return FilePath::fromString(paths.first()); }; utils["writableLocation"] = [](QStandardPaths::StandardLocation location) -> sol::optional { const auto path = QStandardPaths::writableLocation( static_cast(location)); if (path.isEmpty()) return sol::nullopt; return FilePath::fromString(path); }; utils.new_usertype( "CommandLine", sol::call_constructor, sol::constructors(), "executable", sol::property(&CommandLine::executable, &CommandLine::setExecutable), "arguments", sol::property(&CommandLine::arguments, &CommandLine::setArguments), "addArgument", [](CommandLine &cmd, const QString &arg) { cmd.addArg(arg); }, sol::meta_function::to_string, &CommandLine::toUserOutput); auto procRunDataType = utils.new_usertype( "ProcessRunData", sol::call_constructor, sol::constructors(), "commandLine", sol::property( [](const ProcessRunData &prd) { return prd.command; }, [](ProcessRunData &prd, const CommandLine &cmd) { prd.command = cmd; }), "workingDirectory", sol::property( [](const ProcessRunData &prd) { return prd.workingDirectory; }, [](ProcessRunData &prd, const FilePath &wd) { prd.workingDirectory = wd; }), "environment", sol::property( [](const ProcessRunData &prd) { return prd.environment; }, [](ProcessRunData &prd, const Environment &env) { prd.environment = env; }), sol::meta_function::to_string, [](const ProcessRunData &prd) { return QString("ProcessRunData{\n command=%1,\n workingDirectory=%2,\n " "environment={\n %3\n}\n}") .arg(prd.command.toUserOutput()) .arg(prd.workingDirectory.toUrlishString()) .arg(prd.environment.toStringList().join(",\n ")); }); utils.new_usertype( "Timer", "create", [guard = pluginSpec](int timeout, bool singleShort, sol::main_function callback) -> std::unique_ptr { auto timer = std::make_unique(); timer->setInterval(timeout); timer->setSingleShot(singleShort); QObject::connect( timer.get(), &QTimer::timeout, guard->connectionGuard.get(), [callback] { void_safe_call(callback); }); return timer; }, "start", [](QTimer *timer) { timer->start(); }, "stop", [](QTimer *timer) { timer->stop(); }); utils["openExternalUrl"] = [](const QString &url) { QDesktopServices::openUrl(QUrl::fromEncoded(url.toUtf8())); }; utils["stringToBase64Url"] = [](const QString &data) { return QString::fromLatin1(data.toUtf8().toBase64(QByteArray::Base64UrlEncoding)); }; utils["base64UrlToString"] = [](const char *data) { return QString::fromUtf8( QByteArray::fromBase64(data, QByteArray::Base64UrlEncoding)); }; utils["stringToBase64"] = [](const QString &data) { return QString::fromLatin1(data.toUtf8().toBase64()); }; utils["base64ToString"] = [](const char *data) { return QString::fromUtf8(QByteArray::fromBase64(data)); }; utils.new_enum( "IconStyleOption", {{"None", Icon::None}, {"Tint", Icon::Tint}, {"DropShadow", Icon::DropShadow}, {"PunchEdges", Icon::PunchEdges}, {"ToolBarStyle", Icon::ToolBarStyle}, {"MenuTintedStyle", Icon::MenuTintedStyle}}); mirrorEnum(utils, QMetaEnum::fromType(), "ThemeColor"); auto iconType = utils.new_usertype( "Icon", "create", sol::factories( [](FilePathOrString path) { return std::make_shared(toFilePath(path)); }, [](const sol::table &maskAndColor, Icon::IconStyleOption style) { QList args; for (const auto &it : maskAndColor) { auto pair = it.second.as(); args.append(qMakePair( toFilePath(pair.get(1)), pair.get(2))); } return std::make_shared(args, Icon::IconStyleOptions::fromInt(style)); })); return utils; }); } InfoBarCleaner::~InfoBarCleaner() { for (const auto &id : std::as_const(openInfoBars)) Core::ICore::infoBar()->removeInfo(id); } } // namespace Lua::Internal