From 3bca0e761597f590fa3b1c6a57ee325e774e3f49 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 10 Jan 2021 22:06:38 -0500 Subject: split run() into setup() and run() this is so command line processing can happen outside of MOApplication many important objects were local to run(), so they're not members of MOApplication instead --- src/moapplication.cpp | 174 ++++++++++++++++++++------------------------------ 1 file changed, 69 insertions(+), 105 deletions(-) (limited to 'src/moapplication.cpp') diff --git a/src/moapplication.cpp b/src/moapplication.cpp index 9a77c17e..d1622d59 100644 --- a/src/moapplication.cpp +++ b/src/moapplication.cpp @@ -155,77 +155,43 @@ void addDllsToPath() } -MOApplication::MOApplication(cl::CommandLine& cl, int& argc, char** argv) - : QApplication(argc, argv), m_cl(cl) +MOApplication::MOApplication(int& argc, char** argv) + : QApplication(argc, argv) { TimeThis tt("MOApplication()"); - connect(&m_StyleWatcher, &QFileSystemWatcher::fileChanged, [&](auto&& file){ + connect(&m_styleWatcher, &QFileSystemWatcher::fileChanged, [&](auto&& file){ log::debug("style file '{}' changed, reloading", file); updateStyle(file); }); - m_DefaultStyle = style()->objectName(); + m_defaultStyle = style()->objectName(); setStyle(new ProxyStyle(style())); addDllsToPath(); } -int MOApplication::run(MOMultiProcess& multiProcess) +OrganizerCore& MOApplication::core() { - TimeThis tt("MOApplication run() to doOneRun()"); - - auto& m = InstanceManager::singleton(); - - if (m_cl.instance()) - m.overrideInstance(*m_cl.instance()); + return *m_core; +} - if (m_cl.profile()) { - m.overrideProfile(*m_cl.profile()); - } +int MOApplication::setup(MOMultiProcess& multiProcess) +{ + TimeThis tt("MOApplication setup()"); // makes plugin data path available to plugins, see // IOrganizer::getPluginDataPath() MOBase::details::setPluginDataPath(OrganizerCore::pluginDataPath()); - // MO runs in a loop because it can be restarted in several ways, such as - // when switching instances or changing some settings - for (;;) - { - try - { - tt.stop(); - - const auto r = doOneRun(multiProcess); - - if (r == RestartExitCode) { - // resets things when MO is "restarted" - resetForRestart(); - continue; - } - - return r; - } - catch (const std::exception &e) - { - reportError(e.what()); - return 1; - } - } -} - -int MOApplication::doOneRun(MOMultiProcess& multiProcess) -{ - TimeThis tt("MOApplication::doOneRun() instances"); - // figuring out the current instance - auto currentInstance = getCurrentInstance(); - if (!currentInstance) { + m_instance = getCurrentInstance(); + if (!m_instance) { return 1; } // first time the data path is available, set the global property and log // directory, then log a bunch of debug stuff - const QString dataPath = currentInstance->directory(); + const QString dataPath = m_instance->directory(); setProperty("dataPath", dataPath); if (!setLogDirectory(dataPath)) { @@ -245,7 +211,7 @@ int MOApplication::doOneRun(MOMultiProcess& multiProcess) log::debug("another instance of MO is running but --multiple was given"); } - log::info("data path: {}", currentInstance->directory()); + log::info("data path: {}", m_instance->directory()); log::info("working directory: {}", QDir::currentPath()); @@ -261,44 +227,41 @@ int MOApplication::doOneRun(MOMultiProcess& multiProcess) // loading settings - Settings settings(currentInstance->iniPath(), true); - log::getDefault().setLevel(settings.diagnostics().logLevel()); - log::debug("using ini at '{}'", settings.filename()); + m_settings.reset(new Settings(m_instance->iniPath(), true)); + log::getDefault().setLevel(m_settings->diagnostics().logLevel()); + log::debug("using ini at '{}'", m_settings->filename()); - OrganizerCore::setGlobalCoreDumpType(settings.diagnostics().coreDumpType()); + OrganizerCore::setGlobalCoreDumpType(m_settings->diagnostics().coreDumpType()); tt.start("MOApplication::doOneRun() log and checks"); // logging and checking env::Environment env; - env.dump(settings); - settings.dump(); + env.dump(*m_settings); + m_settings->dump(); sanity::checkEnvironment(env); - const auto moduleNotification = env.onModuleLoaded(qApp, [](auto&& m) { + m_modules = std::move(env.onModuleLoaded(qApp, [](auto&& m) { if (m.interesting()) { log::debug("loaded module {}", m.toString()); } sanity::checkIncompatibleModule(m); - }); + })); - // this must outlive `organizer` - std::unique_ptr pluginContainer; - // nexus interface tt.start("MOApplication::doOneRun() NexusInterface"); log::debug("initializing nexus interface"); - NexusInterface ni(&settings); + m_nexus.reset(new NexusInterface(m_settings.get())); // organizer core tt.start("MOApplication::doOneRun() OrganizerCore"); log::debug("initializing core"); - OrganizerCore organizer(settings); - if (!organizer.bootstrap()) { + m_core.reset(new OrganizerCore(*m_settings)); + if (!m_core->bootstrap()) { reportError("failed to set up data paths"); InstanceManager::singleton().clearCurrentInstance(); return 1; @@ -308,58 +271,59 @@ int MOApplication::doOneRun(MOMultiProcess& multiProcess) tt.start("MOApplication::doOneRun() plugins"); log::debug("initializing plugins"); - pluginContainer = std::make_unique(&organizer); - pluginContainer->loadPlugins(); + m_plugins = std::make_unique(m_core.get()); + m_plugins->loadPlugins(); // instance - if (auto r=setupInstanceLoop(*currentInstance, *pluginContainer)) { + if (auto r=setupInstanceLoop(*m_instance, *m_plugins)) { return *r; } - if (currentInstance->isPortable()) { + if (m_instance->isPortable()) { log::debug("this is a portable instance"); } tt.start("MOApplication::doOneRun() OrganizerCore setup"); - sanity::checkPaths(*currentInstance->gamePlugin(), settings); + sanity::checkPaths(*m_instance->gamePlugin(), *m_settings); // setting up organizer core - organizer.setManagedGame(currentInstance->gamePlugin()); - organizer.createDefaultProfile(); + m_core->setManagedGame(m_instance->gamePlugin()); + m_core->createDefaultProfile(); log::info( "using game plugin '{}' ('{}', variant {}, steam id '{}') at {}", - currentInstance->gamePlugin()->gameName(), - currentInstance->gamePlugin()->gameShortName(), - (settings.game().edition().value_or("").isEmpty() ? - "(none)" : *settings.game().edition()), - currentInstance->gamePlugin()->steamAPPId(), - currentInstance->gamePlugin()->gameDirectory().absolutePath()); + m_instance->gamePlugin()->gameName(), + m_instance->gamePlugin()->gameShortName(), + (m_settings->game().edition().value_or("").isEmpty() ? + "(none)" : *m_settings->game().edition()), + m_instance->gamePlugin()->steamAPPId(), + m_instance->gamePlugin()->gameDirectory().absolutePath()); CategoryFactory::instance().loadCategories(); - organizer.updateExecutablesList(); - organizer.updateModInfoFromDisc(); - organizer.setCurrentProfile(currentInstance->profileName()); + m_core->updateExecutablesList(); + m_core->updateModInfoFromDisc(); + m_core->setCurrentProfile(m_instance->profileName()); + + return 0; +} +int MOApplication::run(MOMultiProcess& multiProcess) +{ // checking command line - tt.start("MOApplication::doOneRun() command line"); - if (auto r=m_cl.setupCore(organizer)) { - return *r; - } + TimeThis tt("MOApplication::run()"); // show splash tt.start("MOApplication::doOneRun() splash"); - MOSplash splash( - settings, currentInstance->directory(), currentInstance->gamePlugin()); + MOSplash splash(*m_settings, m_instance->directory(), m_instance->gamePlugin()); tt.start("MOApplication::doOneRun() finishing"); // start an api check QString apiKey; if (GlobalSettings::nexusApiKey(apiKey)) { - ni.getAccessManager()->apiCheck(apiKey); + m_nexus->getAccessManager()->apiCheck(apiKey); } // tutorials @@ -367,12 +331,12 @@ int MOApplication::doOneRun(MOMultiProcess& multiProcess) TutorialManager::init( qApp->applicationDirPath() + "/" + QString::fromStdWString(AppConfig::tutorialsPath()) + "/", - &organizer); + m_core.get()); // styling - if (!setStyleFile(settings.interface().styleName().value_or(""))) { + if (!setStyleFile(m_settings->interface().styleName().value_or(""))) { // disable invalid stylesheet - settings.interface().setStyleName(""); + m_settings->interface().setStyleName(""); } @@ -380,16 +344,16 @@ int MOApplication::doOneRun(MOMultiProcess& multiProcess) { tt.start("MOApplication::doOneRun() MainWindow setup"); - MainWindow mainWindow(settings, organizer, *pluginContainer); + MainWindow mainWindow(*m_settings, *m_core, *m_plugins); // the nexus interface can show dialogs, make sure they're parented to the // main window - ni.getAccessManager()->setTopLevelWidget(&mainWindow); + m_nexus->getAccessManager()->setTopLevelWidget(&mainWindow); QObject::connect(&mainWindow, SIGNAL(styleChanged(QString)), this, SLOT(setStyleFile(QString))); - QObject::connect(&multiProcess, SIGNAL(messageSent(QString)), &organizer, + QObject::connect(&multiProcess, SIGNAL(messageSent(QString)), m_core.get(), SLOT(externalMessage(QString))); @@ -404,16 +368,16 @@ int MOApplication::doOneRun(MOMultiProcess& multiProcess) mainWindow.close(); // main window is about to be destroyed - ni.getAccessManager()->setTopLevelWidget(nullptr); + m_nexus->getAccessManager()->setTopLevelWidget(nullptr); } // reset geometry if the flag was set from the settings dialog - settings.geometry().resetIfNeeded(); + m_settings->geometry().resetIfNeeded(); return res; } -std::optional MOApplication::getCurrentInstance() +std::unique_ptr MOApplication::getCurrentInstance() { auto& m = InstanceManager::singleton(); auto currentInstance = m.currentInstance(); @@ -422,8 +386,6 @@ std::optional MOApplication::getCurrentInstance() { // clear any overrides that might have been given on the command line m.clearOverrides(); - m_cl.clear(); - currentInstance = selectInstance(); } else @@ -433,7 +395,6 @@ std::optional MOApplication::getCurrentInstance() // clear any overrides that might have been given on the command line m.clearOverrides(); - m_cl.clear(); if (m.hasAnyInstances()) { MOShared::criticalOnTop(QObject::tr( @@ -496,31 +457,34 @@ void MOApplication::resetForRestart() // the previous instance gets deleted log::getDefault().setFile({}); - // don't reprocess command line - m_cl.clear(); - // clear instance and profile overrides InstanceManager::singleton().clearOverrides(); + + m_core = {}; + m_plugins = {}; + m_nexus = {}; + m_settings = {}; + m_instance = {}; } bool MOApplication::setStyleFile(const QString& styleName) { // remove all files from watch - QStringList currentWatch = m_StyleWatcher.files(); + QStringList currentWatch = m_styleWatcher.files(); if (currentWatch.count() != 0) { - m_StyleWatcher.removePaths(currentWatch); + m_styleWatcher.removePaths(currentWatch); } // set new stylesheet or clear it if (styleName.length() != 0) { QString styleSheetName = applicationDirPath() + "/" + MOBase::ToQString(AppConfig::stylesheetsPath()) + "/" + styleName; if (QFile::exists(styleSheetName)) { - m_StyleWatcher.addPath(styleSheetName); + m_styleWatcher.addPath(styleSheetName); updateStyle(styleSheetName); } else { updateStyle(styleName); } } else { - setStyle(new ProxyStyle(QStyleFactory::create(m_DefaultStyle))); + setStyle(new ProxyStyle(QStyleFactory::create(m_defaultStyle))); setStyleSheet(""); } return true; @@ -551,7 +515,7 @@ void MOApplication::updateStyle(const QString& fileName) setStyleSheet(""); setStyle(new ProxyStyle(QStyleFactory::create(fileName))); } else { - setStyle(new ProxyStyle(QStyleFactory::create(m_DefaultStyle))); + setStyle(new ProxyStyle(QStyleFactory::create(m_defaultStyle))); if (QFile::exists(fileName)) { setStyleSheet(QString("file:///%1").arg(fileName)); } else { -- cgit v1.3.1 From 09eb507ddad0d164a39d72c5c121d332966ef462 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 11 Jan 2021 01:25:47 -0500 Subject: moved externalMessage() to MOApplication commands can forward to the primary instance added reload-plugin command MessageDialog now tries to find the main window when the reference is null, which actually used to happen often because activeWindow() is typically used, but that's null when the main window doesn't have focus (like when downloading from nexus, for example) --- src/commandline.cpp | 118 +++++++++++++++++++++++++++++++++++++++++++------- src/commandline.h | 37 +++++++++++++--- src/main.cpp | 34 +++++++-------- src/messagedialog.cpp | 22 +++++++--- src/moapplication.cpp | 49 ++++++++++++++++++--- src/moapplication.h | 4 +- src/organizercore.cpp | 19 -------- src/organizercore.h | 1 - 8 files changed, 213 insertions(+), 71 deletions(-) (limited to 'src/moapplication.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp index fd0f6cb5..b6210463 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -2,8 +2,10 @@ #include "env.h" #include "organizercore.h" #include "instancemanager.h" +#include "multiprocess.h" #include "shared/util.h" #include "shared/error_report.h" +#include "shared/appconfig.h" #include #include @@ -54,9 +56,12 @@ CommandLine::CommandLine() : m_command(nullptr) { createOptions(); - m_commands.push_back(std::make_unique()); - m_commands.push_back(std::make_unique()); - m_commands.push_back(std::make_unique()); + + add< + RunCommand, + ReloadPluginCommand, + CrashDumpCommand, + LaunchCommand>(); } std::optional CommandLine::process(const std::wstring& line) @@ -130,7 +135,7 @@ std::optional CommandLine::process(const std::wstring& line) c->process(line, m_vm, opts); m_command = c.get(); - return m_command->runPreOrganizer(); + return m_command->runEarly(); } catch(po::error& e) { @@ -170,7 +175,7 @@ std::optional CommandLine::process(const std::wstring& line) return 1; } - // try as an moshorcut:// + // try as an moshortcut:// m_shortcut = qs; if (!m_shortcut.isValid()) { @@ -205,14 +210,34 @@ std::optional CommandLine::process(const std::wstring& line) } } -std::optional CommandLine::run(OrganizerCore& organizer) const +bool CommandLine::forwardToPrimary(MOMultiProcess& multiProcess) +{ + if (m_shortcut.isValid()) { + multiProcess.sendMessage(m_shortcut.toString()); + } else if (m_nxmLink) { + multiProcess.sendMessage(*m_nxmLink); + } else if (m_command && m_command->canForwardToPrimary()) { + multiProcess.sendMessage(QString::fromWCharArray(GetCommandLineW())); + } else { + return false; + } + + return true; +} + +std::optional CommandLine::runPostMultiProcess(MOMultiProcess& mp) +{ + return {}; +} + +std::optional CommandLine::runPostOrganizer(OrganizerCore& core) { if (m_shortcut.isValid()) { if (m_shortcut.hasExecutable()) { try { // make sure MO doesn't exit even if locking is disabled, ForceWait and // PreventExit will do that - organizer.processRunner() + core.processRunner() .setFromShortcut(m_shortcut) .setWaitForCompletion(ProcessRunner::ForceWait, UILocker::PreventExit) .run(); @@ -227,7 +252,7 @@ std::optional CommandLine::run(OrganizerCore& organizer) const } } else if (m_nxmLink) { log::debug("starting download from command line: {}", *m_nxmLink); - organizer.externalMessage(*m_nxmLink); + core.downloadRequestedNXM(*m_nxmLink); } else if (m_executable) { const QString exeName = *m_executable; log::debug("starting {} from command line", exeName); @@ -238,7 +263,7 @@ std::optional CommandLine::run(OrganizerCore& organizer) const // // make sure MO doesn't exit even if locking is disabled, ForceWait and // PreventExit will do that - organizer.processRunner() + core.processRunner() .setFromFileOrExecutable(exeName, m_untouched) .setWaitForCompletion(ProcessRunner::ForceWait, UILocker::PreventExit) .run(); @@ -252,7 +277,7 @@ std::optional CommandLine::run(OrganizerCore& organizer) const return 1; } } else if (m_command) { - return m_command->runPostOrganizer(organizer); + return m_command->runPostOrganizer(core); } return {}; @@ -479,7 +504,17 @@ void Command::process( m_untouched = untouched; } -std::optional Command::runPreOrganizer() +std::optional Command::runEarly() +{ + return {}; +} + +bool Command::canForwardToPrimary() const +{ + return false; +} + +std::optional Command::runPostMultiProcess(MOMultiProcess&) { return {}; } @@ -520,7 +555,7 @@ Command::Meta CrashDumpCommand::meta() const return {"crashdump", "writes a crashdump for a running process of MO"}; } -std::optional CrashDumpCommand::runPreOrganizer() +std::optional CrashDumpCommand::runEarly() { env::Console console; @@ -550,7 +585,7 @@ bool LaunchCommand::legacy() const return true; } -std::optional LaunchCommand::runPreOrganizer() +std::optional LaunchCommand::runEarly() { // needs at least the working directory and process name if (untouched().size() < 2) { @@ -657,7 +692,7 @@ Command::Meta RunCommand::meta() const return {"run", "runs a program, file or a configured executable"}; } -std::optional RunCommand::runPostOrganizer(OrganizerCore& organizer) +std::optional RunCommand::runPostOrganizer(OrganizerCore& core) { const auto program = QString::fromStdString(vm()["program"].as()); @@ -665,10 +700,10 @@ std::optional RunCommand::runPostOrganizer(OrganizerCore& organizer) { // make sure MO doesn't exit even if locking is disabled, ForceWait and // PreventExit will do that - auto p = organizer.processRunner(); + auto p = core.processRunner(); if (vm()["executable"].as()) { - const auto& exes = *organizer.executablesList(); + const auto& exes = *core.executablesList(); auto itor = exes.find(program); if (itor == exes.end()) { @@ -714,4 +749,55 @@ std::optional RunCommand::runPostOrganizer(OrganizerCore& organizer) } } + + +std::string ReloadPluginCommand::getUsageLine() const +{ + return "plugin-name"; +} + +po::options_description ReloadPluginCommand::getInternalOptions() const +{ + po::options_description d; + + d.add_options() + ("plugin-name", po::value()->required(), "plugin name"); + + return d; +} + +po::positional_options_description ReloadPluginCommand::getPositional() const +{ + po::positional_options_description d; + + d.add("plugin-name", 1); + + return d; +} + +Command::Meta ReloadPluginCommand::meta() const +{ + return {"reload-plugin", "reloads the given plugin"}; +} + +bool ReloadPluginCommand::canForwardToPrimary() const +{ + return true; +} + +std::optional ReloadPluginCommand::runPostOrganizer(OrganizerCore& core) +{ + const QString name = QString::fromStdString(vm()["plugin-name"].as()); + + QString filepath = QDir( + qApp->applicationDirPath() + "/" + + ToQString(AppConfig::pluginPath())) + .absoluteFilePath(name); + + log::debug("reloading plugin from {}", filepath); + core.pluginContainer().reloadPlugin(filepath); + + return {}; +} + } // namespace diff --git a/src/commandline.h b/src/commandline.h index d0d62a72..6f96d724 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -5,6 +5,7 @@ #include class OrganizerCore; +class MOMultiProcess; namespace cl { @@ -59,8 +60,10 @@ public: // // - virtual std::optional runPreOrganizer(); - virtual std::optional runPostOrganizer(OrganizerCore& organizer); + virtual std::optional runEarly(); + virtual bool canForwardToPrimary() const; + virtual std::optional runPostMultiProcess(MOMultiProcess& mp); + virtual std::optional runPostOrganizer(OrganizerCore& core); protected: // meta information about this command, returned by derived classes @@ -118,7 +121,7 @@ class CrashDumpCommand : public Command protected: po::options_description getVisibleOptions() const override; Meta meta() const override; - std::optional runPreOrganizer() override; + std::optional runEarly() override; }; @@ -141,7 +144,7 @@ public: protected: Meta meta() const override; - std::optional runPreOrganizer() override; + std::optional runEarly() override; int SpawnWaitProcess(LPCWSTR workingDirectory, LPCWSTR commandLine); @@ -160,7 +163,21 @@ protected: po::options_description getInternalOptions() const override; po::positional_options_description getPositional() const override; Meta meta() const override; - std::optional runPostOrganizer(OrganizerCore& organizer) override; + std::optional runPostOrganizer(OrganizerCore& core) override; +}; + + +// reloads the given plugin +// +class ReloadPluginCommand : public Command +{ +protected: + std::string getUsageLine() const override; + po::options_description getInternalOptions() const override; + po::positional_options_description getPositional() const override; + Meta meta() const override; + bool canForwardToPrimary() const override; + std::optional runPostOrganizer(OrganizerCore& core) override; }; @@ -215,7 +232,9 @@ public: // returns an empty optional if execution should continue, or a return code // if MO must quit // - std::optional run(OrganizerCore& organizer) const; + bool forwardToPrimary(MOMultiProcess& multiProcess); + std::optional runPostMultiProcess(MOMultiProcess& mp); + std::optional runPostOrganizer(OrganizerCore& core); // clears parsed options, used when MO is "restarted" so the options aren't @@ -269,6 +288,12 @@ private: void createOptions(); std::string more() const; + + template + void add() + { + (m_commands.push_back(std::make_unique()), ...); + } }; } // namespace diff --git a/src/main.cpp b/src/main.cpp index f85e13f3..adb33d91 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,12 +37,27 @@ int main(int argc, char *argv[]) MOMultiProcess multiProcess(cl.multiple()); if (multiProcess.ephemeral()) { - return forwardToPrimary(multiProcess, cl); + if (cl.forwardToPrimary(multiProcess)) { + return 0; + } else { + QMessageBox::information( + nullptr, QObject::tr("Mod Organizer"), + QObject::tr("An instance of Mod Organizer is already running")); + } + + return 0; + } + + if (auto r=cl.runPostMultiProcess(multiProcess)) { + return *r; } tt.stop(); + app.firstTimeSetup(multiProcess); + + // MO runs in a loop because it can be restarted in several ways, such as // when switching instances or changing some settings for (;;) @@ -73,7 +88,7 @@ int main(int argc, char *argv[]) } } - if (auto r=cl.run(app.core())) { + if (auto r=cl.runPostOrganizer(app.core())) { return *r; } @@ -99,21 +114,6 @@ int main(int argc, char *argv[]) } } -int forwardToPrimary(MOMultiProcess& multiProcess, const cl::CommandLine& cl) -{ - if (cl.shortcut().isValid()) { - multiProcess.sendMessage(cl.shortcut().toString()); - } else if (cl.nxmLink()) { - multiProcess.sendMessage(*cl.nxmLink()); - } else { - QMessageBox::information( - nullptr, QObject::tr("Mod Organizer"), - QObject::tr("An instance of Mod Organizer is already running")); - } - - return 0; -} - LONG WINAPI onUnhandledException(_EXCEPTION_POINTERS* ptrs) { const auto path = OrganizerCore::getGlobalCoreDumpPath(); diff --git a/src/messagedialog.cpp b/src/messagedialog.cpp index 78a5dd4d..1244e8c6 100644 --- a/src/messagedialog.cpp +++ b/src/messagedialog.cpp @@ -86,11 +86,23 @@ void MessageDialog::showMessage(const QString &text, QWidget *reference, bool br { log::debug("{}", text); - if (reference != nullptr) { - if (bringToFront || (qApp->activeWindow() != nullptr)) { - MessageDialog *dialog = new MessageDialog(text, reference); - dialog->show(); - reference->activateWindow(); + if (!reference) { + for (QWidget* w : qApp->topLevelWidgets()) { + if (dynamic_cast(w)) { + reference = w; + break; + } } } + + if (!reference) { + return; + } + + MessageDialog *dialog = new MessageDialog(text, reference); + dialog->show(); + + if (bringToFront) { + reference->activateWindow(); + } } diff --git a/src/moapplication.cpp b/src/moapplication.cpp index d1622d59..01d75ad1 100644 --- a/src/moapplication.cpp +++ b/src/moapplication.cpp @@ -31,6 +31,7 @@ along with Mod Organizer. If not, see . #include "tutorialmanager.h" #include "sanitychecks.h" #include "mainwindow.h" +#include "messagedialog.h" #include "shared/error_report.h" #include "shared/util.h" #include @@ -175,6 +176,14 @@ OrganizerCore& MOApplication::core() return *m_core; } +void MOApplication::firstTimeSetup(MOMultiProcess& multiProcess) +{ + connect( + &multiProcess, &MOMultiProcess::messageSent, this, + [this](auto&& s){ externalMessage(s); }, + Qt::QueuedConnection); +} + int MOApplication::setup(MOMultiProcess& multiProcess) { TimeThis tt("MOApplication setup()"); @@ -350,11 +359,10 @@ int MOApplication::run(MOMultiProcess& multiProcess) // main window m_nexus->getAccessManager()->setTopLevelWidget(&mainWindow); - QObject::connect(&mainWindow, SIGNAL(styleChanged(QString)), this, - SLOT(setStyleFile(QString))); - - QObject::connect(&multiProcess, SIGNAL(messageSent(QString)), m_core.get(), - SLOT(externalMessage(QString))); + connect( + &mainWindow, &MainWindow::styleChanged, this, + [this](auto&& file){ setStyleFile(file); }, + Qt::QueuedConnection); log::debug("displaying main window"); @@ -377,6 +385,37 @@ int MOApplication::run(MOMultiProcess& multiProcess) return res; } +void MOApplication::externalMessage(const QString& message) +{ + log::debug("received external message '{}'", message); + + MOShortcut moshortcut(message); + + if (moshortcut.isValid()) { + if(moshortcut.hasExecutable()) { + m_core->processRunner() + .setFromShortcut(moshortcut) + .setWaitForCompletion(ProcessRunner::Refresh) + .run(); + } + } else if (isNxmLink(message)) { + MessageDialog::showMessage(tr("Download started"), qApp->activeWindow()); + m_core->downloadRequestedNXM(message); + } else { + cl::CommandLine cl; + + if (auto r=cl.process(message.toStdWString())) { + log::debug( + "while processing external message, command line wants to " + "exit; ignoring"); + + return; + } + + cl.runPostOrganizer(*m_core); + } +} + std::unique_ptr MOApplication::getCurrentInstance() { auto& m = InstanceManager::singleton(); diff --git a/src/moapplication.h b/src/moapplication.h index 7e427ca8..2fe3409b 100644 --- a/src/moapplication.h +++ b/src/moapplication.h @@ -40,6 +40,7 @@ class MOApplication : public QApplication public: MOApplication(int& argc, char** argv); + void firstTimeSetup(MOMultiProcess& multiProcess); int setup(MOMultiProcess& multiProcess); int run(MOMultiProcess& multiProcess); void resetForRestart(); @@ -65,8 +66,7 @@ private: std::unique_ptr m_plugins; std::unique_ptr m_core; - int doOneRun(MOMultiProcess& multiProcess); - + void externalMessage(const QString& message); std::unique_ptr getCurrentInstance(); std::optional setupInstanceLoop(Instance& currentInstance, PluginContainer& pc); void purgeOldFiles(); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 5d079f89..fe17ee9a 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -347,25 +347,6 @@ void OrganizerCore::profileRemoved(QString const& profileName) m_ProfileRemoved(profileName); } - -void OrganizerCore::externalMessage(const QString &message) -{ - MOShortcut moshortcut(message); - - if (moshortcut.isValid()) { - if(moshortcut.hasExecutable()) { - processRunner() - .setFromShortcut(moshortcut) - .setWaitForCompletion(ProcessRunner::Refresh) - .run(); - } - } - else if (isNxmLink(message)) { - MessageDialog::showMessage(tr("Download started"), qApp->activeWindow()); - downloadRequestedNXM(message); - } -} - void OrganizerCore::downloadRequested(QNetworkReply *reply, QString gameName, int modID, const QString &fileName) { diff --git a/src/organizercore.h b/src/organizercore.h index 8add5542..39a2c3cb 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -351,7 +351,6 @@ public: // IPluginDiagnose interface public slots: void profileRefresh(); - void externalMessage(const QString &message); void syncOverwrite(); -- cgit v1.3.1 From 181acfe832bef26228e33ac3a021d39528b1a4a9 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 17 Jan 2021 18:29:17 -0500 Subject: renamed Refresh to TriggerRefresh, added WaitForRefresh removed duplicate refreshDirectoryStructure() call that could never work added --logs to output logs to stdout, added final "mod organizer done" log added -i with no arguments to output the current instance name `run -e` now does an additional, case insensitive check for names fixed error being output along with --help --- src/commandline.cpp | 100 ++++++++++++++++++++++++++++++++++++++++-------- src/commandline.h | 13 +++++++ src/executableslist.cpp | 16 ++++++-- src/executableslist.h | 4 +- src/filetree.cpp | 4 +- src/loglist.cpp | 21 ++++++++++ src/loglist.h | 1 + src/main.cpp | 18 +++++++++ src/mainwindow.cpp | 4 +- src/moapplication.cpp | 2 +- src/organizercore.cpp | 4 +- src/organizerproxy.cpp | 2 +- src/processrunner.cpp | 38 +++++++++++++++--- src/processrunner.h | 22 ++++++++++- 14 files changed, 209 insertions(+), 40 deletions(-) (limited to 'src/moapplication.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp index 1b7c7b0e..97d54b92 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -3,6 +3,7 @@ #include "organizercore.h" #include "instancemanager.h" #include "multiprocess.h" +#include "loglist.h" #include "shared/util.h" #include "shared/error_report.h" #include "shared/appconfig.h" @@ -124,19 +125,22 @@ std::optional CommandLine::process(const std::wstring& line) parsed = parser.run(); po::store(parsed, m_vm); - po::notify(m_vm); if (m_vm.count("help")) { env::Console console; std::cout << usage(c.get()) << "\n"; return 0; } + + // must be below the help check because it throws if required + // positional arguments are missing + po::notify(m_vm); } c->set(line, m_vm, opts); m_command = c.get(); - return m_command->runEarly(); + return runEarly(); } catch(po::error& e) { @@ -164,6 +168,7 @@ std::optional CommandLine::process(const std::wstring& line) return 0; } + if (!opts.empty()) { const auto qs = QString::fromStdWString(opts[0]); @@ -226,6 +231,42 @@ bool CommandLine::forwardToPrimary(MOMultiProcess& multiProcess) return true; } +std::optional CommandLine::runEarly() +{ + if (m_vm.count("logs")) { + // in loglist.h + logToStdout(true); + } + + if (m_command) { + return m_command->runEarly(); + } + + return {}; +} + +std::optional CommandLine::runPostApplication(MOApplication& a) +{ + // handle -i with no arguments + if (m_vm.count("instance") && m_vm["instance"].as() == "") { + env::Console c; + + if (auto i=InstanceManager::singleton().currentInstance()) { + std::cout << i->name().toStdString() << "\n"; + } else { + std::cout << "no instance configured\n"; + } + + return 0; + } + + if (m_command) { + return m_command->runPostApplication(a); + } + + return {}; +} + std::optional CommandLine::runPostMultiProcess(MOMultiProcess& mp) { if (m_command) { @@ -244,7 +285,7 @@ std::optional CommandLine::runPostOrganizer(OrganizerCore& core) // PreventExit will do that core.processRunner() .setFromShortcut(m_shortcut) - .setWaitForCompletion(ProcessRunner::ForceWait, UILocker::PreventExit) + .setWaitForCompletion(ProcessRunner::ForCommandLine, UILocker::PreventExit) .run(); return 0; @@ -270,7 +311,7 @@ std::optional CommandLine::runPostOrganizer(OrganizerCore& core) // PreventExit will do that core.processRunner() .setFromFileOrExecutable(exeName, m_untouched) - .setWaitForCompletion(ProcessRunner::ForceWait, UILocker::PreventExit) + .setWaitForCompletion(ProcessRunner::ForCommandLine, UILocker::PreventExit) .run(); return 0; @@ -298,10 +339,23 @@ void CommandLine::clear() void CommandLine::createOptions() { m_visibleOptions.add_options() - ("help", "show this message") - ("multiple", "allow multiple MO processes to run; see below") - ("instance,i", po::value(), "use the given instance (defaults to last used)") - ("profile,p", po::value(), "use the given profile (defaults to last used)"); + ("help", + "show this message") + + ("multiple", + "allow multiple MO processes to run; see below") + + ("logs", + "duplicates the logs to stdout") + + ("instance,i", + po::value()->implicit_value(""), + "use the given instance (defaults to last used)") + + ("profile,p", + po::value(), + "use the given profile (defaults to last used)"); + po::options_description options; options.add_options() @@ -530,9 +584,9 @@ std::optional Command::runEarly() return {}; } -bool Command::canForwardToPrimary() const +std::optional Command::runPostApplication(MOApplication& a) { - return false; + return {}; } std::optional Command::runPostMultiProcess(MOMultiProcess&) @@ -545,6 +599,11 @@ std::optional Command::runPostOrganizer(OrganizerCore&) return {}; } +bool Command::canForwardToPrimary() const +{ + return false; +} + const std::wstring& Command::originalCmd() const { return m_original; @@ -740,14 +799,21 @@ std::optional RunCommand::runPostOrganizer(OrganizerCore& core) if (vm()["executable"].as()) { const auto& exes = *core.executablesList(); - auto itor = exes.find(program); + // case sensitive + auto itor = exes.find(program, true); if (itor == exes.end()) { - MOShared::criticalOnTop( - QObject::tr("Executable '%1' not found in instance '%2'.") - .arg(program) - .arg(InstanceManager::singleton().currentInstance()->name())); + // case insensitive + itor = exes.find(program, false); - return 1; + if (itor == exes.end()) { + // not found + MOShared::criticalOnTop( + QObject::tr("Executable '%1' not found in instance '%2'.") + .arg(program) + .arg(InstanceManager::singleton().currentInstance()->name())); + + return 1; + } } p.setFromExecutable(*itor); @@ -763,7 +829,7 @@ std::optional RunCommand::runPostOrganizer(OrganizerCore& core) p.setCurrentDirectory(QString::fromStdString(vm()["cwd"].as())); } - p.setWaitForCompletion(ProcessRunner::ForceWait, UILocker::PreventExit); + p.setWaitForCompletion(ProcessRunner::ForCommandLine, UILocker::PreventExit); const auto r = p.run(); if (r == ProcessRunner::Error) { diff --git a/src/commandline.h b/src/commandline.h index 5dcb8871..6bd57132 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -5,6 +5,7 @@ #include class OrganizerCore; +class MOApplication; class MOMultiProcess; namespace cl @@ -68,6 +69,11 @@ public: // virtual std::optional runEarly(); + // called as soon as the MOApplication has been created, which is also the + // first time where Qt stuff is available + // + virtual std::optional runPostApplication(MOApplication& a); + // called as soon as the multi process checks have confirmed that this is // a primary instance; return something to exit immediately // @@ -270,6 +276,11 @@ public: // std::optional process(const std::wstring& line); + // called as soon as the MOApplication has been created; this handles a few + // global actions and forwards to the command, if any + // + std::optional runPostApplication(MOApplication& a); + // calls Command::runPostMultiProcess() on the command, if any // std::optional runPostMultiProcess(MOMultiProcess& mp); @@ -356,6 +367,8 @@ private: { (m_commands.push_back(std::make_unique()), ...); } + + std::optional runEarly(); }; } // namespace diff --git a/src/executableslist.cpp b/src/executableslist.cpp index 75f29e8f..78aa981d 100644 --- a/src/executableslist.cpp +++ b/src/executableslist.cpp @@ -211,14 +211,22 @@ Executable &ExecutablesList::getByBinary(const QFileInfo &info) throw std::runtime_error("invalid info"); } -ExecutablesList::iterator ExecutablesList::find(const QString &title) +ExecutablesList::iterator ExecutablesList::find(const QString &title, bool ci) { - return std::find_if(begin(), end(), [&](auto&& e) { return e.title() == title; }); + const auto cif = ci ? Qt::CaseInsensitive : Qt::CaseSensitive; + + return std::find_if(begin(), end(), [&](auto&& e) { + return (e.title().compare(title, cif) == 0); + }); } -ExecutablesList::const_iterator ExecutablesList::find(const QString &title) const +ExecutablesList::const_iterator ExecutablesList::find(const QString &title, bool ci) const { - return std::find_if(begin(), end(), [&](auto&& e) { return e.title() == title; }); + const auto cif = ci ? Qt::CaseInsensitive : Qt::CaseSensitive; + + return std::find_if(begin(), end(), [&](auto&& e) { + return (e.title().compare(title, cif) == 0); + }); } bool ExecutablesList::titleExists(const QString &title) const diff --git a/src/executableslist.h b/src/executableslist.h index a18042db..97d39500 100644 --- a/src/executableslist.h +++ b/src/executableslist.h @@ -148,8 +148,8 @@ public: /** * @brief returns an iterator for the given executable by title, or end() */ - iterator find(const QString &title); - const_iterator find(const QString &title) const; + iterator find(const QString &title, bool caseSensitive=true); + const_iterator find(const QString &title, bool caseSensitive=true) const; /** * @brief determine if an executable exists diff --git a/src/filetree.cpp b/src/filetree.cpp index f46216b5..7bc4d09c 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -202,7 +202,7 @@ void FileTree::open(FileTreeItem* item) m_core.processRunner() .setFromFile(m_tree->window(), targetInfo) .setHooked(false) - .setWaitForCompletion(ProcessRunner::Refresh) + .setWaitForCompletion(ProcessRunner::TriggerRefresh) .run(); } @@ -226,7 +226,7 @@ void FileTree::openHooked(FileTreeItem* item) m_core.processRunner() .setFromFile(m_tree->window(), targetInfo) .setHooked(true) - .setWaitForCompletion(ProcessRunner::Refresh) + .setWaitForCompletion(ProcessRunner::TriggerRefresh) .run(); } diff --git a/src/loglist.cpp b/src/loglist.cpp index 7f8f05bd..bea7363f 100644 --- a/src/loglist.cpp +++ b/src/loglist.cpp @@ -20,12 +20,18 @@ along with Mod Organizer. If not, see . #include "loglist.h" #include "organizercore.h" #include "copyeventfilter.h" +#include "env.h" using namespace MOBase; static LogModel* g_instance = nullptr; const std::size_t MaxLines = 1000; +static std::unique_ptr m_console; +static bool m_stdout = false; +static std::mutex m_stdoutMutex; + + LogModel::LogModel() { } @@ -324,6 +330,21 @@ void qtLogCallback( } } +void logToStdout(bool b) +{ + m_stdout = b; + + // logging to stdout is already set up in uibase by log::createDefault(), + // all it needs is to redirect stdout to the console, which is done by + // creating an env::Console object + + if (m_stdout) { + m_console.reset(new env::Console); + } else { + m_console.reset(); + } +} + void initLogging() { LogModel::create(); diff --git a/src/loglist.h b/src/loglist.h index d502f193..df7c1467 100644 --- a/src/loglist.h +++ b/src/loglist.h @@ -87,6 +87,7 @@ private: }; +void logToStdout(bool b); void initLogging(); bool setLogDirectory(const QString& dir); diff --git a/src/main.cpp b/src/main.cpp index ba808db3..0cd9ba26 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,7 +15,16 @@ using namespace MOBase; thread_local LPTOP_LEVEL_EXCEPTION_FILTER g_prevExceptionFilter = nullptr; thread_local std::terminate_handler g_prevTerminateHandler = nullptr; +int run(int argc, char *argv[]); + int main(int argc, char *argv[]) +{ + const int r = run(argc, argv); + std::cout << "mod organizer done\n"; + return r; +} + +int run(int argc, char *argv[]) { MOShared::SetThisThreadName("main"); setExceptionHandlers(); @@ -34,6 +43,12 @@ int main(int argc, char *argv[]) MOApplication app(argc, argv); + // check if the command line wants to run something right now + if (auto r=cl.runPostApplication(app)) { + return *r; + } + + // check if there's another process running MOMultiProcess multiProcess(cl.multiple()); @@ -95,6 +110,9 @@ int main(int argc, char *argv[]) cl.clear(); continue; + } else if (r != 0) { + // something failed, quit + return r; } } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a2b12686..a79aca2f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1486,7 +1486,7 @@ void MainWindow::startExeAction() m_OrganizerCore.processRunner() .setFromExecutable(*itor) - .setWaitForCompletion(ProcessRunner::Refresh) + .setWaitForCompletion(ProcessRunner::TriggerRefresh) .run(); } @@ -2033,7 +2033,7 @@ void MainWindow::on_startButton_clicked() m_OrganizerCore.processRunner() .setFromExecutable(*selectedExecutable) - .setWaitForCompletion(ProcessRunner::Refresh) + .setWaitForCompletion(ProcessRunner::TriggerRefresh) .run(); } diff --git a/src/moapplication.cpp b/src/moapplication.cpp index 01d75ad1..da45e26c 100644 --- a/src/moapplication.cpp +++ b/src/moapplication.cpp @@ -395,7 +395,7 @@ void MOApplication::externalMessage(const QString& message) if(moshortcut.hasExecutable()) { m_core->processRunner() .setFromShortcut(moshortcut) - .setWaitForCompletion(ProcessRunner::Refresh) + .setWaitForCompletion(ProcessRunner::TriggerRefresh) .run(); } } else if (isNxmLink(message)) { diff --git a/src/organizercore.cpp b/src/organizercore.cpp index fe17ee9a..c7c1f828 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1897,8 +1897,6 @@ bool OrganizerCore::beforeRun( void OrganizerCore::afterRun(const QFileInfo& binary, DWORD exitCode) { - refreshDirectoryStructure(); - // need to remove our stored load order because it may be outdated if a // foreign tool changed the file time. After removing that file, // refreshESPList will use the file time as the order @@ -1913,7 +1911,7 @@ void OrganizerCore::afterRun(const QFileInfo& binary, DWORD exitCode) savePluginList(); cycleDiagnostics(); - //These callbacks should not fiddle with directoy structure and ESPs. + //These callbacks should not fiddle with directory structure and ESPs. m_FinishedRun(binary.absoluteFilePath(), exitCode); } diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index acfb8404..fe3d502c 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -193,7 +193,7 @@ bool OrganizerProxy::waitForApplication(HANDLE handle, bool refresh, LPDWORD exi ProcessRunner::WaitFlags waitFlags = ProcessRunner::ForceWait; if (refresh) { - waitFlags |= ProcessRunner::Refresh; + waitFlags |= ProcessRunner::TriggerRefresh; } const auto r = runner diff --git a/src/processrunner.cpp b/src/processrunner.cpp index cdcbfa16..c932ce44 100644 --- a/src/processrunner.cpp +++ b/src/processrunner.cpp @@ -521,6 +521,13 @@ ProcessRunner& ProcessRunner::setWaitForCompletion( { m_waitFlags = flags; m_lockReason = reason; + + if (m_waitFlags.testFlag(WaitForRefresh) && !m_waitFlags.testFlag(TriggerRefresh)) { + log::warn( + "process runner: WaitForRefresh without TriggerRefresh " + "makes no sense, will be ignored"); + } + return *this; } @@ -825,8 +832,8 @@ bool ProcessRunner::shouldRefresh(Results r) const // 2) the mod info dialog is not set up to deal with refreshes, so that // it will crash because the old DirectoryEntry's are still being used // in the list - if (!m_waitFlags.testFlag(Refresh)) { - log::debug("not refreshing because the flag isn't set"); + if (!m_waitFlags.testFlag(TriggerRefresh)) { + log::debug("process runner: not refreshing because the flag isn't set"); return false; } @@ -834,13 +841,13 @@ bool ProcessRunner::shouldRefresh(Results r) const { case Completed: { - log::debug("refreshing because the process completed"); + log::debug("process runner: refreshing because the process completed"); return true; } case ForceUnlocked: { - log::debug("refreshing because the ui was force unlocked"); + log::debug("process runner: refreshing because the ui was force unlocked"); return true; } @@ -891,7 +898,10 @@ ProcessRunner::Results ProcessRunner::postRun() if (!lockEnabled) { // disabling locking is like clicking on unlock immediately - log::debug("not waiting for process because locking is disabled"); + log::debug( + "process runner: not waiting for process because " + "locking is disabled"); + return ForceUnlocked; } } @@ -917,8 +927,24 @@ ProcessRunner::Results ProcessRunner::postRun() } if (shouldRefresh(r)) { + QEventLoop loop; + const bool wait = m_waitFlags.testFlag(WaitForRefresh); + + if (wait) { + QObject::connect( + &m_core, &OrganizerCore::directoryStructureReady, + &loop, &QEventLoop::quit, + Qt::ConnectionType::QueuedConnection); + } + m_core.afterRun(m_sp.binary, m_exitCode); - } + + if (wait) { + log::debug("process runner: waiting until refresh finishes"); + loop.exec(); + log::debug("process runner: refresh is done"); + } +} return r; } diff --git a/src/processrunner.h b/src/processrunner.h index d8ff0227..b8c8935c 100644 --- a/src/processrunner.h +++ b/src/processrunner.h @@ -39,12 +39,30 @@ public: { NoFlags = 0x00, - // the ui will be refreshed once the process has completed - Refresh = 0x01, + // the directory structure will be refreshed once the process has completed + TriggerRefresh = 0x01, // the process will be waited for even if locking is disabled or the // process is not hooked ForceWait = 0x02, + + // only valid with TriggerRefresh; run() will block until the refresh has + // completed + WaitForRefresh = 0x04, + + // combination of flags used to run programs from the command line + // + // 1) TriggerRefresh: MO must refresh after running the program because + // programs can modify files behind its back; for example, external + // LOOT will modify loadorder.txt, so MO must read it back or it will + // write back the old order when exiting + // + // 2) WaitForRefresh: refreshing is asynchronous, so the refresh must + // complete before MO exits or stale data might be written to disk + // + // 3) ForceWait: MO must wait for the program to finish even if locking the + // ui is disabled + ForCommandLine = TriggerRefresh | WaitForRefresh | ForceWait }; using WaitFlags = QFlags; -- cgit v1.3.1 From 6feab2ea8f96704e44a14c212a635dc17f4ba71e Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 17 Jan 2021 21:26:21 -0500 Subject: moved criticalOnTop() to uibase changed all calls to reportError(), which now uses the main window if it exists as a parent, or calls criticalOnTop() added errors when running something from the command line for a different instance/profile removed unused crap --- src/CMakeLists.txt | 3 - src/commandline.cpp | 7 +- src/instancemanager.cpp | 9 ++- src/moapplication.cpp | 33 +++++++-- src/organizercore.cpp | 5 +- src/profile.cpp | 1 - src/shared/error_report.cpp | 64 ----------------- src/shared/error_report.h | 44 ------------ src/shared/leaktrace.cpp | 68 ------------------ src/shared/leaktrace.h | 24 ------- src/shared/stackdata.cpp | 169 -------------------------------------------- src/shared/stackdata.h | 50 ------------- 12 files changed, 37 insertions(+), 440 deletions(-) delete mode 100644 src/shared/error_report.cpp delete mode 100644 src/shared/error_report.h delete mode 100644 src/shared/leaktrace.cpp delete mode 100644 src/shared/leaktrace.h delete mode 100644 src/shared/stackdata.cpp delete mode 100644 src/shared/stackdata.h (limited to 'src/moapplication.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a5d15740..e8c52721 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -212,12 +212,9 @@ add_filter(NAME src/utilities GROUPS shared/appconfig bbcode csvbuilder - shared/error_report - shared/leaktrace persistentcookiejar serverinfo spawn - shared/stackdata shared/util usvfsconnector shared/windows_error diff --git a/src/commandline.cpp b/src/commandline.cpp index 97d54b92..477bfba1 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -5,7 +5,6 @@ #include "multiprocess.h" #include "loglist.h" #include "shared/util.h" -#include "shared/error_report.h" #include "shared/appconfig.h" #include #include @@ -807,7 +806,7 @@ std::optional RunCommand::runPostOrganizer(OrganizerCore& core) if (itor == exes.end()) { // not found - MOShared::criticalOnTop( + reportError( QObject::tr("Executable '%1' not found in instance '%2'.") .arg(program) .arg(InstanceManager::singleton().currentInstance()->name())); @@ -833,7 +832,7 @@ std::optional RunCommand::runPostOrganizer(OrganizerCore& core) const auto r = p.run(); if (r == ProcessRunner::Error) { - MOShared::criticalOnTop( + reportError( QObject::tr("Failed to run '%1'. The logs might have more information.").arg(program)); return 1; @@ -842,7 +841,7 @@ std::optional RunCommand::runPostOrganizer(OrganizerCore& core) return 0; } catch (const std::exception &e) { - MOShared::criticalOnTop( + reportError( QObject::tr("Failed to run '%1'. The logs might have more information. %2") .arg(program).arg(e.what())); diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index a9905b75..8add4af8 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -28,7 +28,6 @@ along with Mod Organizer. If not, see . #include "createinstancedialogpages.h" #include "shared/appconfig.h" #include "shared/util.h" -#include "shared/error_report.h" #include #include #include @@ -896,7 +895,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc) // unreadable ini, there's not much that can be done, select another // instance - MOShared::criticalOnTop( + reportError( QObject::tr("Cannot open instance '%1', failed to read INI file %2.") .arg(instance.name()).arg(instance.iniPath())); @@ -911,7 +910,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc) // // ask the user for the game managed by this instance - MOShared::criticalOnTop( + reportError( QObject::tr( "Cannot open instance '%1', the managed game was not found in the INI " "file %2. Select the game managed by this instance.") @@ -925,7 +924,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc) // there is no plugin that can handle the game name/directory from the // ini, so this instance is unusable - MOShared::criticalOnTop( + reportError( QObject::tr( "Cannot open instance '%1', the game plugin '%2' doesn't exist. It " "may have been deleted by an antivirus. Select another instance.") @@ -939,7 +938,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc) // the game directory doesn't exist or the plugin doesn't recognize it; // ask the user for the game managed by this instance - MOShared::criticalOnTop( + reportError( QObject::tr( "Cannot open instance '%1', the game directory '%2' doesn't exist or " "the game plugin '%3' doesn't recognize it. Select the game managed " diff --git a/src/moapplication.cpp b/src/moapplication.cpp index da45e26c..91cac2b1 100644 --- a/src/moapplication.cpp +++ b/src/moapplication.cpp @@ -32,7 +32,6 @@ along with Mod Organizer. If not, see . #include "sanitychecks.h" #include "mainwindow.h" #include "messagedialog.h" -#include "shared/error_report.h" #include "shared/util.h" #include #include @@ -204,7 +203,7 @@ int MOApplication::setup(MOMultiProcess& multiProcess) setProperty("dataPath", dataPath); if (!setLogDirectory(dataPath)) { - reportError("Failed to create log folder"); + reportError(tr("Failed to create log folder.")); InstanceManager::singleton().clearCurrentInstance(); return 1; } @@ -271,7 +270,7 @@ int MOApplication::setup(MOMultiProcess& multiProcess) m_core.reset(new OrganizerCore(*m_settings)); if (!m_core->bootstrap()) { - reportError("failed to set up data paths"); + reportError(tr("Failed to set up data paths.")); InstanceManager::singleton().clearCurrentInstance(); return 1; } @@ -412,6 +411,30 @@ void MOApplication::externalMessage(const QString& message) return; } + if (auto i=cl.instance()) { + const auto ci = InstanceManager::singleton().currentInstance(); + + if (*i != ci->name()) { + reportError(tr( + "This shortcut or command line is for instance '%1', but the current " + "instance is '%2'.") + .arg(*i).arg(ci->name())); + + return; + } + } + + if (auto p=cl.profile()) { + if (*p != m_core->profileName()) { + reportError(tr( + "This shortcut or command line is for profile '%1', but the current " + "profile is '%2'.") + .arg(*p).arg(m_core->profileName())); + + return; + } + } + cl.runPostOrganizer(*m_core); } } @@ -436,11 +459,11 @@ std::unique_ptr MOApplication::getCurrentInstance() m.clearOverrides(); if (m.hasAnyInstances()) { - MOShared::criticalOnTop(QObject::tr( + reportError(QObject::tr( "Instance at '%1' not found. Select another instance.") .arg(currentInstance->directory())); } else { - MOShared::criticalOnTop(QObject::tr( + reportError(QObject::tr( "Instance at '%1' not found. You must create a new instance") .arg(currentInstance->directory())); } diff --git a/src/organizercore.cpp b/src/organizercore.cpp index c7c1f828..d3e817a5 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -37,7 +37,6 @@ #include "shared/filesorigin.h" #include "shared/fileentry.h" #include "shared/util.h" -#include "shared/error_report.h" #include #include @@ -562,7 +561,7 @@ void OrganizerCore::setCurrentProfile(const QString &profileName) log::error("picked profile '{}' instead", QDir(profileDir).dirName()); - MOShared::criticalOnTop( + reportError( tr("The selected profile '%1' does not exist. The profile '%2' will be used instead") .arg(profileName).arg(QDir(profileDir).dirName())); } @@ -812,7 +811,7 @@ ModInfo::Ptr OrganizerCore::installDownload(int index, int priority) } } } catch (const std::exception &e) { - reportError(e.what()); + reportError(QString(e.what())); } return nullptr; diff --git a/src/profile.cpp b/src/profile.cpp index 3cc1a2a3..695cd3ae 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -22,7 +22,6 @@ along with Mod Organizer. If not, see . #include "modinfo.h" #include "settings.h" #include -#include "shared/error_report.h" #include "shared/appconfig.h" #include #include diff --git a/src/shared/error_report.cpp b/src/shared/error_report.cpp deleted file mode 100644 index 09fdcb49..00000000 --- a/src/shared/error_report.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#include "error_report.h" -#include -#include - -namespace MOShared { - -void reportError(LPCSTR format, ...) -{ - char buffer[1025]; - memset(buffer, 0, sizeof(char) * 1025); - - va_list argList; - va_start(argList, format); - - vsnprintf(buffer, 1024, format, argList); - va_end(argList); - - MessageBoxA(nullptr, buffer, "Error", MB_OK | MB_ICONERROR); -} - -void reportError(LPCWSTR format, ...) -{ - WCHAR buffer[1025]; - memset(buffer, 0, sizeof(WCHAR) * 1025); - - va_list argList; - va_start(argList, format); - - _vsnwprintf_s(buffer, 1024, format, argList); - va_end(argList); - - MessageBoxW(nullptr, buffer, L"Error", MB_OK | MB_ICONERROR); -} - -void criticalOnTop(const QString& message) -{ - QMessageBox mb(QMessageBox::Critical, QObject::tr("Mod Organizer"), message); - - mb.show(); - mb.activateWindow(); - mb.raise(); - mb.exec(); -} - -} // namespace MOShared diff --git a/src/shared/error_report.h b/src/shared/error_report.h deleted file mode 100644 index 9343d3da..00000000 --- a/src/shared/error_report.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#ifndef MODORGANIZER_SHARED_ERROR_REPORT_INCLUDED -#define MODORGANIZER_SHARED_ERROR_REPORT_INCLUDED - -#include -#define WIN32_LEAN_AND_MEAN -#include -#include - -namespace MOShared -{ - -void reportError(LPCSTR format, ...); -void reportError(LPCWSTR format, ...); - -// shows a critical message box that's raised to the top of the zorder, useful -// for messages without a main window, which sometimes makes them pop up behind -// all other windows -// -// the dialog is not topmost, it's just raised once when shown -// -void criticalOnTop(const QString& message); - -} // namespace MOShared - -#endif // MODORGANIZER_SHARED_ERROR_REPORT_INCLUDED diff --git a/src/shared/leaktrace.cpp b/src/shared/leaktrace.cpp deleted file mode 100644 index 466162a4..00000000 --- a/src/shared/leaktrace.cpp +++ /dev/null @@ -1,68 +0,0 @@ -//disable warning messages 4302 , 4311 and 4312 -#pragma warning( disable : 4302 ) -#pragma warning( disable : 4311 ) -#pragma warning( disable : 4312 ) - -#include "leaktrace.h" -#include "stackdata.h" -#include -#include -#include -#include -#include -#include -#include - - -using namespace MOShared; - - -static struct __TraceData { - void regTrace(void *pointer, const char *functionName, int line) { - m_Traces[reinterpret_cast(pointer)] = StackData(functionName, line); - } - void deregTrace(void *pointer) { - auto iter = m_Traces.find(reinterpret_cast(pointer)); - if (iter != m_Traces.end()) { - m_Traces.erase(iter); - } - } - - ~__TraceData() { - std::map > result; - for (auto iter = m_Traces.begin(); iter != m_Traces.end(); ++iter) { - result[iter->second].push_back(iter->first); - } - for (auto iter = result.begin(); iter != result.end(); ++iter) { - printf("-----------------------------------\n" - "%d objects not freed, allocated at:\n%s", - static_cast(iter->second.size()), iter->first.toString().c_str()); - printf("Addresses: "); - for (int i = 0; - i < (std::min)(5, static_cast(iter->second.size())); ++i) { - printf("%p, ", reinterpret_cast(iter->second[i])); - } - printf("\n"); - } - } - - std::map m_Traces; - -} __trace; - - -void LeakTrace::TraceAlloc(void *ptr, const char *functionName, int line) -{ - __trace.regTrace(ptr, functionName, line); -} - -void LeakTrace::TraceDealloc(void *ptr) -{ - __trace.deregTrace(ptr); -} - -//re-enable warning messages 4302 , 4311 and 4312 -#pragma warning( default : 4302 ) -#pragma warning( default : 4311 ) -#pragma warning( default : 4312 ) - diff --git a/src/shared/leaktrace.h b/src/shared/leaktrace.h deleted file mode 100644 index 4985925e..00000000 --- a/src/shared/leaktrace.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef LEAKTRACE_H -#define LEAKTRACE_H - - -namespace LeakTrace { - -void TraceAlloc(void *ptr, const char *functionName, int line); -void TraceDealloc(void *ptr); - -}; - -#ifdef TRACE_LEAKS - -#define LEAK_TRACE LeakTrace::TraceAlloc(this, __FUNCTION__, __LINE__) -#define LEAK_UNTRACE LeakTrace::TraceDealloc(this) - -#else // TRACE_LEAKS - -#define LEAK_TRACE -#define LEAK_UNTRACE - -#endif // TRACE_LEAKS - -#endif // LEAKTRACE_H diff --git a/src/shared/stackdata.cpp b/src/shared/stackdata.cpp deleted file mode 100644 index b336593a..00000000 --- a/src/shared/stackdata.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include "stackdata.h" - -#include "util.h" -#include -#include -#include -#include -#include "error_report.h" -#include - - -using namespace MOShared; - -#if defined _MSC_VER - -static void initDbgIfNecess() -{ - HANDLE process = ::GetCurrentProcess(); - static std::set initialized; - if (initialized.find(::GetCurrentProcessId()) == initialized.end()) { - static bool firstCall = true; - if (firstCall) { - ::SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); - firstCall = false; - } - if (!::SymInitialize(process, NULL, TRUE)) { - printf("failed to initialize symbols: %lu", ::GetLastError()); - } - initialized.insert(::GetCurrentProcessId()); - } -} - - - -StackData::StackData() - : m_Count(0) - , m_Function() - , m_Line(-1) -{ - initTrace(); -} - -StackData::StackData(const char *function, int line) - : m_Count(0) - , m_Function(function) - , m_Line(line) -{ - -} - -std::string StackData::toString() const { - initDbgIfNecess(); - - char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; - PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; - symbol->SizeOfStruct = sizeof(SYMBOL_INFO); - symbol->MaxNameLen = MAX_SYM_NAME; - - std::ostringstream stackStream; - - if (m_Function.length() > 0) { - stackStream << "[" << m_Function << ":" << m_Line << "]\n"; - } - - for(unsigned int i = 0; i < m_Count; ++i) { - DWORD64 displacement = 0; - if (!::SymFromAddr(::GetCurrentProcess(), (DWORD64)m_Stack[i], &displacement, symbol)) { - stackStream << m_Count - i - 1 << ": [" << m_Stack[i] << "]\n"; - } else { - stackStream << m_Count - i - 1 << ": " << symbol->Name << "\n"; - } - } - return stackStream.str(); -} - -void StackData::load_modules(HANDLE process, DWORD processID) { - HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processID); - if (snap == INVALID_HANDLE_VALUE) - return; - - MODULEENTRY32 entry; - entry.dwSize = sizeof(entry); - - if (Module32First(snap, &entry)) { - do { - std::string fileName = ToString(entry.szExePath, false); - std::string moduleName = ToString(entry.szModule, false); - SymLoadModule64(process, NULL, fileName.c_str(), moduleName.c_str(), (DWORD64) entry.modBaseAddr, entry.modBaseSize); - } while (Module32Next(snap, &entry)); - } - CloseHandle(snap); -} - -#pragma warning( disable : 4748 ) - -void StackData::initTrace() { -#ifdef _X86_ - load_modules(::GetCurrentProcess(), ::GetCurrentProcessId()); - CONTEXT context; - std::memset(&context, 0, sizeof(CONTEXT)); - context.ContextFlags = CONTEXT_CONTROL; - //Why only for 64 bit? -#if BOOST_ARCH_X86_64 || defined(__clang__) - ::RtlCaptureContext(&context); -#else - __asm - { - Label: - mov [context.Ebp], ebp; - mov [context.Esp], esp; - mov eax, [Label]; - mov [context.Eip], eax; - } -#endif - - STACKFRAME64 stackFrame; - ::ZeroMemory(&stackFrame, sizeof(STACKFRAME64)); - stackFrame.AddrPC.Offset = context.Eip; - stackFrame.AddrPC.Mode = AddrModeFlat; - stackFrame.AddrFrame.Offset = context.Ebp; - stackFrame.AddrFrame.Mode = AddrModeFlat; - stackFrame.AddrStack.Offset = context.Esp; - stackFrame.AddrStack.Mode = AddrModeFlat; - m_Count = 0; - while (m_Count < FRAMES_TO_CAPTURE) { - if (!StackWalk64(IMAGE_FILE_MACHINE_I386, ::GetCurrentProcess(), - ::GetCurrentThread(), &stackFrame, &context, NULL, - &SymFunctionTableAccess64, &SymGetModuleBase64, NULL)) { - break; - } - - if (stackFrame.AddrPC.Offset == 0) { - continue; - break; - } - - m_Stack[m_Count++] = reinterpret_cast(stackFrame.AddrPC.Offset); - } -#endif -} - - -bool MOShared::operator==(const StackData &LHS, const StackData &RHS) { - if (LHS.m_Count != RHS.m_Count) { - return false; - } else { - for (int i = 0; i < LHS.m_Count; ++i) { - if (LHS.m_Stack[i] != RHS.m_Stack[i]) { - return false; - } - } - } - return true; -} - - -bool MOShared::operator<(const StackData &LHS, const StackData &RHS) { - if (LHS.m_Count != RHS.m_Count) { - return LHS.m_Count < RHS.m_Count; - } else { - for (int i = 0; i < LHS.m_Count; ++i) { - if (LHS.m_Stack[i] != RHS.m_Stack[i]) { - return LHS.m_Stack[i] < RHS.m_Stack[i]; - } - } - } - return false; -} -#endif diff --git a/src/shared/stackdata.h b/src/shared/stackdata.h deleted file mode 100644 index 7151699c..00000000 --- a/src/shared/stackdata.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef STACKDATA_H -#define STACKDATA_H - - -#include -#include - - -namespace MOShared { - - -class StackData { - friend bool operator==(const StackData &LHS, const StackData &RHS); - friend bool operator<(const StackData &LHS, const StackData &RHS); -public: - - StackData(); - StackData(const char *function, int line); - - std::string toString() const; - -private: - - void load_modules(HANDLE process, DWORD processID); - - void initTrace(); - -private: - - static const int FRAMES_TO_SKIP = 1; - static const int FRAMES_TO_CAPTURE = 20; - -private: - - LPVOID m_Stack[FRAMES_TO_CAPTURE]; - USHORT m_Count; - std::string m_Function; - int m_Line; - -}; - - -bool operator==(const StackData &LHS, const StackData &RHS); - -bool operator<(const StackData &LHS, const StackData &RHS); - -} // namespace MOShared - - -#endif // STACKDATA_H -- cgit v1.3.1