summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commandline.cpp118
-rw-r--r--src/commandline.h37
-rw-r--r--src/main.cpp34
-rw-r--r--src/messagedialog.cpp22
-rw-r--r--src/moapplication.cpp49
-rw-r--r--src/moapplication.h4
-rw-r--r--src/organizercore.cpp19
-rw-r--r--src/organizercore.h1
8 files changed, 213 insertions, 71 deletions
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 <log.h>
#include <report.h>
@@ -54,9 +56,12 @@ CommandLine::CommandLine()
: m_command(nullptr)
{
createOptions();
- m_commands.push_back(std::make_unique<RunCommand>());
- m_commands.push_back(std::make_unique<CrashDumpCommand>());
- m_commands.push_back(std::make_unique<LaunchCommand>());
+
+ add<
+ RunCommand,
+ ReloadPluginCommand,
+ CrashDumpCommand,
+ LaunchCommand>();
}
std::optional<int> CommandLine::process(const std::wstring& line)
@@ -130,7 +135,7 @@ std::optional<int> 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<int> 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<int> CommandLine::process(const std::wstring& line)
}
}
-std::optional<int> 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<int> CommandLine::runPostMultiProcess(MOMultiProcess& mp)
+{
+ return {};
+}
+
+std::optional<int> 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<int> 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<int> 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<int> 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<int> Command::runPreOrganizer()
+std::optional<int> Command::runEarly()
+{
+ return {};
+}
+
+bool Command::canForwardToPrimary() const
+{
+ return false;
+}
+
+std::optional<int> 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<int> CrashDumpCommand::runPreOrganizer()
+std::optional<int> CrashDumpCommand::runEarly()
{
env::Console console;
@@ -550,7 +585,7 @@ bool LaunchCommand::legacy() const
return true;
}
-std::optional<int> LaunchCommand::runPreOrganizer()
+std::optional<int> 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<int> RunCommand::runPostOrganizer(OrganizerCore& organizer)
+std::optional<int> RunCommand::runPostOrganizer(OrganizerCore& core)
{
const auto program = QString::fromStdString(vm()["program"].as<std::string>());
@@ -665,10 +700,10 @@ std::optional<int> 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<bool>()) {
- 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<int> 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<std::string>()->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<int> ReloadPluginCommand::runPostOrganizer(OrganizerCore& core)
+{
+ const QString name = QString::fromStdString(vm()["plugin-name"].as<std::string>());
+
+ 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 <memory>
class OrganizerCore;
+class MOMultiProcess;
namespace cl
{
@@ -59,8 +60,10 @@ public:
//
//
- virtual std::optional<int> runPreOrganizer();
- virtual std::optional<int> runPostOrganizer(OrganizerCore& organizer);
+ virtual std::optional<int> runEarly();
+ virtual bool canForwardToPrimary() const;
+ virtual std::optional<int> runPostMultiProcess(MOMultiProcess& mp);
+ virtual std::optional<int> 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<int> runPreOrganizer() override;
+ std::optional<int> runEarly() override;
};
@@ -141,7 +144,7 @@ public:
protected:
Meta meta() const override;
- std::optional<int> runPreOrganizer() override;
+ std::optional<int> 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<int> runPostOrganizer(OrganizerCore& organizer) override;
+ std::optional<int> 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<int> 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<int> run(OrganizerCore& organizer) const;
+ bool forwardToPrimary(MOMultiProcess& multiProcess);
+ std::optional<int> runPostMultiProcess(MOMultiProcess& mp);
+ std::optional<int> 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 <class... Ts>
+ void add()
+ {
+ (m_commands.push_back(std::make_unique<Ts>()), ...);
+ }
};
} // 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<QMainWindow*>(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 <http://www.gnu.org/licenses/>.
#include "tutorialmanager.h"
#include "sanitychecks.h"
#include "mainwindow.h"
+#include "messagedialog.h"
#include "shared/error_report.h"
#include "shared/util.h"
#include <iplugingame.h>
@@ -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<Instance> 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<PluginContainer> m_plugins;
std::unique_ptr<OrganizerCore> m_core;
- int doOneRun(MOMultiProcess& multiProcess);
-
+ void externalMessage(const QString& message);
std::unique_ptr<Instance> getCurrentInstance();
std::optional<int> 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();