mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Merge branch 'bitg_multiple_instances' into back_in_the_game
This commit is contained in:
commit
dc5f812946
2
qtox.pro
2
qtox.pro
|
@ -430,6 +430,7 @@ SOURCES += \
|
|||
src/core/coreencryption.cpp \
|
||||
src/core/corefile.cpp \
|
||||
src/core/corestructs.cpp \
|
||||
src/profilelocker.cpp
|
||||
|
||||
HEADERS += \
|
||||
src/audio.h \
|
||||
|
@ -454,3 +455,4 @@ HEADERS += \
|
|||
src/widget/gui.h \
|
||||
src/toxme.h \
|
||||
src/misc/qrwidget.h \
|
||||
src/profilelocker.h
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "src/widget/gui.h"
|
||||
#include "src/historykeeper.h"
|
||||
#include "src/audio.h"
|
||||
#include "src/profilelocker.h"
|
||||
#include "corefile.h"
|
||||
|
||||
#include <tox/tox.h>
|
||||
|
@ -884,6 +885,18 @@ QByteArray Core::loadToxSave(QString path)
|
|||
QByteArray data;
|
||||
loadPath = ""; // if not empty upon return, then user forgot a password and is switching
|
||||
|
||||
// If we can't get a lock, then another instance is already using that profile
|
||||
while (!ProfileLocker::lock(QFileInfo(path).baseName()))
|
||||
{
|
||||
qWarning() << "Profile "<<QFileInfo(path).baseName()<<" is already in use, pick another";
|
||||
GUI::showWarning(tr("Profile already in use"),
|
||||
tr("Your profile is already used by another qTox\n"
|
||||
"Please select another profile"));
|
||||
path = Settings::getInstance().askProfiles();
|
||||
Settings::getInstance().switchProfile(QFileInfo(path).baseName());
|
||||
HistoryKeeper::resetInstance();
|
||||
}
|
||||
|
||||
QFile configurationFile(path);
|
||||
qDebug() << "Core::loadConfiguration: reading from " << path;
|
||||
|
||||
|
@ -959,8 +972,19 @@ void Core::saveConfiguration()
|
|||
saveConfiguration(path);
|
||||
}
|
||||
|
||||
void Core::switchConfiguration(const QString& profile)
|
||||
void Core::switchConfiguration(const QString& _profile)
|
||||
{
|
||||
QString profile = QFileInfo(_profile).baseName();
|
||||
// If we can't get a lock, then another instance is already using that profile
|
||||
while (!profile.isEmpty() && !ProfileLocker::lock(profile))
|
||||
{
|
||||
qWarning() << "Profile "<<profile<<" is already in use, pick another";
|
||||
GUI::showWarning(tr("Profile already in use"),
|
||||
tr("Your profile is already used by another qTox instance\n"
|
||||
"Please select another profile"));
|
||||
profile = QFileInfo(Settings::getInstance().askProfiles()).baseName();
|
||||
}
|
||||
|
||||
if (profile.isEmpty())
|
||||
qDebug() << "Core: creating new Id";
|
||||
else
|
||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -21,6 +21,7 @@
|
|||
#include "src/widget/toxuri.h"
|
||||
#include "src/widget/toxsave.h"
|
||||
#include "src/autoupdate.h"
|
||||
#include "src/profilelocker.h"
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <QDateTime>
|
||||
|
@ -213,6 +214,13 @@ int main(int argc, char *argv[])
|
|||
ipc.registerEventHandler("save", &toxSaveEventHandler);
|
||||
ipc.registerEventHandler("activate", &toxActivateEventHandler);
|
||||
|
||||
// If we're the IPC owner and we just started, then
|
||||
// either we're the only running instance or any other instance
|
||||
// is already so frozen it lost ownership.
|
||||
// It's safe to remove any potential stale locks in this situation.
|
||||
if (ipc.isCurrentOwner())
|
||||
ProfileLocker::clearAllLocks();
|
||||
|
||||
if (parser.positionalArguments().size() > 0)
|
||||
{
|
||||
QString firstParam(parser.positionalArguments()[0]);
|
||||
|
@ -254,7 +262,7 @@ int main(int argc, char *argv[])
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else if (!ipc.isCurrentOwner())
|
||||
else if (!ipc.isCurrentOwner() && !parser.isSet("p"))
|
||||
{
|
||||
uint32_t dest = 0;
|
||||
if (parser.isSet("p"))
|
||||
|
|
|
@ -215,8 +215,11 @@ void Settings::load()
|
|||
setProxyType(s.value("proxyType", static_cast<int>(ProxyType::ptNone)).toInt());
|
||||
proxyAddr = s.value("proxyAddr", "").toString();
|
||||
proxyPort = s.value("proxyPort", 0).toInt();
|
||||
currentProfile = s.value("currentProfile", "").toString();
|
||||
currentProfileId = makeProfileId(currentProfile);
|
||||
if (currentProfile.isEmpty())
|
||||
{
|
||||
currentProfile = s.value("currentProfile", "").toString();
|
||||
currentProfileId = makeProfileId(currentProfile);
|
||||
}
|
||||
autoAwayTime = s.value("autoAwayTime", 10).toInt();
|
||||
checkUpdates = s.value("checkUpdates", false).toBool();
|
||||
showWindow = s.value("showWindow", true).toBool();
|
||||
|
|
69
src/profilelocker.cpp
Normal file
69
src/profilelocker.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include "profilelocker.h"
|
||||
#include "src/misc/settings.h"
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
using namespace std;
|
||||
|
||||
unique_ptr<QLockFile> ProfileLocker::lockfile;
|
||||
QString ProfileLocker::curLockName;
|
||||
|
||||
QString ProfileLocker::lockPathFromName(const QString& name)
|
||||
{
|
||||
return Settings::getInstance().getSettingsDirPath()+'/'+name+".lock";
|
||||
}
|
||||
|
||||
bool ProfileLocker::isLockable(QString profile)
|
||||
{
|
||||
// If we already have the lock, it's definitely lockable
|
||||
if (lockfile && curLockName == profile)
|
||||
return true;
|
||||
|
||||
QLockFile newLock(lockPathFromName(profile));
|
||||
return newLock.tryLock();
|
||||
}
|
||||
|
||||
bool ProfileLocker::lock(QString profile)
|
||||
{
|
||||
if (lockfile && curLockName == profile)
|
||||
return true;
|
||||
|
||||
QLockFile* newLock = new QLockFile(lockPathFromName(profile));
|
||||
if (!newLock->tryLock())
|
||||
{
|
||||
delete newLock;
|
||||
return false;
|
||||
}
|
||||
|
||||
unlock();
|
||||
lockfile.reset(newLock);
|
||||
curLockName = profile;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProfileLocker::unlock()
|
||||
{
|
||||
if (!lockfile)
|
||||
return;
|
||||
lockfile->unlock();
|
||||
delete lockfile.release();
|
||||
lockfile = nullptr;
|
||||
curLockName.clear();
|
||||
}
|
||||
|
||||
void ProfileLocker::clearAllLocks()
|
||||
{
|
||||
qDebug() << "ProfileLocker::clearAllLocks: Wiping out all lock files";
|
||||
if (lockfile)
|
||||
unlock();
|
||||
|
||||
QDir dir(Settings::getInstance().getSettingsDirPath());
|
||||
dir.setFilter(QDir::Files);
|
||||
dir.setNameFilters({"*.lock"});
|
||||
QFileInfoList files = dir.entryInfoList();
|
||||
for (QFileInfo fileInfo : files)
|
||||
{
|
||||
QFile file(fileInfo.absoluteFilePath());
|
||||
file.remove();
|
||||
}
|
||||
}
|
40
src/profilelocker.h
Normal file
40
src/profilelocker.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef PROFILELOCKER_H
|
||||
#define PROFILELOCKER_H
|
||||
|
||||
#include <QLockFile>
|
||||
#include <memory>
|
||||
|
||||
/// Locks a Tox profile so that multiple instances can not use the same profile.
|
||||
/// Only one lock can be acquired at the same time, which means
|
||||
/// that there is little need for manually unlocking.
|
||||
/// The current lock will expire if you exit or acquire a new one.
|
||||
class ProfileLocker
|
||||
{
|
||||
private:
|
||||
ProfileLocker()=delete;
|
||||
|
||||
public:
|
||||
/// Checks if a profile is currently locked by *another* instance
|
||||
/// If we own the lock, we consider it lockable
|
||||
/// There is no guarantee that the result will still be valid by the
|
||||
/// time it is returned, this is provided on a best effort basis
|
||||
static bool isLockable(QString profile);
|
||||
/// Tries to acquire the lock on a profile, will not block
|
||||
/// Returns true if we already own the lock
|
||||
static bool lock(QString profile);
|
||||
/// Releases the lock on the current profile
|
||||
static void unlock();
|
||||
/// Releases all locks on all profiles
|
||||
/// DO NOT call unless all we're the only qTox instance
|
||||
/// and we don't hold any lock yet.
|
||||
static void clearAllLocks();
|
||||
|
||||
private:
|
||||
static QString lockPathFromName(const QString& name);
|
||||
|
||||
private:
|
||||
static std::unique_ptr<QLockFile> lockfile;
|
||||
static QString curLockName;
|
||||
};
|
||||
|
||||
#endif // PROFILELOCKER_H
|
|
@ -27,6 +27,7 @@
|
|||
#include "src/widget/gui.h"
|
||||
#include "src/historykeeper.h"
|
||||
#include "src/misc/style.h"
|
||||
#include "src/profilelocker.h"
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QGroupBox>
|
||||
|
@ -247,6 +248,13 @@ void ProfileForm::onRenameClicked()
|
|||
if (!QFile::exists(file) || GUI::askQuestion(tr("Profile already exists", "rename confirm title"),
|
||||
tr("A profile named \"%1\" already exists. Do you want to erase it?", "rename confirm text").arg(cur)))
|
||||
{
|
||||
if (!ProfileLocker::lock(name))
|
||||
{
|
||||
GUI::showWarning(tr("Profile already exists", "rename failed title"),
|
||||
tr("A profile named \"%1\" already exists and is in use.").arg(cur));
|
||||
break;
|
||||
}
|
||||
|
||||
QFile::rename(dir.filePath(cur+Core::TOX_EXT), file);
|
||||
QFile::rename(dir.filePath(cur+".ini"), dir.filePath(name+".ini"));
|
||||
bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name);
|
||||
|
|
Loading…
Reference in New Issue
Block a user