From f97e40f127441828eb4ffe11841ebf79191ac8a3 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 22 Jul 2020 22:35:40 -0400 Subject: moved initLogging() to loglist.cpp --- src/loglist.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'src/loglist.cpp') diff --git a/src/loglist.cpp b/src/loglist.cpp index a4868ed4..7a64ecad 100644 --- a/src/loglist.cpp +++ b/src/loglist.cpp @@ -262,3 +262,68 @@ void LogList::onContextMenu(const QPoint& pos) auto* menu = createMenu(this); menu->popup(viewport()->mapToGlobal(pos)); } + + +log::Levels convertQtLevel(QtMsgType t) +{ + switch (t) + { + case QtDebugMsg: + return log::Debug; + + case QtWarningMsg: + return log::Warning; + + case QtCriticalMsg: // fall-through + case QtFatalMsg: + return log::Error; + + case QtInfoMsg: // fall-through + default: + return log::Info; + } +} + +void qtLogCallback( + QtMsgType type, const QMessageLogContext& context, const QString& message) +{ + std::string_view file = ""; + + if (type != QtDebugMsg) { + if (context.file) { + file = context.file; + + const auto lastSep = file.find_last_of("/\\"); + if (lastSep != std::string_view::npos) { + file = {context.file + lastSep + 1}; + } + } + } + + if (file.empty()) { + log::log( + convertQtLevel(type), "{}", + message.toStdString()); + } else { + log::log( + convertQtLevel(type), "[{}:{}] {}", + file, context.line, message.toStdString()); + } +} + +void initLogging() +{ + LogModel::create(); + + log::LoggerConfiguration conf; + conf.maxLevel = MOBase::log::Debug; + conf.pattern = "%^[%Y-%m-%d %H:%M:%S.%e %L] %v%$"; + conf.utc = true; + + log::createDefault(conf); + + log::getDefault().setCallback( + [](log::Entry e){ LogModel::instance().add(e); }); + + qInstallMessageHandler(qtLogCallback); +} -- cgit v1.3.1 From 9435202034cafb05ffc11aed48ff57536bce73f7 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 22 Jul 2020 23:27:01 -0400 Subject: removed flags from SingleInstance because there's only one left refactoring in main.cpp: - moved stuff to loglist.cpp and moapplication.cpp - split main() into a few functions --- src/env.cpp | 13 ++- src/env.h | 3 +- src/loglist.cpp | 26 ++++++ src/loglist.h | 1 + src/main.cpp | 225 ++++++++++++++++++++++++------------------------- src/moapplication.cpp | 19 ++--- src/moapplication.h | 20 ++--- src/singleinstance.cpp | 4 +- src/singleinstance.h | 18 +--- src/spawn.cpp | 2 +- 10 files changed, 174 insertions(+), 157 deletions(-) (limited to 'src/loglist.cpp') diff --git a/src/env.cpp b/src/env.cpp index bf75c9fa..9f0acbbd 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -400,11 +400,18 @@ QString path() return get("PATH"); } -QString addPath(const QString& s) +QString appendToPath(const QString& s) { auto old = path(); - set("PATH", get("PATH") + ";" + s); - return old; + set("PATH", old + ";" + s); + return old; +} + +QString prependToPath(const QString& s) +{ + auto old = path(); + set("PATH", s + ";" + old); + return old; } QString setPath(const QString& s) diff --git a/src/env.h b/src/env.h index 9bec1713..a563f2c3 100644 --- a/src/env.h +++ b/src/env.h @@ -236,7 +236,8 @@ QString get(const QString& name); QString set(const QString& name, const QString& value); QString path(); -QString addPath(const QString& s); +QString appendToPath(const QString& s); +QString prependToPath(const QString& s); QString setPath(const QString& s); diff --git a/src/loglist.cpp b/src/loglist.cpp index 7a64ecad..167b61ef 100644 --- a/src/loglist.cpp +++ b/src/loglist.cpp @@ -327,3 +327,29 @@ void initLogging() qInstallMessageHandler(qtLogCallback); } + +bool createAndMakeWritable(const std::wstring &subPath) { + QString const dataPath = qApp->property("dataPath").toString(); + QString fullPath = dataPath + "/" + QString::fromStdWString(subPath); + + if (!QDir(fullPath).exists() && !QDir().mkdir(fullPath)) { + QMessageBox::critical(nullptr, QObject::tr("Error"), + QObject::tr("Failed to create \"%1\". Your user " + "account probably lacks permission.") + .arg(fullPath)); + return false; + } else { + return true; + } +} + +bool setLogDirectory(const QString& dir) +{ + const auto logFile = dir + "/logs/mo_interface.log"; + + if (!createAndMakeWritable(AppConfig::logPath())) { + return false; + } + + log::getDefault().setFile(MOBase::log::File::single(logFile.toStdWString())); +} diff --git a/src/loglist.h b/src/loglist.h index 7387eb50..0745ed3e 100644 --- a/src/loglist.h +++ b/src/loglist.h @@ -84,5 +84,6 @@ private: void initLogging(); +bool setLogDirectory(const QString& dir); #endif // LOGBUFFER_H diff --git a/src/main.cpp b/src/main.cpp index e52b252b..864d26a1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,7 +38,13 @@ along with Mod Organizer. If not, see . #include #include -#pragma comment(linker, "/manifestDependency:\"name='dlls' processorArchitecture='x86' version='1.0.0.0' type='win32' \"") +// see addDllsToPath() below +#pragma comment(linker, "/manifestDependency:\"" \ + "name='dlls' " \ + "processorArchitecture='x86' " \ + "version='1.0.0.0' " \ + "type='win32' \"") + using namespace MOBase; using namespace MOShared; @@ -47,21 +53,6 @@ void sanityChecks(const env::Environment& env); int checkIncompatibleModule(const env::Module& m); int checkPathsForSanity(MOBase::IPluginGame& game, const Settings& s); -bool createAndMakeWritable(const std::wstring &subPath) { - QString const dataPath = qApp->property("dataPath").toString(); - QString fullPath = dataPath + "/" + QString::fromStdWString(subPath); - - if (!QDir(fullPath).exists() && !QDir().mkdir(fullPath)) { - QMessageBox::critical(nullptr, QObject::tr("Error"), - QObject::tr("Failed to create \"%1\". Your user " - "account probably lacks permission.") - .arg(fullPath)); - return false; - } else { - return true; - } -} - void purgeOldFiles() { // remove the temporary backup directory in case we're restarting after an @@ -305,36 +296,47 @@ MOBase::IPluginGame *determineCurrentGame( } -// extend path to include dll directory so plugins don't need a manifest -// (using AddDllDirectory would be an alternative to this but it seems fairly -// complicated esp. -// since it isn't easily accessible on Windows < 8 -// SetDllDirectory replaces other search directories and this seems to -// propagate to child processes) -void setupPath() +// This adds the `dlls` directory to the path so the dlls can be found. How +// MO is able to find dlls in there is a bit convoluted: +// +// Dependencies on DLLs can be baked into an executable by passing a +// `manifestdependency` option to the linker. This can be done on the command +// line or with a pragma. Typically, the dependency will not be a hardcoded +// filename, but an assembly name, such as Microsoft.Windows.Common-Controls. +// +// When Windows loads the exe, it will look for this assembly in a variety of +// places, such as in the WinSxS folder, but also in the program's folder. It +// will look for `assemblyname.dll` or `assemblyname/assemblyname.dll` and try +// to load that. +// +// If these files don't exist, then the loader gets creative and looks for +// `assemblyname.manifest` and `assemblyname/assemblyname.manifest`. A manifest +// file is just an XML file that can contain a list of DLLs to load for this +// assembly. +// +// In MO's case, there's a `pragma` at the beginning of this file which adds +// `dlls` as an "assembly" dependency. This is a bit of a hack to just force +// the loader to eventually find `dlls/dlls.manifest`, which contains the list +// of all the DLLs MO requires to load. +// +// This file was handwritten in `modorganizer/src/dlls.manifest.qt5` and +// is copied and renamed in CMakeLists.txt into `bin/dlls/dlls.manifest`. Note +// that the useless and incorrect .qt5 extension is removed. +// +void addDllsToPath() { - static const int BUFSIZE = 4096; - - QCoreApplication::setLibraryPaths(QStringList(QCoreApplication::applicationDirPath() + "/dlls") + QCoreApplication::libraryPaths()); + const auto dllsPath = QDir::toNativeSeparators( + QCoreApplication::applicationDirPath() + "/dlls"); - boost::scoped_array oldPath(new TCHAR[BUFSIZE]); - DWORD offset = ::GetEnvironmentVariable(TEXT("PATH"), oldPath.get(), BUFSIZE); - if (offset > BUFSIZE) { - oldPath.reset(new TCHAR[offset]); - ::GetEnvironmentVariable(TEXT("PATH"), oldPath.get(), offset); - } - - std::wstring newPath(ToWString(QDir::toNativeSeparators( - QCoreApplication::applicationDirPath())) + L"\\dlls"); - newPath += L";"; - newPath += oldPath.get(); + QCoreApplication::setLibraryPaths( + QStringList(dllsPath) + QCoreApplication::libraryPaths()); - ::SetEnvironmentVariableW(L"PATH", newPath.c_str()); + env::prependToPath(dllsPath); } int runApplication( MOApplication &application, const cl::CommandLine& cl, - SingleInstance &instance, const QString &splashPath) + SingleInstance &instance, const QString &dataPath) { TimeThis tt("runApplication() to exec()"); @@ -343,7 +345,6 @@ int runApplication( createVersionInfo().displayString(3), GITID, QCoreApplication::applicationDirPath(), MOShared::getUsvfsVersionString()); - const QString dataPath = application.property("dataPath").toString(); log::info("data path: {}", dataPath); if (InstanceManager::isPortablePath(dataPath)) { @@ -429,8 +430,14 @@ int runApplication( checkPathsForSanity(*game, settings); bool useSplash = settings.useSplash(); + QString splashPath; if (useSplash) { + splashPath = dataPath + "/splash.png"; + if (!QFile::exists(dataPath + "/splash.png")) { + splashPath = ":/MO/gui/splash"; + } + if (splashPath.startsWith(':')) { // currently using MO splash, see if the plugin contains one QString pluginSplash @@ -599,111 +606,101 @@ int runApplication( return 1; } -int main(int argc, char *argv[]) +int forwardToPrimary(SingleInstance& instance, const cl::CommandLine& cl) { - cl::CommandLine cl; - - const auto r = cl.run(GetCommandLineW()); - if (r) - return *r; - - TimeThis tt("main to runApplication()"); - - // in loglist.cpp - initLogging(); + if (cl.shortcut().isValid()) { + instance.sendMessage(cl.shortcut().toString()); + } else if (cl.nxmLink()) { + instance.sendMessage(*cl.nxmLink()); + } else { + QMessageBox::information( + nullptr, QObject::tr("Mod Organizer"), + QObject::tr("An instance of Mod Organizer is already running")); + } - //Make sure the configured temp folder exists - QDir tempDir = QDir::temp(); - if (!tempDir.exists()) - tempDir.root().mkpath(tempDir.canonicalPath()); + return 0; +} - //Should allow for better scaling of ui with higher resolution displays - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +void resetForRestart(cl::CommandLine& cl) +{ + LogModel::instance().clear(); + ResetExitFlag(); - MOApplication application(argc, argv); - QStringList arguments = application.arguments(); + // make sure the log file isn't locked in case MO was restarted and + // the previous instance gets deleted + log::getDefault().setFile({}); - SetThisThreadName("main"); + // don't reprocess command line + cl.clear(); +} - setupPath(); +QString determineDataPath(const cl::CommandLine& cl) +{ + try + { + InstanceManager& instanceManager = InstanceManager::instance(); + if (cl.instance()) + instanceManager.overrideInstance(*cl.instance()); - SingleInstance::Flags siFlags = SingleInstance::NoFlags; + return instanceManager.determineDataPath(); + } + catch (const std::exception &e) + { + if (strcmp(e.what(),"Canceled")) { + QMessageBox::critical(nullptr, QObject::tr("Failed to set up instance"), e.what()); + } - if (cl.multiple()) { - arguments.removeAll("--multiple"); - siFlags |= SingleInstance::AllowMultiple; + return {}; } +} - SingleInstance instance(siFlags); - if (instance.ephemeral()) { - if (cl.shortcut().isValid()) { - instance.sendMessage(cl.shortcut().toString()); - return 0; - } else if (cl.nxmLink()) { - instance.sendMessage(*cl.nxmLink()); - return 0; - } else if (arguments.size() == 1) { - QMessageBox::information( - nullptr, QObject::tr("Mod Organizer"), - QObject::tr("An instance of Mod Organizer is already running")); - return 0; - } - } // we continue for the primary instance OR if MO was called with parameters +int main(int argc, char *argv[]) +{ + cl::CommandLine cl; - do { - LogModel::instance().clear(); - ResetExitFlag(); + if (auto r=cl.run(GetCommandLineW())) { + return *r; + } - // make sure the log file isn't locked in case MO was restarted and - // the previous instance gets deleted - log::getDefault().setFile({}); + TimeThis tt("main to runApplication()"); + SetThisThreadName("main"); - QString dataPath; + initLogging(); + auto application = MOApplication::create(argc, argv); + addDllsToPath(); - try { - InstanceManager& instanceManager = InstanceManager::instance(); + SingleInstance instance(cl.multiple()); + if (instance.ephemeral()) { + return forwardToPrimary(instance, cl); + } - if (cl.instance()) - instanceManager.overrideInstance(*cl.instance()); + for (;;) + { + // resets things when MO is "restarted" + resetForRestart(cl); - dataPath = instanceManager.determineDataPath(); - } catch (const std::exception &e) { - if (strcmp(e.what(),"Canceled")) - QMessageBox::critical(nullptr, QObject::tr("Failed to set up instance"), e.what()); + const QString dataPath = determineDataPath(cl); + if (dataPath.isEmpty()) { return 1; } - application.setProperty("dataPath", dataPath); - - // initialize dump collection only after "dataPath" since the crashes are stored under it - setUnhandledExceptionHandler(); - const auto logFile = - qApp->property("dataPath").toString() + "/logs/mo_interface.log"; + application.setProperty("dataPath", dataPath); + setExceptionHandler(); - if (!createAndMakeWritable(AppConfig::logPath())) { + if (!setLogDirectory(dataPath)) { reportError("Failed to create log folder"); InstanceManager::instance().clearCurrentInstance(); return 1; } - log::getDefault().setFile(MOBase::log::File::single(logFile.toStdWString())); - log::debug("command line: '{}'", QString::fromWCharArray(GetCommandLineW())); - QString splash = dataPath + "/splash.png"; - if (!QFile::exists(dataPath + "/splash.png")) { - splash = ":/MO/gui/splash"; - } - tt.stop(); - const int result = runApplication(application, cl, instance, splash); + const int result = runApplication(application, cl, instance, dataPath); if (result != RestartExitCode) { return result; } - - argc = 1; - cl.clear(); - } while (true); + } } diff --git a/src/moapplication.cpp b/src/moapplication.cpp index dd49bf53..d95d544a 100644 --- a/src/moapplication.cpp +++ b/src/moapplication.cpp @@ -24,16 +24,10 @@ along with Mod Organizer. If not, see . #include "shared/appconfig.h" #include #include -#if QT_VERSION < QT_VERSION_CHECK(5,0,0) -#include -#include -#endif #include #include #include #include - - #include @@ -77,7 +71,7 @@ public: }; -MOApplication::MOApplication(int &argc, char **argv) +MOApplication::MOApplication(int argc, char** argv) : QApplication(argc, argv) { connect(&m_StyleWatcher, &QFileSystemWatcher::fileChanged, [&](auto&& file){ @@ -89,8 +83,13 @@ MOApplication::MOApplication(int &argc, char **argv) setStyle(new ProxyStyle(style())); } +MOApplication MOApplication::create(int argc, char** argv) +{ + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + return MOApplication(argc, argv); +} -bool MOApplication::setStyleFile(const QString &styleName) +bool MOApplication::setStyleFile(const QString& styleName) { // remove all files from watch QStringList currentWatch = m_StyleWatcher.files(); @@ -114,7 +113,7 @@ bool MOApplication::setStyleFile(const QString &styleName) } -bool MOApplication::notify(QObject *receiver, QEvent *event) +bool MOApplication::notify(QObject* receiver, QEvent* event) { try { return QApplication::notify(receiver, event); @@ -134,7 +133,7 @@ bool MOApplication::notify(QObject *receiver, QEvent *event) } -void MOApplication::updateStyle(const QString &fileName) +void MOApplication::updateStyle(const QString& fileName) { if (QStyleFactory::keys().contains(fileName)) { setStyle(QStyleFactory::create(fileName)); diff --git a/src/moapplication.h b/src/moapplication.h index 9db130af..3ed71fb6 100644 --- a/src/moapplication.h +++ b/src/moapplication.h @@ -24,27 +24,25 @@ along with Mod Organizer. If not, see . #include -class MOApplication : public QApplication { -Q_OBJECT -public: - - MOApplication(int &argc, char **argv); +class MOApplication : public QApplication +{ + Q_OBJECT - virtual bool notify (QObject *receiver, QEvent *event); +public: + static MOApplication create(int argc, char** argv); + virtual bool notify (QObject* receiver, QEvent* event); public slots: - - bool setStyleFile(const QString &style); + bool setStyleFile(const QString& style); private slots: - - void updateStyle(const QString &fileName); + void updateStyle(const QString& fileName); private: - QFileSystemWatcher m_StyleWatcher; QString m_DefaultStyle; + MOApplication(int argc, char** argv); }; diff --git a/src/singleinstance.cpp b/src/singleinstance.cpp index e32c8de1..bd7ccc43 100644 --- a/src/singleinstance.cpp +++ b/src/singleinstance.cpp @@ -28,14 +28,14 @@ static const int s_Timeout = 5000; using MOBase::reportError; -SingleInstance::SingleInstance(Flags flags, QObject *parent) : +SingleInstance::SingleInstance(bool allowMultiple, QObject *parent) : QObject(parent), m_Ephemeral(false), m_OwnsSM(false) { m_SharedMem.setKey(s_Key); if (!m_SharedMem.create(1)) { if (m_SharedMem.error() == QSharedMemory::AlreadyExists) { - if (!flags.testFlag(AllowMultiple)) { + if (!allowMultiple) { m_SharedMem.attach(); m_Ephemeral = true; } diff --git a/src/singleinstance.h b/src/singleinstance.h index d500a056..5f6c3633 100644 --- a/src/singleinstance.h +++ b/src/singleinstance.h @@ -36,19 +36,9 @@ class SingleInstance : public QObject Q_OBJECT public: - enum Flag - { - NoFlags = 0x00, - - // if another instance is running, run this one disconnected from the - // shared memory - AllowMultiple = 0x01 - }; - - using Flags = QFlags; - - - explicit SingleInstance(Flags flags, QObject *parent = 0); + // `allowMultiple`: if another instance is running, run this one + // disconnected from the shared memory + explicit SingleInstance(bool allowMultiple, QObject *parent = 0); /** * @return true if this instance's job is to forward data to the primary @@ -97,6 +87,4 @@ private: }; -Q_DECLARE_OPERATORS_FOR_FLAGS(SingleInstance::Flags); - #endif // SINGLEINSTANCE_H diff --git a/src/spawn.cpp b/src/spawn.cpp index 2016bb23..a9ecb61e 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -488,7 +488,7 @@ DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle) } const QString moPath = QCoreApplication::applicationDirPath(); - const auto oldPath = env::addPath(QDir::toNativeSeparators(moPath)); + const auto oldPath = env::appendToPath(QDir::toNativeSeparators(moPath)); PROCESS_INFORMATION pi = {}; BOOL success = FALSE; -- cgit v1.3.1 From ae118153fbedc0240ea183488834a32abe202839 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 23 Jul 2020 00:38:03 -0400 Subject: more refactoring: - moved splash stuff together - moved stuff in the main loop into doOneRun() --- src/loglist.cpp | 2 + src/main.cpp | 213 ++++++++++++++++++++++++++++---------------------- src/moapplication.cpp | 4 +- src/moapplication.h | 4 +- src/organizercore.cpp | 44 +++++++++-- src/settings.cpp | 2 +- src/settings.h | 2 +- 7 files changed, 164 insertions(+), 107 deletions(-) (limited to 'src/loglist.cpp') diff --git a/src/loglist.cpp b/src/loglist.cpp index 167b61ef..fad4678c 100644 --- a/src/loglist.cpp +++ b/src/loglist.cpp @@ -352,4 +352,6 @@ bool setLogDirectory(const QString& dir) } log::getDefault().setFile(MOBase::log::File::single(logFile.toStdWString())); + + return true; } diff --git a/src/main.cpp b/src/main.cpp index 864d26a1..52011ef4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -68,26 +68,31 @@ void purgeOldFiles() "usvfs*.log", 5, QDir::Name); } -thread_local LPTOP_LEVEL_EXCEPTION_FILTER prevUnhandledExceptionFilter = nullptr; -thread_local std::terminate_handler prevTerminateHandler = nullptr; +thread_local LPTOP_LEVEL_EXCEPTION_FILTER g_prevExceptionFilter = nullptr; +thread_local std::terminate_handler g_prevTerminateHandler = nullptr; -LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtrs) +LONG WINAPI onUnhandledException(_EXCEPTION_POINTERS* ptrs) { const std::wstring& dumpPath = OrganizerCore::crashDumpsPath(); - int dumpRes = - CreateMiniDump(exceptionPtrs, OrganizerCore::getGlobalCrashDumpsType(), dumpPath.c_str()); - if (!dumpRes) + + const int r = CreateMiniDump( + ptrs, OrganizerCore::getGlobalCrashDumpsType(), dumpPath.c_str()); + + if (r == 0) { log::error("ModOrganizer has crashed, crash dump created."); - else - log::error("ModOrganizer has crashed, CreateMiniDump failed ({}, error {}).", dumpRes, GetLastError()); + } else { + log::error( + "ModOrganizer has crashed, CreateMiniDump failed ({}, error {}).", + r, GetLastError()); + } - if (prevUnhandledExceptionFilter && exceptionPtrs) - return prevUnhandledExceptionFilter(exceptionPtrs); + if (g_prevExceptionFilter && ptrs) + return g_prevExceptionFilter(ptrs); else return EXCEPTION_CONTINUE_SEARCH; } -void terminateHandler() noexcept +void onTerminate() noexcept { __try { @@ -96,22 +101,22 @@ void terminateHandler() noexcept } __except ( - MyUnhandledExceptionFilter(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER + onUnhandledException(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER ) { } - if (prevTerminateHandler) { - prevTerminateHandler(); + if (g_prevTerminateHandler) { + g_prevTerminateHandler(); } else { std::abort(); } } -void setUnhandledExceptionHandler() +void setExceptionHandlers() { - prevUnhandledExceptionFilter = SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); - prevTerminateHandler = std::set_terminate(terminateHandler); + g_prevExceptionFilter = SetUnhandledExceptionFilter(onUnhandledException); + g_prevTerminateHandler = std::set_terminate(onTerminate); } QString determineProfile(const cl::CommandLine& cl, const Settings &settings) @@ -334,6 +339,55 @@ void addDllsToPath() env::prependToPath(dllsPath); } +QString getSplashPath( + const Settings& settings, const QString& dataPath, + const MOBase::IPluginGame* game) +{ + if (!settings.useSplash()) { + return {}; + } + + const QString splashPath = dataPath + "/splash.png"; + if (QFile::exists(dataPath + "/splash.png")) { + return splashPath; + } + + // currently using MO splash, see if the plugin contains one + QString pluginSplash = QString(":/%1/splash").arg(game->gameShortName()); + QImage image(pluginSplash); + + if (image.isNull()) { + return {}; + } + + image.save(splashPath); + return splashPath; +} + +std::unique_ptr createSplash( + const Settings& settings, const QString& dataPath, + const MOBase::IPluginGame* game) +{ + const auto splashPath = getSplashPath(settings, dataPath, game); + if (splashPath.isEmpty()) { + return {}; + } + + QPixmap image(splashPath); + if (image.isNull()) { + log::error("failed to load splash from {}", splashPath); + return {}; + } + + auto splash = std::make_unique(image); + settings.geometry().centerOnMainWindowMonitor(splash.get()); + + splash->show(); + splash->activateWindow(); + + return splash; +} + int runApplication( MOApplication &application, const cl::CommandLine& cl, SingleInstance &instance, const QString &dataPath) @@ -351,6 +405,8 @@ int runApplication( log::debug("this is a portable instance"); } + log::info("working directory: {}", QDir::currentPath()); + if (!instance.secondary()) { purgeOldFiles(); } @@ -358,9 +414,8 @@ int runApplication( QWindowsWindowFunctions::setWindowActivationBehavior( QWindowsWindowFunctions::AlwaysActivateWindow); - try { - log::info("working directory: {}", QDir::currentPath()); - + try + { Settings settings(dataPath + "/" + QString::fromStdWString(AppConfig::iniFileName())); log::getDefault().setLevel(settings.diagnostics().logLevel()); @@ -370,7 +425,6 @@ int runApplication( log::debug("another instance of MO is running but --multiple was given"); } - // global crashDumpType sits in OrganizerCore to make a bit less ugly to // update it when the settings are changed during runtime OrganizerCore::setGlobalCrashDumpsType(settings.diagnostics().crashDumpsType()); @@ -385,8 +439,10 @@ int runApplication( checkIncompatibleModule(m); }); - log::debug("initializing core"); + // this must outlive `organizer` std::unique_ptr pluginContainer; + + log::debug("initializing core"); OrganizerCore organizer(settings); if (!organizer.bootstrap()) { reportError("failed to set up data paths"); @@ -394,25 +450,11 @@ int runApplication( return 1; } - { - // log if there are any dmp files - const auto hasCrashDumps = - !QDir(QString::fromStdWString(organizer.crashDumpsPath())) - .entryList({"*.dmp"}, QDir::Files) - .empty(); - - if (hasCrashDumps) { - log::debug( - "there are crash dumps in '{}'", - QString::fromStdWString(organizer.crashDumpsPath())); - } - } - log::debug("initializing plugins"); pluginContainer = std::make_unique(&organizer); pluginContainer->loadPlugins(); - MOBase::IPluginGame *game = determineCurrentGame( + MOBase::IPluginGame* game = determineCurrentGame( application.applicationDirPath(), settings, *pluginContainer); if (game == nullptr) { @@ -429,25 +471,6 @@ int runApplication( checkPathsForSanity(*game, settings); - bool useSplash = settings.useSplash(); - QString splashPath; - - if (useSplash) { - splashPath = dataPath + "/splash.png"; - if (!QFile::exists(dataPath + "/splash.png")) { - splashPath = ":/MO/gui/splash"; - } - - if (splashPath.startsWith(':')) { - // currently using MO splash, see if the plugin contains one - QString pluginSplash - = QString(":/%1/splash").arg(game->gameShortName()); - QImage image(pluginSplash); - if (!image.isNull()) { - image.save(dataPath + "/splash.png"); - } - } - } organizer.setManagedGame(game); organizer.createDefaultProfile(); @@ -535,18 +558,7 @@ int runApplication( } } - QPixmap pixmap; - - QSplashScreen splash; - - if (useSplash) { - pixmap = QPixmap(splashPath); - splash.setPixmap(pixmap); - - settings.geometry().centerOnMainWindowMonitor(&splash); - splash.show(); - splash.activateWindow(); - } + auto splash = createSplash(settings, dataPath, game); QString apiKey; if (settings.nexus().apiKey(apiKey)) { @@ -582,10 +594,10 @@ int runApplication( mainWindow.show(); mainWindow.activateWindow(); - if (useSplash) { + if (splash) { // don't pass mainwindow as it just waits half a second for it // instead of proceding - splash.finish(nullptr); + splash->finish(nullptr); } tt.stop(); @@ -655,6 +667,35 @@ QString determineDataPath(const cl::CommandLine& cl) } } +int doOneRun( + cl::CommandLine& cl, MOApplication& application, SingleInstance& instance) +{ + TimeThis tt("doOneRun() to runApplication()"); + + // resets things when MO is "restarted" + resetForRestart(cl); + + const QString dataPath = determineDataPath(cl); + if (dataPath.isEmpty()) { + return 1; + } + + application.setProperty("dataPath", dataPath); + setExceptionHandlers(); + + if (!setLogDirectory(dataPath)) { + reportError("Failed to create log folder"); + InstanceManager::instance().clearCurrentInstance(); + return 1; + } + + log::debug("command line: '{}'", QString::fromWCharArray(GetCommandLineW())); + + tt.stop(); + + return runApplication(application, cl, instance, dataPath); +} + int main(int argc, char *argv[]) { cl::CommandLine cl; @@ -663,7 +704,8 @@ int main(int argc, char *argv[]) return *r; } - TimeThis tt("main to runApplication()"); + TimeThis tt("main() to doOneRun()"); + SetThisThreadName("main"); initLogging(); @@ -675,32 +717,15 @@ int main(int argc, char *argv[]) return forwardToPrimary(instance, cl); } + tt.stop(); + for (;;) { - // resets things when MO is "restarted" - resetForRestart(cl); - - const QString dataPath = determineDataPath(cl); - if (dataPath.isEmpty()) { - return 1; - } - - application.setProperty("dataPath", dataPath); - setExceptionHandler(); - - if (!setLogDirectory(dataPath)) { - reportError("Failed to create log folder"); - InstanceManager::instance().clearCurrentInstance(); - return 1; + const auto r = doOneRun(cl, application, instance); + if (r == RestartExitCode) { + continue; } - log::debug("command line: '{}'", QString::fromWCharArray(GetCommandLineW())); - - tt.stop(); - - const int result = runApplication(application, cl, instance, dataPath); - if (result != RestartExitCode) { - return result; - } + return r; } } diff --git a/src/moapplication.cpp b/src/moapplication.cpp index d95d544a..290666af 100644 --- a/src/moapplication.cpp +++ b/src/moapplication.cpp @@ -71,7 +71,7 @@ public: }; -MOApplication::MOApplication(int argc, char** argv) +MOApplication::MOApplication(int& argc, char** argv) : QApplication(argc, argv) { connect(&m_StyleWatcher, &QFileSystemWatcher::fileChanged, [&](auto&& file){ @@ -83,7 +83,7 @@ MOApplication::MOApplication(int argc, char** argv) setStyle(new ProxyStyle(style())); } -MOApplication MOApplication::create(int argc, char** argv) +MOApplication MOApplication::create(int& argc, char** argv) { QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); return MOApplication(argc, argv); diff --git a/src/moapplication.h b/src/moapplication.h index 3ed71fb6..c67c4622 100644 --- a/src/moapplication.h +++ b/src/moapplication.h @@ -29,7 +29,7 @@ class MOApplication : public QApplication Q_OBJECT public: - static MOApplication create(int argc, char** argv); + static MOApplication create(int& argc, char** argv); virtual bool notify (QObject* receiver, QEvent* event); public slots: @@ -42,7 +42,7 @@ private: QFileSystemWatcher m_StyleWatcher; QString m_DefaultStyle; - MOApplication(int argc, char** argv); + MOApplication(int& argc, char** argv); }; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index a9469e6e..0849d756 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -473,13 +473,43 @@ bool OrganizerCore::checkPathSymlinks() { return true; } -bool OrganizerCore::bootstrap() { - return createDirectory(m_Settings.paths().profiles()) && - createDirectory(m_Settings.paths().mods()) && - createDirectory(m_Settings.paths().downloads()) && - createDirectory(m_Settings.paths().overwrite()) && - createDirectory(QString::fromStdWString(crashDumpsPath())) && - checkPathSymlinks() && cycleDiagnostics(); +bool OrganizerCore::bootstrap() +{ + const auto dirs = { + m_Settings.paths().profiles(), + m_Settings.paths().mods(), + m_Settings.paths().downloads(), + m_Settings.paths().overwrite(), + QString::fromStdWString(crashDumpsPath()) + }; + + for (auto&& dir : dirs) { + if (!createDirectory(dir)) { + return false; + } + } + + if (!checkPathSymlinks()) { + return false; + } + + if (!cycleDiagnostics()) { + return false; + } + + // log if there are any dmp files + const auto hasCrashDumps = + !QDir(QString::fromStdWString(crashDumpsPath())) + .entryList({"*.dmp"}, QDir::Files) + .empty(); + + if (hasCrashDumps) { + log::debug( + "there are crash dumps in '{}'", + QString::fromStdWString(crashDumpsPath())); + } + + return true; } void OrganizerCore::createDefaultProfile() diff --git a/src/settings.cpp b/src/settings.cpp index 534e67c8..d37f0d99 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -876,7 +876,7 @@ void GeometrySettings::setCenterDialogs(bool b) set(m_Settings, "Settings", "center_dialogs", b); } -void GeometrySettings::centerOnMainWindowMonitor(QWidget* w) +void GeometrySettings::centerOnMainWindowMonitor(QWidget* w) const { const auto monitor = getOptional( m_Settings, "Geometry", "MainWindow_monitor").value_or(-1); diff --git a/src/settings.h b/src/settings.h index 636719bb..a1b30462 100644 --- a/src/settings.h +++ b/src/settings.h @@ -169,7 +169,7 @@ public: // assumes the given widget is a top-level // - void centerOnMainWindowMonitor(QWidget* w); + void centerOnMainWindowMonitor(QWidget* w) const; // saves the monitor number of the given window // -- cgit v1.3.1