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/coreencryption.cpp \
|
||||||
src/core/corefile.cpp \
|
src/core/corefile.cpp \
|
||||||
src/core/corestructs.cpp \
|
src/core/corestructs.cpp \
|
||||||
|
src/profilelocker.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
src/audio.h \
|
src/audio.h \
|
||||||
|
@ -454,3 +455,4 @@ HEADERS += \
|
||||||
src/widget/gui.h \
|
src/widget/gui.h \
|
||||||
src/toxme.h \
|
src/toxme.h \
|
||||||
src/misc/qrwidget.h \
|
src/misc/qrwidget.h \
|
||||||
|
src/profilelocker.h
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "src/widget/gui.h"
|
#include "src/widget/gui.h"
|
||||||
#include "src/historykeeper.h"
|
#include "src/historykeeper.h"
|
||||||
#include "src/audio.h"
|
#include "src/audio.h"
|
||||||
|
#include "src/profilelocker.h"
|
||||||
#include "corefile.h"
|
#include "corefile.h"
|
||||||
|
|
||||||
#include <tox/tox.h>
|
#include <tox/tox.h>
|
||||||
|
@ -884,6 +885,18 @@ QByteArray Core::loadToxSave(QString path)
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
loadPath = ""; // if not empty upon return, then user forgot a password and is switching
|
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);
|
QFile configurationFile(path);
|
||||||
qDebug() << "Core::loadConfiguration: reading from " << path;
|
qDebug() << "Core::loadConfiguration: reading from " << path;
|
||||||
|
|
||||||
|
@ -959,8 +972,19 @@ void Core::saveConfiguration()
|
||||||
saveConfiguration(path);
|
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())
|
if (profile.isEmpty())
|
||||||
qDebug() << "Core: creating new Id";
|
qDebug() << "Core: creating new Id";
|
||||||
else
|
else
|
||||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -21,6 +21,7 @@
|
||||||
#include "src/widget/toxuri.h"
|
#include "src/widget/toxuri.h"
|
||||||
#include "src/widget/toxsave.h"
|
#include "src/widget/toxsave.h"
|
||||||
#include "src/autoupdate.h"
|
#include "src/autoupdate.h"
|
||||||
|
#include "src/profilelocker.h"
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
@ -213,6 +214,13 @@ int main(int argc, char *argv[])
|
||||||
ipc.registerEventHandler("save", &toxSaveEventHandler);
|
ipc.registerEventHandler("save", &toxSaveEventHandler);
|
||||||
ipc.registerEventHandler("activate", &toxActivateEventHandler);
|
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)
|
if (parser.positionalArguments().size() > 0)
|
||||||
{
|
{
|
||||||
QString firstParam(parser.positionalArguments()[0]);
|
QString firstParam(parser.positionalArguments()[0]);
|
||||||
|
@ -254,7 +262,7 @@ int main(int argc, char *argv[])
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!ipc.isCurrentOwner())
|
else if (!ipc.isCurrentOwner() && !parser.isSet("p"))
|
||||||
{
|
{
|
||||||
uint32_t dest = 0;
|
uint32_t dest = 0;
|
||||||
if (parser.isSet("p"))
|
if (parser.isSet("p"))
|
||||||
|
|
|
@ -215,8 +215,11 @@ void Settings::load()
|
||||||
setProxyType(s.value("proxyType", static_cast<int>(ProxyType::ptNone)).toInt());
|
setProxyType(s.value("proxyType", static_cast<int>(ProxyType::ptNone)).toInt());
|
||||||
proxyAddr = s.value("proxyAddr", "").toString();
|
proxyAddr = s.value("proxyAddr", "").toString();
|
||||||
proxyPort = s.value("proxyPort", 0).toInt();
|
proxyPort = s.value("proxyPort", 0).toInt();
|
||||||
|
if (currentProfile.isEmpty())
|
||||||
|
{
|
||||||
currentProfile = s.value("currentProfile", "").toString();
|
currentProfile = s.value("currentProfile", "").toString();
|
||||||
currentProfileId = makeProfileId(currentProfile);
|
currentProfileId = makeProfileId(currentProfile);
|
||||||
|
}
|
||||||
autoAwayTime = s.value("autoAwayTime", 10).toInt();
|
autoAwayTime = s.value("autoAwayTime", 10).toInt();
|
||||||
checkUpdates = s.value("checkUpdates", false).toBool();
|
checkUpdates = s.value("checkUpdates", false).toBool();
|
||||||
showWindow = s.value("showWindow", true).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/widget/gui.h"
|
||||||
#include "src/historykeeper.h"
|
#include "src/historykeeper.h"
|
||||||
#include "src/misc/style.h"
|
#include "src/misc/style.h"
|
||||||
|
#include "src/profilelocker.h"
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
|
@ -247,6 +248,13 @@ void ProfileForm::onRenameClicked()
|
||||||
if (!QFile::exists(file) || GUI::askQuestion(tr("Profile already exists", "rename confirm title"),
|
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)))
|
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+Core::TOX_EXT), file);
|
||||||
QFile::rename(dir.filePath(cur+".ini"), dir.filePath(name+".ini"));
|
QFile::rename(dir.filePath(cur+".ini"), dir.filePath(name+".ini"));
|
||||||
bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name);
|
bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user