From b103f297752b170ee4ade6e0e9085c6d31c020ca Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 18 Jul 2020 00:35:26 -0400 Subject: added --multiple to allow launching multiple instances --- src/main.cpp | 47 ++++++++++++++++++++++---------------- src/singleinstance.cpp | 23 +++++++++++-------- src/singleinstance.h | 62 ++++++++++++++++++++++++++++++++++---------------- 3 files changed, 85 insertions(+), 47 deletions(-) (limited to 'src') 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 . /** * 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; + + + 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 -- cgit v1.3.1