diff options
| author | isanae <14251494+isanae@users.noreply.github.com> | 2020-07-18 00:35:26 -0400 |
|---|---|---|
| committer | isanae <14251494+isanae@users.noreply.github.com> | 2020-11-03 11:39:01 -0500 |
| commit | b103f297752b170ee4ade6e0e9085c6d31c020ca (patch) | |
| tree | be9c58357e069e4b5e7add1fc24016a0d44d3283 | |
| parent | a7d4f2a0c40d35d547e93994be3f92b6d43a0833 (diff) | |
added --multiple to allow launching multiple instances
| -rw-r--r-- | src/main.cpp | 47 | ||||
| -rw-r--r-- | src/singleinstance.cpp | 23 | ||||
| -rw-r--r-- | src/singleinstance.h | 62 |
3 files changed, 85 insertions, 47 deletions
diff --git a/src/main.cpp b/src/main.cpp index a81ae962..7130d15c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -112,19 +112,19 @@ bool createAndMakeWritable(const std::wstring &subPath) { } } -bool bootstrap() +void purgeOldFiles() { - // remove the temporary backup directory in case we're restarting after an update + // remove the temporary backup directory in case we're restarting after an + // update QString backupDirectory = qApp->applicationDirPath() + "/update_backup"; if (QDir(backupDirectory).exists()) { shellDelete(QStringList(backupDirectory)); } // cycle log file - removeOldFiles(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::logPath()), - "usvfs*.log", 5, QDir::Name); - - return true; + removeOldFiles( + qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::logPath()), + "usvfs*.log", 5, QDir::Name); } thread_local LPTOP_LEVEL_EXCEPTION_FILTER prevUnhandledExceptionFilter = nullptr; @@ -533,8 +533,9 @@ static QString getVersionDisplayString() } -int runApplication(MOApplication &application, SingleInstance &instance, - const QString &splashPath) +int runApplication( + MOApplication &application, QStringList& arguments, + SingleInstance &instance, const QString &splashPath) { TimeThis tt("runApplication() to exec()"); @@ -555,17 +556,13 @@ int runApplication(MOApplication &application, SingleInstance &instance, log::debug("this is a portable instance"); } - if (!bootstrap()) { - reportError("failed to set up data paths"); - InstanceManager::instance().clearCurrentInstance(); - return 1; + if (!instance.secondary()) { + purgeOldFiles(); } QWindowsWindowFunctions::setWindowActivationBehavior( QWindowsWindowFunctions::AlwaysActivateWindow); - QStringList arguments = application.arguments(); - try { log::info("working directory: {}", QDir::currentPath()); @@ -574,6 +571,11 @@ int runApplication(MOApplication &application, SingleInstance &instance, log::debug("using ini at '{}'", settings.filename()); + if (instance.secondary()) { + log::debug("another instance of MO is running but --multiple was given"); + } + + // global crashDumpType sits in OrganizerCore to make a bit less ugly to // update it when the settings are changed during runtime OrganizerCore::setGlobalCrashDumpsType(settings.diagnostics().crashDumpsType()); @@ -929,16 +931,23 @@ int main(int argc, char *argv[]) setupPath(); - bool forcePrimary = false; + + SingleInstance::Flags siFlags = SingleInstance::NoFlags; + if (arguments.contains("update")) { arguments.removeAll("update"); - forcePrimary = true; + siFlags |= SingleInstance::ForcePrimary; + } + + if (arguments.contains("--multiple")) { + arguments.removeAll("--multiple"); + siFlags |= SingleInstance::AllowMultiple; } MOShortcut moshortcut{ arguments.size() > 1 ? arguments.at(1) : "" }; - SingleInstance instance(forcePrimary); - if (!instance.primaryInstance()) { + SingleInstance instance(siFlags); + if (instance.ephemeral()) { if (moshortcut || arguments.size() > 1 && OrganizerCore::isNxmLink(arguments.at(1))) { @@ -998,7 +1007,7 @@ int main(int argc, char *argv[]) tt.stop(); - const int result = runApplication(application, instance, splash); + const int result = runApplication(application, arguments, instance, splash); if (result != RestartExitCode) { return result; } diff --git a/src/singleinstance.cpp b/src/singleinstance.cpp index aa62d40a..d38095df 100644 --- a/src/singleinstance.cpp +++ b/src/singleinstance.cpp @@ -28,33 +28,38 @@ static const int s_Timeout = 5000; using MOBase::reportError;
-SingleInstance::SingleInstance(bool forcePrimary, QObject *parent) :
- QObject(parent), m_PrimaryInstance(false)
+SingleInstance::SingleInstance(Flags flags, QObject *parent) :
+ QObject(parent), m_Ephemeral(false), m_OwnsSM(false)
{
m_SharedMem.setKey(s_Key);
+
if (!m_SharedMem.create(1)) {
- if (forcePrimary) {
+ if (flags.testFlag(ForcePrimary)) {
while (m_SharedMem.error() == QSharedMemory::AlreadyExists) {
Sleep(500);
if (m_SharedMem.create(1)) {
- m_PrimaryInstance = true;
+ m_OwnsSM = true;
break;
}
}
}
if (m_SharedMem.error() == QSharedMemory::AlreadyExists) {
- m_SharedMem.attach();
- m_PrimaryInstance = false;
+ if (!flags.testFlag(AllowMultiple)) {
+ m_SharedMem.attach();
+ m_Ephemeral = true;
+ }
}
+
if ((m_SharedMem.error() != QSharedMemory::NoError) &&
(m_SharedMem.error() != QSharedMemory::AlreadyExists)) {
throw MOBase::MyException(tr("SHM error: %1").arg(m_SharedMem.errorString()));
}
} else {
- m_PrimaryInstance = true;
+ m_OwnsSM = true;
}
- if (m_PrimaryInstance) {
+
+ if (m_OwnsSM) {
connect(&m_Server, SIGNAL(newConnection()), this, SLOT(receiveMessage()), Qt::QueuedConnection);
// has to be called before listen
m_Server.setSocketOptions(QLocalServer::WorldAccessOption);
@@ -65,7 +70,7 @@ SingleInstance::SingleInstance(bool forcePrimary, QObject *parent) : void SingleInstance::sendMessage(const QString &message)
{
- if (m_PrimaryInstance) {
+ if (m_OwnsSM) {
// nobody there to receive the message
return;
}
diff --git a/src/singleinstance.h b/src/singleinstance.h index 14d49d6d..5c7cdf85 100644 --- a/src/singleinstance.h +++ b/src/singleinstance.h @@ -27,8 +27,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. /**
* used to ensure only a single instance of Mod Organizer is started and to
- * allow secondary instances to send messages to the primary (visible) one. This way,
- * secondary instances can start a download in the primary one
+ * allow ephemeral instances to send messages to the primary (visible) one.
+ * This way, other instances can start a download in the primary one
**/
class SingleInstance : public QObject
{
@@ -36,24 +36,46 @@ class SingleInstance : public QObject Q_OBJECT
public:
+ enum Flag
+ {
+ NoFlags = 0x00,
- /**
- * @brief constructor
- *
- * @param forcePrimary if true, this will be treated as the primary instance even
- * if another instance is running. This is used after an update since
- * the other instance is assumed to be in the process of quitting
- * @param parent parent object
- * @todo the forcePrimary parameter makes no sense. The second instance after an update
- * needs to delete the files from before the update so the first instance needs to quit
- * first anyway
- **/
- explicit SingleInstance(bool forcePrimary, QObject *parent = 0);
+
+ // when set, this will be treated as the primary instance even if
+ // another instance is running. This is used after an update since the
+ // other instance is assumed to be in the process of quitting
+ //
+ // todo: this makes no sense. The second instance after an update needs
+ // to delete the files from before the update so the first instance
+ // needs to quit first anyway
+ ForcePrimary = 0x01,
+
+ // if another instance is running, run this one disconnected from the
+ // shared memory
+ AllowMultiple = 0x02
+ };
+
+ using Flags = QFlags<Flag>;
+
+
+ explicit SingleInstance(Flags flags, QObject *parent = 0);
/**
- * @return true if this is the primary instance (the one that gets to display a UI)
+ * @return true if this instance's job is to forward data to the primary
+ * instance through shared memory
**/
- bool primaryInstance() const { return m_PrimaryInstance; }
+ bool ephemeral() const
+ {
+ return m_Ephemeral;
+ }
+
+ // returns true if this is not the primary instance, but was allowed because
+ // of the AllowMultiple flag
+ //
+ bool secondary() const
+ {
+ return !m_Ephemeral && !m_OwnsSM;
+ }
/**
* send a message to the primary instance. This can be used to transmit download urls
@@ -65,7 +87,7 @@ public: signals:
/**
- * @brief emitted when a secondary instance has sent a message (to us)
+ * @brief emitted when an ephemeral instance has sent a message (to us)
*
* @param message the message we received
**/
@@ -78,11 +100,13 @@ private slots: void receiveMessage();
private:
-
- bool m_PrimaryInstance;
+ bool m_Ephemeral;
+ bool m_OwnsSM;
QSharedMemory m_SharedMem;
QLocalServer m_Server;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(SingleInstance::Flags);
+
#endif // SINGLEINSTANCE_H
|
