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') 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