Introduced SingleInstance class

This commit is contained in:
Andrei Smirnov 2021-07-21 19:34:28 +03:00 committed by Michał
parent 8c095ec628
commit cba2e276b2
8 changed files with 112 additions and 45 deletions

View File

@ -22,6 +22,7 @@ macro(add_target name type)
include/DOtherSide/Utils.h
include/DOtherSide/DosDockClicker.h
include/DOtherSide/DOtherSideStatusWindow.h
include/DOtherSide/DOtherSideSingleInstance.h
src/DOtherSide.cpp
src/DosQMetaObject.cpp
src/DosQDeclarative.cpp
@ -32,6 +33,7 @@ macro(add_target name type)
src/DosQQuickImageProvider.cpp
src/DosDockClicker.cpp
src/DOtherSideStatusWindow.cpp
src/DOtherSideSingleInstance.cpp
)
if (APPLE)

View File

@ -973,6 +973,26 @@ DOS_API int DOS_CALL dos_qdeclarative_qmlregistersingletontype(const QmlRegister
/// @}
/// \defgroup SingleInstance SingleInstance
/// \brief Functions related to the SingleInstance cclass
/// @{
/// \brief Create a new SingleInstance class
/// \param uniqueName The UTF-8 string for QLocalServer name
/// \note The returned SingleInstance should be freed using the dos_singleinstance_delete() function
DOS_API DosSingleInstance *DOS_CALL dos_singleinstance_create(const char *uniqueName);
/// \brief Returns bool indicating whether this is the first instance or not
/// \returns true if this is the first instance
/// \param vptr The SingleInstance
DOS_API bool DOS_CALL dos_singleinstance_isfirst(DosSingleInstance *vptr);
/// \brief Free the memory allocated for the SingleInstance
/// \param vptr The SingleInstance to be freed
DOS_API void DOS_CALL dos_singleinstance_delete(DosSingleInstance *vptr);
/// @}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,26 @@
#ifndef SINGLEINSTANCE_H
#define SINGLEINSTANCE_H
#include <QObject>
class QLocalServer;
class SingleInstance : public QObject
{
Q_OBJECT
public:
explicit SingleInstance(const QString &uniqueName, QObject *parent = nullptr);
~SingleInstance() override;
bool isFirstInstance() const;
signals:
void secondInstanceDetected();
private:
QLocalServer *m_localServer;
};
#endif // SINGLEINSTANCE_H

View File

@ -4,8 +4,6 @@
#include <QQuickWindow>
#include <QScreen>
class QLocalServer;
class StatusWindow: public QQuickWindow
{
Q_OBJECT
@ -15,7 +13,6 @@ class StatusWindow: public QQuickWindow
public:
explicit StatusWindow(QWindow *parent = nullptr);
~StatusWindow();
Q_INVOKABLE void toggleFullScreen();
@ -33,14 +30,12 @@ signals:
void secondInstanceDetected();
private:
void checkSingleInstance();
void removeTitleBar();
void showTitleBar();
void initCallbacks();
private:
bool m_isFullScreen;
QLocalServer *m_localServer;
};
#endif // STATUSWINDOW_H

View File

@ -95,6 +95,9 @@ typedef void DosQQuickImageProvider;
/// A pointer to a QPixmap
typedef void DosPixmap;
/// A pointer to SingleInstance
typedef void DosSingleInstance;
/// A pixmap callback to be supplied to an image provider
/// \param id Image source id
/// \param width pointer to the width of the image

View File

@ -59,6 +59,7 @@
#include "DOtherSide/DosQQuickImageProvider.h"
#include "DOtherSide/DosDockClicker.h"
#include "DOtherSide/DOtherSideStatusWindow.h"
#include "DOtherSide/DOtherSideSingleInstance.h"
namespace {
@ -1292,3 +1293,23 @@ char *dos_qurl_replaceHostAndAddPath(char* url, char* newScheme, char* newHost,
return convert_to_cstring(newQurl.toString());
}
DosSingleInstance *dos_singleinstance_create(const char *uniqueName)
{
return new SingleInstance(QString::fromUtf8(uniqueName));
}
void dos_singleinstance_delete(DosSingleInstance *vptr)
{
auto dsi = static_cast<SingleInstance *>(vptr);
delete dsi;
}
bool dos_singleinstance_isfirst(DosSingleInstance *vptr)
{
auto dsi = static_cast<SingleInstance *>(vptr);
if (dsi) {
return dsi->isFirstInstance();
}
return false;
}

View File

@ -0,0 +1,39 @@
#include "DOtherSide/DOtherSideSingleInstance.h"
#include <QLocalServer>
#include <QLocalSocket>
SingleInstance::SingleInstance(const QString &uniqueName, QObject *parent)
: QObject(parent)
, m_localServer(new QLocalServer(this))
{
QString socketName = uniqueName;
#ifndef Q_OS_WIN
socketName = QString("/tmp/%1").arg(socketName);
#endif
QLocalSocket localSocket;
localSocket.connectToServer(socketName);
// the first instance start will be delayed by this timeout (ms) to ensure there are no other instances.
// note: this is an ad-hoc timeout value selected based on prior experience.
if (!localSocket.waitForConnected(100)) {
connect(m_localServer, &QLocalServer::newConnection, this, &SingleInstance::secondInstanceDetected);
if (!m_localServer->listen(socketName)) {
qWarning() << "QLocalServer::listen(" << socketName << ") failed";
}
}
}
SingleInstance::~SingleInstance()
{
if (m_localServer->isListening()) {
m_localServer->close();
}
}
bool SingleInstance::isFirstInstance() const
{
return m_localServer->isListening();
}

View File

@ -1,16 +1,9 @@
#include "DOtherSide/DOtherSideStatusWindow.h"
#include <QLocalServer>
#include <QLocalSocket>
#include <QDir>
#include <QCryptographicHash>
StatusWindow::StatusWindow(QWindow *parent)
: QQuickWindow(parent),
m_isFullScreen(false),
m_localServer(new QLocalServer(this))
m_isFullScreen(false)
{
checkSingleInstance();
removeTitleBar();
connect(this, &QQuickWindow::windowStateChanged, [&](Qt::WindowState windowState) {
@ -26,13 +19,6 @@ StatusWindow::StatusWindow(QWindow *parent)
});
}
StatusWindow::~StatusWindow()
{
if (m_localServer->isListening()) {
m_localServer->close();
}
}
void StatusWindow::toggleFullScreen()
{
if (m_isFullScreen) {
@ -46,28 +32,3 @@ bool StatusWindow::isFullScreen() const
{
return m_isFullScreen;
}
void StatusWindow::checkSingleInstance()
{
const auto currentDir = QDir::currentPath();
auto socketName = QString(QCryptographicHash::hash(currentDir.toUtf8(), QCryptographicHash::Md5).toHex());
#ifndef Q_OS_WIN
socketName = QString("/tmp/%1").arg(socketName);
#endif
QLocalSocket localSocket;
localSocket.connectToServer(socketName);
// the first instance start will be delayed by this timeout (ms) to ensure there are no other instances.
// note: this is an ad-hoc timeout value selected based on prior experience.
const bool connected = localSocket.waitForConnected(100);
if (!connected) {
connect(m_localServer, &QLocalServer::newConnection, this, &StatusWindow::secondInstanceDetected);
if (!m_localServer->listen(socketName)) {
qWarning() << "QLocalServer::listen(" << socketName << ") failed";
}
} else {
qFatal("Terminating app as the second running instance...");
}
}