From e4ae41e3a29fc3e4714e1c3f8c9f071768d17b68 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 10 Jan 2021 22:28:38 -0500 Subject: fixed all commands being legacy split running command into pre and post organizer --- src/commandline.cpp | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) (limited to 'src/commandline.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp index 8c722990..c31b726f 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -49,6 +49,7 @@ std::string table( CommandLine::CommandLine() + : m_command(nullptr) { createOptions(); m_commands.push_back(std::make_unique()); @@ -57,7 +58,7 @@ CommandLine::CommandLine() m_commands.push_back(std::make_unique()); } -std::optional CommandLine::run(const std::wstring& line) +std::optional CommandLine::process(const std::wstring& line) { try { @@ -125,8 +126,10 @@ std::optional CommandLine::run(const std::wstring& line) } } - // run the command - return c->run(line, m_vm, opts); + c->process(line, m_vm, opts); + m_command = c.get(); + + return m_command->runPreOrganizer(); } catch(po::error& e) { @@ -201,7 +204,7 @@ std::optional CommandLine::run(const std::wstring& line) } } -std::optional CommandLine::setupCore(OrganizerCore& organizer) const +std::optional CommandLine::run(OrganizerCore& organizer) const { if (m_shortcut.isValid()) { if (m_shortcut.hasExecutable()) { @@ -247,6 +250,8 @@ std::optional CommandLine::setupCore(OrganizerCore& organizer) const QObject::tr("failed to start application: %1").arg(e.what())); return 1; } + } else if (m_command) { + return m_command->runPostOrganizer(); } return {}; @@ -437,7 +442,7 @@ po::positional_options_description Command::positional() const bool Command::legacy() const { - return true; + return false; } std::string Command::getUsageLine() const @@ -463,8 +468,7 @@ po::positional_options_description Command::getPositional() const return {}; } - -std::optional Command::run( +void Command::process( const std::wstring& originalLine, po::variables_map vm, std::vector untouched) @@ -472,8 +476,16 @@ std::optional Command::run( m_original = originalLine; m_vm = vm; m_untouched = untouched; +} + +std::optional Command::runPreOrganizer() +{ + return {}; +} - return doRun(); +std::optional Command::runPostOrganizer() +{ + return {}; } const std::wstring& Command::originalCmd() const @@ -507,7 +519,7 @@ Command::Meta CrashDumpCommand::meta() const return {"crashdump", "writes a crashdump for a running process of MO"}; } -std::optional CrashDumpCommand::doRun() +std::optional CrashDumpCommand::runPreOrganizer() { env::Console console; @@ -537,7 +549,7 @@ bool LaunchCommand::legacy() const return true; } -std::optional LaunchCommand::doRun() +std::optional LaunchCommand::runPreOrganizer() { // needs at least the working directory and process name if (untouched().size() < 2) { @@ -643,7 +655,7 @@ Command::Meta ExeCommand::meta() const return {"exe", "launches a configured executable"}; } -std::optional ExeCommand::doRun() +std::optional ExeCommand::runPostOrganizer() { const auto exe = vm()["exe-name"].as(); const auto args = vm()["arguments"].as(); @@ -665,7 +677,7 @@ Command::Meta RunCommand::meta() const return {"run", "launches an arbitrary program"}; } -std::optional RunCommand::doRun() +std::optional RunCommand::runPostOrganizer() { std::cout << "not implemented\n"; return {}; -- cgit v1.3.1 From aa7a552654ba3b218b87397dedc9e5a66d2f6701 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 10 Jan 2021 23:08:07 -0500 Subject: implemented exe command better error when cwd doesn't exist --- src/commandline.cpp | 47 +++++++++++++++++++++++++++++++++++++++-------- src/commandline.h | 6 +++--- src/spawn.cpp | 9 +++++++-- 3 files changed, 49 insertions(+), 13 deletions(-) (limited to 'src/commandline.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp index c31b726f..596ece68 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -2,6 +2,7 @@ #include "env.h" #include "organizercore.h" #include "shared/util.h" +#include "shared/error_report.h" #include #include @@ -251,7 +252,7 @@ std::optional CommandLine::run(OrganizerCore& organizer) const return 1; } } else if (m_command) { - return m_command->runPostOrganizer(); + return m_command->runPostOrganizer(organizer); } return {}; @@ -483,7 +484,7 @@ std::optional Command::runPreOrganizer() return {}; } -std::optional Command::runPostOrganizer() +std::optional Command::runPostOrganizer(OrganizerCore&) { return {}; } @@ -655,13 +656,43 @@ Command::Meta ExeCommand::meta() const return {"exe", "launches a configured executable"}; } -std::optional ExeCommand::runPostOrganizer() +std::optional ExeCommand::runPostOrganizer(OrganizerCore& organizer) { - const auto exe = vm()["exe-name"].as(); - const auto args = vm()["arguments"].as(); - const auto cwd = vm()["cwd"].as(); + const auto exe = QString::fromStdString(vm()["exe-name"].as()); - std::cout << "not implemented\n"; + const auto& exes = *organizer.executablesList(); + + auto itor = exes.find(exe); + if (itor == exes.end()) { + MOShared::criticalOnTop(QObject::tr("Executable '%1' not found.").arg(exe)); + return 1; + } + + try { + // make sure MO doesn't exit even if locking is disabled, ForceWait and + // PreventExit will do that + auto p = organizer.processRunner(); + + p.setFromExecutable(*itor); + + if (vm().count("arguments")) { + p.setArguments(QString::fromStdString(vm()["arguments"].as())); + } + + if (vm().count("cwd")) { + p.setCurrentDirectory(QString::fromStdString(vm()["cwd"].as())); + } + + p.setWaitForCompletion(ProcessRunner::ForceWait, UILocker::PreventExit); + p.run(); + + return 0; + } + catch (const std::exception &e) { + reportError( + QObject::tr("failed to start shortcut: %1").arg(e.what())); + return 1; + } return 0; } @@ -677,7 +708,7 @@ Command::Meta RunCommand::meta() const return {"run", "launches an arbitrary program"}; } -std::optional RunCommand::runPostOrganizer() +std::optional RunCommand::runPostOrganizer(OrganizerCore&) { std::cout << "not implemented\n"; return {}; diff --git a/src/commandline.h b/src/commandline.h index 14a453b2..d2cfbfa1 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -60,7 +60,7 @@ public: // // virtual std::optional runPreOrganizer(); - virtual std::optional runPostOrganizer(); + virtual std::optional runPostOrganizer(OrganizerCore& organizer); protected: // meta information about this command, returned by derived classes @@ -160,7 +160,7 @@ protected: po::options_description getInternalOptions() const override; po::positional_options_description getPositional() const override; Meta meta() const override; - std::optional runPostOrganizer() override; + std::optional runPostOrganizer(OrganizerCore& organizer) override; }; @@ -171,7 +171,7 @@ class RunCommand : public Command protected: po::options_description getOptions() const; Meta meta() const override; - std::optional runPostOrganizer() override; + std::optional runPostOrganizer(OrganizerCore& organizer) override; }; diff --git a/src/spawn.cpp b/src/spawn.cpp index a9ecb61e..68526165 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -164,9 +164,14 @@ QString makeContent(const SpawnParameters& sp, DWORD code) } else if (code == ERROR_FILE_NOT_FOUND) { return QObject::tr("The file '%1' does not exist.") .arg(QDir::toNativeSeparators(sp.binary.absoluteFilePath())); - } else { - return QString::fromStdWString(formatSystemMessage(code)); + } else if (code == ERROR_DIRECTORY) { + if (!sp.currentDirectory.exists()) { + return QObject::tr("The working directory '%1' does not exist.") + .arg(QDir::toNativeSeparators(sp.currentDirectory.absolutePath())); + } } + + return QString::fromStdWString(formatSystemMessage(code)); } QMessageBox::StandardButton badSteamReg( -- cgit v1.3.1 From 7e8018481284ff9ac1915425e9ed94d532ab33db Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 11 Jan 2021 00:01:50 -0500 Subject: merged exe and run commands, added -e flag instead added dialog when selected profile doesn't exist, this can happen with -p on the command line --- src/commandline.cpp | 92 +++++++++++++++++++++++++-------------------------- src/commandline.h | 15 ++------- src/organizercore.cpp | 5 +++ 3 files changed, 53 insertions(+), 59 deletions(-) (limited to 'src/commandline.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp index 596ece68..fd0f6cb5 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -1,6 +1,7 @@ #include "commandline.h" #include "env.h" #include "organizercore.h" +#include "instancemanager.h" #include "shared/util.h" #include "shared/error_report.h" #include @@ -53,7 +54,6 @@ 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()); m_commands.push_back(std::make_unique()); @@ -616,64 +616,74 @@ LPCWSTR LaunchCommand::UntouchedCommandLineArguments( } -std::string ExeCommand::getUsageLine() const +std::string RunCommand::getUsageLine() const { - return "[options] exe-name"; + return "[options] program"; } -po::options_description ExeCommand::getVisibleOptions() const +po::options_description RunCommand::getVisibleOptions() const { po::options_description d; d.add_options() - ("arguments,a", po::value()->default_value(""), "override arguments") - ("cwd,c", po::value()->default_value(""), "override working directory"); + ("executable,e", po::value()->default_value(false)->zero_tokens(), "the program is a configured executable name") + ("arguments,a", po::value(), "override arguments") + ("cwd,c", po::value(), "override working directory"); return d; } -po::options_description ExeCommand::getInternalOptions() const +po::options_description RunCommand::getInternalOptions() const { po::options_description d; d.add_options() - ("exe-name", po::value()->required(), "executable name"); + ("program", po::value()->required(), "program or executable name"); return d; } -po::positional_options_description ExeCommand::getPositional() const +po::positional_options_description RunCommand::getPositional() const { po::positional_options_description d; - d.add("exe-name", 1); + d.add("program", 1); return d; } -Command::Meta ExeCommand::meta() const +Command::Meta RunCommand::meta() const { - return {"exe", "launches a configured executable"}; + return {"run", "runs a program, file or a configured executable"}; } -std::optional ExeCommand::runPostOrganizer(OrganizerCore& organizer) +std::optional RunCommand::runPostOrganizer(OrganizerCore& organizer) { - const auto exe = QString::fromStdString(vm()["exe-name"].as()); - - const auto& exes = *organizer.executablesList(); - - auto itor = exes.find(exe); - if (itor == exes.end()) { - MOShared::criticalOnTop(QObject::tr("Executable '%1' not found.").arg(exe)); - return 1; - } + const auto program = QString::fromStdString(vm()["program"].as()); - try { + try + { // make sure MO doesn't exit even if locking is disabled, ForceWait and // PreventExit will do that auto p = organizer.processRunner(); - p.setFromExecutable(*itor); + if (vm()["executable"].as()) { + const auto& exes = *organizer.executablesList(); + + auto itor = exes.find(program); + if (itor == exes.end()) { + MOShared::criticalOnTop( + QObject::tr("Executable '%1' not found in instance '%2'.") + .arg(program) + .arg(InstanceManager::singleton().currentInstance()->name())); + + return 1; + } + + p.setFromExecutable(*itor); + } else { + p.setFromFile(nullptr, QFileInfo(program)); + } if (vm().count("arguments")) { p.setArguments(QString::fromStdString(vm()["arguments"].as())); @@ -684,34 +694,24 @@ std::optional ExeCommand::runPostOrganizer(OrganizerCore& organizer) } p.setWaitForCompletion(ProcessRunner::ForceWait, UILocker::PreventExit); - p.run(); + + const auto r = p.run(); + if (r == ProcessRunner::Error) { + MOShared::criticalOnTop( + QObject::tr("Failed to run '%1'. The logs might have more information.").arg(program)); + + return 1; + } return 0; } catch (const std::exception &e) { - reportError( - QObject::tr("failed to start shortcut: %1").arg(e.what())); + MOShared::criticalOnTop( + QObject::tr("Failed to run '%1'. The logs might have more information. %2") + .arg(program).arg(e.what())); + return 1; } - - return 0; -} - - -po::options_description RunCommand::getOptions() const -{ - return {}; -} - -Command::Meta RunCommand::meta() const -{ - return {"run", "launches an arbitrary program"}; -} - -std::optional RunCommand::runPostOrganizer(OrganizerCore&) -{ - std::cout << "not implemented\n"; - return {}; } } // namespace diff --git a/src/commandline.h b/src/commandline.h index d2cfbfa1..d0d62a72 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -150,9 +150,9 @@ protected: }; -// runs a configured executable +// runs a program or an executable // -class ExeCommand : public Command +class RunCommand : public Command { protected: std::string getUsageLine() const override; @@ -164,17 +164,6 @@ protected: }; -// runs an arbitrary executable -// -class RunCommand : public Command -{ -protected: - po::options_description getOptions() const; - Meta meta() const override; - std::optional runPostOrganizer(OrganizerCore& organizer) override; -}; - - // parses the command line and runs any given command // // the command line used to support a few commands but with no real conventions; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index e95aa565..5d079f89 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -37,6 +37,7 @@ #include "shared/filesorigin.h" #include "shared/fileentry.h" #include "shared/util.h" +#include "shared/error_report.h" #include #include @@ -579,6 +580,10 @@ void OrganizerCore::setCurrentProfile(const QString &profileName) profileBaseDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot).at(0)); log::error("picked profile '{}' instead", QDir(profileDir).dirName()); + + MOShared::criticalOnTop( + tr("The selected profile '%1' does not exist. The profile '%2' will be used instead") + .arg(profileName).arg(QDir(profileDir).dirName())); } // Keep the old profile to emit signal-changed: -- 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/commandline.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 65a9e51e0ed8169bbf2a176ce147ca5a9ae09a6f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 11 Jan 2021 01:43:48 -0500 Subject: added refresh command added more help for run --- src/commandline.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++-------- src/commandline.h | 26 +++++++++++++++++++ 2 files changed, 90 insertions(+), 11 deletions(-) (limited to 'src/commandline.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp index b6210463..01ee866e 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -60,6 +60,7 @@ CommandLine::CommandLine() add< RunCommand, ReloadPluginCommand, + RefreshCommand, CrashDumpCommand, LaunchCommand>(); } @@ -322,8 +323,14 @@ std::string CommandLine::usage(const Command* c) const if (c) { oss - << " ModOrganizer.exe [global-options] " << c->usageLine() << "\n" - << "\n" + << " ModOrganizer.exe [global-options] " << c->usageLine() << "\n\n"; + + const std::string more = c->moreInfo(); + if (!more.empty()) { + oss << more << "\n\n"; + } + + oss << "Command options:\n" << c->visibleOptions() << "\n"; } else { @@ -335,6 +342,11 @@ std::string CommandLine::usage(const Command* c) const // name and description for all commands std::vector> v; for (auto&& c : m_commands) { + // don't show legacy commands + if (c->legacy()) { + continue; + } + v.push_back({c->name(), c->description()}); } @@ -441,6 +453,11 @@ std::string Command::usageLine() const return name() + " " + getUsageLine(); } +std::string Command::moreInfo() const +{ + return getMoreInfo(); +} + po::options_description Command::allOptions() const { po::options_description d; @@ -476,6 +493,11 @@ std::string Command::getUsageLine() const return "[options]"; } +std::string Command::getMoreInfo() const +{ + return ""; +} + po::options_description Command::getVisibleOptions() const { // no-op @@ -653,7 +675,16 @@ LPCWSTR LaunchCommand::UntouchedCommandLineArguments( std::string RunCommand::getUsageLine() const { - return "[options] program"; + return "[options] NAME"; +} + +std::string RunCommand::getMoreInfo() const +{ + return + "Runs a program or a file with the virtual filesystem. If NAME is a path\n" + "to a non-executable file, the program that is associated with the file\n" + "extension is run instead. With -e, NAME must refer to the name of an\n" + "executable in the instance (for example, \"SKSE\")."; } po::options_description RunCommand::getVisibleOptions() const @@ -661,7 +692,7 @@ po::options_description RunCommand::getVisibleOptions() const po::options_description d; d.add_options() - ("executable,e", po::value()->default_value(false)->zero_tokens(), "the program is a configured executable name") + ("executable,e", po::value()->default_value(false)->zero_tokens(), "the name is a configured executable name") ("arguments,a", po::value(), "override arguments") ("cwd,c", po::value(), "override working directory"); @@ -673,7 +704,7 @@ po::options_description RunCommand::getInternalOptions() const po::options_description d; d.add_options() - ("program", po::value()->required(), "program or executable name"); + ("NAME", po::value()->required(), "program or executable name"); return d; } @@ -682,7 +713,7 @@ po::positional_options_description RunCommand::getPositional() const { po::positional_options_description d; - d.add("program", 1); + d.add("NAME", 1); return d; } @@ -692,9 +723,14 @@ Command::Meta RunCommand::meta() const return {"run", "runs a program, file or a configured executable"}; } +bool RunCommand::canForwardToPrimary() const +{ + return true; +} + std::optional RunCommand::runPostOrganizer(OrganizerCore& core) { - const auto program = QString::fromStdString(vm()["program"].as()); + const auto program = QString::fromStdString(vm()["NAME"].as()); try { @@ -753,7 +789,7 @@ std::optional RunCommand::runPostOrganizer(OrganizerCore& core) std::string ReloadPluginCommand::getUsageLine() const { - return "plugin-name"; + return "PLUGIN"; } po::options_description ReloadPluginCommand::getInternalOptions() const @@ -761,7 +797,7 @@ po::options_description ReloadPluginCommand::getInternalOptions() const po::options_description d; d.add_options() - ("plugin-name", po::value()->required(), "plugin name"); + ("PLUGIN", po::value()->required(), "plugin name"); return d; } @@ -770,7 +806,7 @@ po::positional_options_description ReloadPluginCommand::getPositional() const { po::positional_options_description d; - d.add("plugin-name", 1); + d.add("PLUGIN", 1); return d; } @@ -787,7 +823,7 @@ bool ReloadPluginCommand::canForwardToPrimary() const std::optional ReloadPluginCommand::runPostOrganizer(OrganizerCore& core) { - const QString name = QString::fromStdString(vm()["plugin-name"].as()); + const QString name = QString::fromStdString(vm()["PLUGIN"].as()); QString filepath = QDir( qApp->applicationDirPath() + "/" + @@ -800,4 +836,21 @@ std::optional ReloadPluginCommand::runPostOrganizer(OrganizerCore& core) return {}; } + +Command::Meta RefreshCommand::meta() const +{ + return {"refresh", "refreshes MO (same as F5)"}; +} + +bool RefreshCommand::canForwardToPrimary() const +{ + return true; +} + +std::optional RefreshCommand::runPostOrganizer(OrganizerCore& core) +{ + core.refresh(); + return {}; +} + } // namespace diff --git a/src/commandline.h b/src/commandline.h index 6f96d724..9cad4c71 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -32,6 +32,10 @@ public: // std::string usageLine() const; + // shown after the usage line + // + std::string moreInfo() const; + // returns all options for this command, including hidden ones; used to parse // the command line @@ -77,6 +81,10 @@ protected: // virtual std::string getUsageLine() const; + // returns text shown after the usage line + // + virtual std::string getMoreInfo() const; + // returns visible options specific to this command // virtual po::options_description getVisibleOptions() const; @@ -159,10 +167,14 @@ class RunCommand : public Command { protected: std::string getUsageLine() const override; + std::string getMoreInfo() const override; + po::options_description getVisibleOptions() 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; }; @@ -173,14 +185,28 @@ 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; }; +// refreshes mo +// +class RefreshCommand : public Command +{ +protected: + Meta meta() const override; + bool canForwardToPrimary() const override; + std::optional runPostOrganizer(OrganizerCore& core) override; +}; + + + // parses the command line and runs any given command // // the command line used to support a few commands but with no real conventions; -- cgit v1.3.1 From 06109a568be5d31ebb3303b4702dec7185f22066 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 11 Jan 2021 01:54:25 -0500 Subject: put more stuff in Meta to simplify --- src/commandline.cpp | 67 ++++++++++++++++++++++++++--------------------------- src/commandline.h | 25 +++++++------------- 2 files changed, 42 insertions(+), 50 deletions(-) (limited to 'src/commandline.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp index 01ee866e..10d0f1fd 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -438,6 +438,11 @@ std::string CommandLine::more() const } +Command::Meta::Meta(std::string n, std::string d, std::string u, std::string m) + : name(n), description(d), usage(u), more(m) +{ +} + std::string Command::name() const { return meta().name; @@ -450,12 +455,12 @@ std::string Command::description() const std::string Command::usageLine() const { - return name() + " " + getUsageLine(); + return name() + " " + meta().usage; } std::string Command::moreInfo() const { - return getMoreInfo(); + return meta().more; } po::options_description Command::allOptions() const @@ -488,16 +493,6 @@ bool Command::legacy() const return false; } -std::string Command::getUsageLine() const -{ - return "[options]"; -} - -std::string Command::getMoreInfo() const -{ - return ""; -} - po::options_description Command::getVisibleOptions() const { // no-op @@ -574,7 +569,12 @@ po::options_description CrashDumpCommand::getVisibleOptions() const Command::Meta CrashDumpCommand::meta() const { - return {"crashdump", "writes a crashdump for a running process of MO"}; + return { + "crashdump", + "writes a crashdump for a running process of MO", + "[options]", + "" + }; } std::optional CrashDumpCommand::runEarly() @@ -599,7 +599,7 @@ std::optional CrashDumpCommand::runEarly() Command::Meta LaunchCommand::meta() const { - return {"launch", "(internal, do not use)"}; + return {"launch", "(internal, do not use)", "", ""}; } bool LaunchCommand::legacy() const @@ -673,18 +673,18 @@ LPCWSTR LaunchCommand::UntouchedCommandLineArguments( } -std::string RunCommand::getUsageLine() const +Command::Meta RunCommand::meta() const { - return "[options] NAME"; -} + return { + "run", + "runs a program, file or a configured executable", + "[options] NAME", -std::string RunCommand::getMoreInfo() const -{ - return "Runs a program or a file with the virtual filesystem. If NAME is a path\n" "to a non-executable file, the program that is associated with the file\n" "extension is run instead. With -e, NAME must refer to the name of an\n" - "executable in the instance (for example, \"SKSE\")."; + "executable in the instance (for example, \"SKSE\")." + }; } po::options_description RunCommand::getVisibleOptions() const @@ -718,11 +718,6 @@ po::positional_options_description RunCommand::getPositional() const return d; } -Command::Meta RunCommand::meta() const -{ - return {"run", "runs a program, file or a configured executable"}; -} - bool RunCommand::canForwardToPrimary() const { return true; @@ -787,9 +782,14 @@ std::optional RunCommand::runPostOrganizer(OrganizerCore& core) -std::string ReloadPluginCommand::getUsageLine() const +Command::Meta ReloadPluginCommand::meta() const { - return "PLUGIN"; + return { + "reload-plugin", + "reloads the given plugin", + "PLUGIN", + "" + }; } po::options_description ReloadPluginCommand::getInternalOptions() const @@ -811,11 +811,6 @@ po::positional_options_description ReloadPluginCommand::getPositional() const return d; } -Command::Meta ReloadPluginCommand::meta() const -{ - return {"reload-plugin", "reloads the given plugin"}; -} - bool ReloadPluginCommand::canForwardToPrimary() const { return true; @@ -839,7 +834,11 @@ std::optional ReloadPluginCommand::runPostOrganizer(OrganizerCore& core) Command::Meta RefreshCommand::meta() const { - return {"refresh", "refreshes MO (same as F5)"}; + return { + "refresh", + "refreshes MO (same as F5)", + "", "" + }; } bool RefreshCommand::canForwardToPrimary() const diff --git a/src/commandline.h b/src/commandline.h index 9cad4c71..a92ef094 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -28,11 +28,11 @@ public: // std::string description() const; - // usage line, puts together the name and whatever getUsageLine() returns + // usage line, puts together the name and whatever meta().usage is // std::string usageLine() const; - // shown after the usage line + // shown after the usage line; this is meta().more // std::string moreInfo() const; @@ -74,16 +74,12 @@ protected: // struct Meta { - std::string name, description; - }; - - // returns the usage line for this command, not including the name - // - virtual std::string getUsageLine() const; + std::string name, description, usage, more; - // returns text shown after the usage line - // - virtual std::string getMoreInfo() const; + Meta( + std::string name, std::string description, + std::string usage, std::string more); + }; // returns visible options specific to this command // @@ -166,13 +162,11 @@ protected: class RunCommand : public Command { protected: - std::string getUsageLine() const override; - std::string getMoreInfo() const override; + Meta meta() const override; po::options_description getVisibleOptions() 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; @@ -184,11 +178,10 @@ protected: class ReloadPluginCommand : public Command { protected: - std::string getUsageLine() const override; + Meta meta() 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; -- cgit v1.3.1 From 16ba428e44e22680ae255d46f5e928a4e6ff591a Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 11 Jan 2021 02:07:34 -0500 Subject: fixed runPostMultiProcess() not being called comments --- src/commandline.cpp | 8 ++++-- src/commandline.h | 75 +++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 65 insertions(+), 18 deletions(-) (limited to 'src/commandline.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp index 10d0f1fd..1b7c7b0e 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -133,7 +133,7 @@ std::optional CommandLine::process(const std::wstring& line) } } - c->process(line, m_vm, opts); + c->set(line, m_vm, opts); m_command = c.get(); return m_command->runEarly(); @@ -228,6 +228,10 @@ bool CommandLine::forwardToPrimary(MOMultiProcess& multiProcess) std::optional CommandLine::runPostMultiProcess(MOMultiProcess& mp) { + if (m_command) { + return m_command->runPostMultiProcess(mp); + } + return {}; } @@ -511,7 +515,7 @@ po::positional_options_description Command::getPositional() const return {}; } -void Command::process( +void Command::set( const std::wstring& originalLine, po::variables_map vm, std::vector untouched) diff --git a/src/commandline.h b/src/commandline.h index a92ef094..5dcb8871 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -55,20 +55,43 @@ public: // virtual bool legacy() const; + // remembers the given values // - // - void process( + void set( const std::wstring& originalLine, po::variables_map vm, std::vector untouched); - // + + // called as soon as the command line has been parsed; return something to + // exit immediately // virtual std::optional runEarly(); - virtual bool canForwardToPrimary() const; + + // called as soon as the multi process checks have confirmed that this is + // a primary instance; return something to exit immediately + // virtual std::optional runPostMultiProcess(MOMultiProcess& mp); + + // called after the OrganizerCore has been initialized; return something to + // exit immediatley + // virtual std::optional runPostOrganizer(OrganizerCore& core); + // whether this command can be forwarded to the primary instance if one is + // already running + // + // if an instance is already running and this returns true, the whole command + // line is sent as a message to the primary instance, which will construct a + // new CommandLine and call runPostOrganizer() with it + // + // if an instance is already running and this returns false, the "an instance + // is already running" error dialog will be shown + // + // if this is the primary instance, this function is not called + // + virtual bool canForwardToPrimary() const; + protected: // meta information about this command, returned by derived classes // @@ -81,6 +104,12 @@ protected: std::string usage, std::string more); }; + + // meta + // + virtual Meta meta() const = 0; + + // returns visible options specific to this command // virtual po::options_description getVisibleOptions() const; @@ -94,11 +123,6 @@ protected: virtual po::positional_options_description getPositional() const; - // meta - // - virtual Meta meta() const = 0; - - // returns the original command line // const std::wstring& originalCmd() const; @@ -238,23 +262,41 @@ class CommandLine public: CommandLine(); - // parses the given command line and executes the appropriate command, if - // any + // parses the given command line and calls runEarly() on the appropriate + // command, if any // // returns an empty optional if execution should continue, or a return code // if MO must quit // std::optional process(const std::wstring& line); - // handles moshortcut, nxm links and starting processes + // calls Command::runPostMultiProcess() on the command, if any // - // returns an empty optional if execution should continue, or a return code - // if MO must quit - // - bool forwardToPrimary(MOMultiProcess& multiProcess); std::optional runPostMultiProcess(MOMultiProcess& mp); + + // calls Command::runPostOrganizer() on the command, if any + // + // if MO wasn't invoked with a valid command, this also handles moshortcut, + // nxm links and starting processes + // std::optional runPostOrganizer(OrganizerCore& core); + // called when this instance is not the primary one + // + // if a command was executed and the command returns true for + // canForwardToPrimary(), this forwards the command line as an external + // message and returns true + // + // if the command returns false for canForwardToPrimary(), returns false + // + // if there was no valid command on the command line, this also forwards + // moshortcut and nxm links + // + // if there was no command, moshortcut or nxm links, this returns false, which + // will end up displaying an error message about an instance already running + // + bool forwardToPrimary(MOMultiProcess& multiProcess); + // clears parsed options, used when MO is "restarted" so the options aren't // processed again @@ -287,6 +329,7 @@ public: std::optional nxmLink() const; // returns the executable/binary, if any + // std::optional executable() const; // returns the list of arguments, excluding moshortcut or executable name; -- 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/commandline.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/commandline.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