Raise existing instance

Closes #193
This commit is contained in:
VukoDrakkeinen 2016-07-25 06:41:13 +02:00 committed by Anton Gulenko
parent 9f819061cd
commit d1310b3337
5 changed files with 87 additions and 1 deletions

View File

@ -198,6 +198,7 @@ if(WITH_TESTS)
endif(WITH_TESTS)
find_package(Qt5Core 5.2 REQUIRED)
find_package(Qt5Network 5.2 REQUIRED)
find_package(Qt5Concurrent 5.2 REQUIRED)
find_package(Qt5Widgets 5.2 REQUIRED)
find_package(Qt5Test 5.2 REQUIRED)

View File

@ -195,6 +195,7 @@ target_link_libraries(keepassx_core
${YUBIKEY_LIBRARIES}
zxcvbn
Qt5::Core
Qt5::Network
Qt5::Concurrent
Qt5::Widgets
${GCRYPT_LIBRARIES}

View File

@ -22,6 +22,9 @@
#include <QAbstractNativeEventFilter>
#include <QFileOpenEvent>
#include <QSocketNotifier>
#include <QLockFile>
#include <QStandardPaths>
#include <QtNetwork/QLocalSocket>
#include "autotype/AutoType.h"
@ -76,6 +79,8 @@ Application::Application(int& argc, char** argv)
#ifdef Q_OS_UNIX
, m_unixSignalNotifier(nullptr)
#endif
, alreadyRunning(false)
, lock(nullptr)
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX)
installNativeEventFilter(new XcbEventFilter());
@ -85,6 +90,58 @@ Application::Application(int& argc, char** argv)
#if defined(Q_OS_UNIX)
registerUnixSignals();
#endif
QString userName = qgetenv("USER");
if (userName.isEmpty()) {
userName = qgetenv("USERNAME");
}
QString identifier = "keepassx2";
if (!userName.isEmpty()) {
identifier.append("-");
identifier.append(userName);
}
QString socketName = identifier + ".socket";
QString lockName = identifier + ".lock";
// According to documentation we should use RuntimeLocation on *nixes, but even Qt doesn't respect
// this and creates sockets in TempLocation, so let's be consistent.
lock = new QLockFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/" + lockName);
lock->setStaleLockTime(0);
lock->tryLock();
switch (lock->error()) {
case QLockFile::NoError:
server.setSocketOptions(QLocalServer::UserAccessOption);
server.listen(socketName);
connect(&server, SIGNAL(newConnection()), this, SIGNAL(anotherInstanceStarted()));
break;
case QLockFile::LockFailedError: {
alreadyRunning = true;
// notify the other instance
// try several times, in case the other instance is still starting up
QLocalSocket client;
for (int i = 0; i < 3; i++) {
client.connectToServer(socketName);
if (client.waitForConnected(150)) {
client.abort();
break;
}
}
break;
}
default:
qWarning() << QCoreApplication::translate("Main",
"The lock file could not be created. Single-instance mode disabled.")
.toUtf8().constData();
}
}
Application::~Application()
{
server.close();
if (lock) {
lock->unlock();
delete lock;
}
}
QWidget* Application::mainWindow() const
@ -171,3 +228,9 @@ void Application::quitBySignal()
static_cast<MainWindow*>(m_mainWindow)->appExit();
}
#endif
bool Application::isAlreadyRunning() const
{
return alreadyRunning;
}

View File

@ -20,6 +20,8 @@
#define KEEPASSX_APPLICATION_H
#include <QApplication>
#include <QtNetwork/QLocalServer>
class QLockFile;
class QSocketNotifier;
@ -30,12 +32,15 @@ class Application : public QApplication
public:
Application(int& argc, char** argv);
QWidget* mainWindow() const;
~Application();
void setMainWindow(QWidget* mainWindow);
bool event(QEvent* event) override;
bool isAlreadyRunning() const;
signals:
void openFile(const QString& filename);
void anotherInstanceStarted();
private slots:
#if defined(Q_OS_UNIX)
@ -54,6 +59,9 @@ private:
static void handleUnixSignal(int sig);
static int unixSignalSocket[2];
#endif
bool alreadyRunning;
QLockFile* lock;
QLocalServer server;
};
#endif // KEEPASSX_APPLICATION_H

View File

@ -56,6 +56,11 @@ int main(int argc, char** argv)
// don't set organizationName as that changes the return value of
// QStandardPaths::writableLocation(QDesktopServices::DataLocation)
if (app.isAlreadyRunning()) {
qWarning() << QCoreApplication::translate("Main", "Another instance of KeePassX 2 is already running.").toUtf8().constData();
return 0;
}
QApplication::setQuitOnLastWindowClosed(false);
if (!Crypto::init()) {
@ -103,6 +108,14 @@ int main(int argc, char** argv)
MainWindow mainWindow;
app.setMainWindow(&mainWindow);
QObject::connect(&app, &Application::anotherInstanceStarted,
[&]() {
mainWindow.ensurePolished();
mainWindow.setWindowState(mainWindow.windowState() & ~Qt::WindowMinimized);
mainWindow.show();
mainWindow.raise();
mainWindow.activateWindow();
});
QObject::connect(&app, SIGNAL(openFile(QString)), &mainWindow, SLOT(openDatabase(QString)));
// start minimized if configured