Commit 4c87947d authored by Andreas Traczyk's avatar Andreas Traczyk

mainwindow: detect secondary instances of Jami and restore primary

- connects a qt socket to the primary instance running a local
  server to inform the primary instance that it should restore
  from a possibly minimized state

Change-Id: Iceda1a5a8be9fda2d34d41d9d32b6c02f26d06e3
Gitlab: #480
parent 47ee4c17
......@@ -21,6 +21,7 @@
#include <QApplication>
#include <QFile>
#include <QMessageBox>
#include "callmodel.h"
#include "globalinstances.h"
......@@ -105,13 +106,6 @@ fileDebug(QFile& debugFile)
int
main(int argc, char* argv[])
{
RunGuard guard("680b3e5eaf");
if (!guard.tryToRun())
return 0;
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
char ARG_DISABLE_WEB_SECURITY[] = "--disable-web-security";
int newArgc = argc + 1 + 1;
char** newArgv = new char*[newArgc];
......@@ -127,12 +121,20 @@ main(int argc, char* argv[])
QApplication a(newArgc, newArgv);
QCoreApplication::setApplicationName("Ring");
QCoreApplication::setOrganizationDomain("jami.net");
QCryptographicHash appData(QCryptographicHash::Sha256);
appData.addData(QApplication::applicationName().toUtf8());
appData.addData(QApplication::organizationDomain().toUtf8());
RunGuard guard(appData.result());
if (!guard.tryToRun()) {
return 0;
}
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, false);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QCoreApplication::setOrganizationDomain("jami.net");
QCoreApplication::setApplicationName("Ring");
auto startMinimized = false;
QString uri = "";
......
......@@ -443,7 +443,10 @@
<ClInclude Include="ringthemeutils.h" />
<QtMoc Include="selectareadialog.h">
</QtMoc>
<ClInclude Include="runguard.h" />
<QtMoc Include="runguard.h">
<IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath>
<IncludePath Condition="'$(Configuration)|$(Platform)'=='ReleaseCompile|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)winsparkle\include;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath>
</QtMoc>
<ClInclude Include="settingskey.h" />
<QtMoc Include="smartlistview.h">
<IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName)\.;.\GeneratedFiles;.;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\lrc\src;$(ProjectDir)..\client-windows\winsparkle\include;$(ProjectDir)..\client-windows\qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath>
......
......@@ -359,6 +359,9 @@
<QtMoc Include="downloadmanager.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="runguard.h">
<Filter>Header Files</Filter>
</QtMoc>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">
......@@ -789,8 +792,5 @@
<ClInclude Include="version.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="runguard.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
\ No newline at end of file
// From: https://stackoverflow.com/a/28172162
/***************************************************************************
* Copyright (C) 2019 by Savoir-faire Linux *
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
**************************************************************************/
// Based on: https://stackoverflow.com/a/28172162
#include "runguard.h"
#include <QCryptographicHash>
#include "mainwindow.h"
namespace
{
......@@ -21,59 +40,85 @@ QString generateKeyHash(const QString& key, const QString& salt)
}
RunGuard::RunGuard(const QString& key)
: key(key)
, memLockKey(generateKeyHash(key, "_memLockKey"))
, sharedmemKey(generateKeyHash(key, "_sharedmemKey"))
, sharedMem(sharedmemKey)
, memLock(memLockKey, 1)
{
memLock.acquire();
{
QSharedMemory fix(sharedmemKey); // Fix for *nix: http://habrahabr.ru/post/173281/
fix.attach();
}
memLock.release();
}
: key_(key)
, memLockKey_(generateKeyHash(key, "_memLockKey"))
, sharedmemKey_(generateKeyHash(key, "_sharedmemKey"))
, sharedMem_(sharedmemKey_)
, memLock_(memLockKey_, 1)
{}
RunGuard::~RunGuard()
{
release();
}
void
RunGuard::tryRestorePrimaryInstance()
{
MainWindow::instance().showWindow();
}
bool RunGuard::isAnotherRunning()
{
if (sharedMem.isAttached())
if (sharedMem_.isAttached())
return false;
memLock.acquire();
const bool isRunning = sharedMem.attach();
memLock_.acquire();
const bool isRunning = sharedMem_.attach();
if (isRunning)
sharedMem.detach();
memLock.release();
sharedMem_.detach();
memLock_.release();
return isRunning;
}
bool RunGuard::tryToRun()
{
if (isAnotherRunning()) // Extra check
if (isAnotherRunning()) {
// This is a secondary instance,
// connect to the primary instance to trigger a restore
// then fail.
if (socket_ == nullptr) {
socket_ = new QLocalSocket();
}
if (socket_->state() == QLocalSocket::UnconnectedState ||
socket_->state() == QLocalSocket::ClosingState) {
socket_->connectToServer(key_);
}
if (socket_->state() == QLocalSocket::ConnectingState) {
socket_->waitForConnected();
}
return false;
}
memLock.acquire();
const bool result = sharedMem.create(sizeof(quint64));
memLock.release();
memLock_.acquire();
const bool result = sharedMem_.create(sizeof(quint64));
memLock_.release();
if (!result) {
release();
return false;
}
// This is the primary instance,
// listen for subsequent instances.
QLocalServer::removeServer(key_);
server_ = new QLocalServer();
server_->setSocketOptions(QLocalServer::UserAccessOption);
server_->listen(key_);
QObject::connect(
server_,
&QLocalServer::newConnection,
this,
&RunGuard::tryRestorePrimaryInstance
);
return true;
}
void RunGuard::release()
{
memLock.acquire();
if (sharedMem.isAttached())
sharedMem.detach();
memLock.release();
memLock_.acquire();
if (sharedMem_.isAttached())
sharedMem_.detach();
memLock_.release();
}
\ No newline at end of file
// From: https://stackoverflow.com/a/28172162
/***************************************************************************
* Copyright (C) 2019 by Savoir-faire Linux *
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
**************************************************************************/
// Based on: https://stackoverflow.com/a/28172162
#pragma once
#include <QObject>
#include <QSharedMemory>
#include <QSystemSemaphore>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
class RunGuard
class RunGuard : public QObject
{
Q_OBJECT;
public:
RunGuard(const QString& key);
~RunGuard();
......@@ -17,13 +36,19 @@ public:
bool tryToRun();
void release();
private slots:
void tryRestorePrimaryInstance();
private:
const QString key;
const QString memLockKey;
const QString sharedmemKey;
const QString key_;
const QString memLockKey_;
const QString sharedmemKey_;
QSharedMemory sharedMem_;
QSystemSemaphore memLock_;
QSharedMemory sharedMem;
QSystemSemaphore memLock;
QLocalSocket *socket_;
QLocalServer *server_;
Q_DISABLE_COPY(RunGuard)
};
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment