mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-06 06:19:52 +08:00
226 lines
6.2 KiB
C++
226 lines
6.2 KiB
C++
#include "singleinstanceguard.h"
|
|
|
|
#include <QDebug>
|
|
#include <QLocalServer>
|
|
#include <QLocalSocket>
|
|
#include <QDataStream>
|
|
#include <QByteArray>
|
|
|
|
#include <utils/utils.h>
|
|
|
|
using namespace vnotex;
|
|
|
|
const QString SingleInstanceGuard::c_serverName = "vnote";
|
|
|
|
SingleInstanceGuard::SingleInstanceGuard()
|
|
{
|
|
qInfo() << "guarding is on";
|
|
}
|
|
|
|
SingleInstanceGuard::~SingleInstanceGuard()
|
|
{
|
|
qInfo() << "guarding is off";
|
|
exit();
|
|
}
|
|
|
|
bool SingleInstanceGuard::tryRun()
|
|
{
|
|
Q_ASSERT(!m_online);
|
|
|
|
#if defined(Q_OS_WIN)
|
|
// On Windows, multiple servers on the same name are allowed.
|
|
m_client = tryConnect();
|
|
if (m_client) {
|
|
// There is one server running and we are now connected, so we could not continue.
|
|
return false;
|
|
}
|
|
|
|
m_server = tryListen();
|
|
if (m_server) {
|
|
// We are the lucky one.
|
|
qInfo() << "guard succeeds to run";
|
|
} else {
|
|
// We still allow the guard to run. There maybe a bug need to fix.
|
|
qWarning() << "failed to connect to an existing instance or establish a new local server";
|
|
}
|
|
#else
|
|
m_server = tryListen();
|
|
if (m_server) {
|
|
// We are the lucky one.
|
|
qInfo() << "guard succeeds to run";
|
|
} else {
|
|
// Here we are sure there is another instance running. But we still use a socket to connect to make sure.
|
|
m_client = tryConnect();
|
|
if (m_client) {
|
|
// We are sure there is another instance running.
|
|
return false;
|
|
}
|
|
|
|
// We still allow the guard to run. There maybe a bug need to fix.
|
|
qWarning() << "failed to connect to an existing instance or establish a new local server";
|
|
}
|
|
#endif
|
|
|
|
setupServer();
|
|
|
|
m_online = true;
|
|
return true;
|
|
}
|
|
|
|
void SingleInstanceGuard::requestOpenFiles(const QStringList &p_files)
|
|
{
|
|
if (p_files.isEmpty()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
void SingleInstanceGuard::requestShow()
|
|
{
|
|
Q_ASSERT(!m_online);
|
|
if (!m_client || m_client->state() != QLocalSocket::ConnectedState) {
|
|
qWarning() << "failed to request show" << m_client->errorString();
|
|
return ;
|
|
}
|
|
|
|
sendRequest(m_client.data(), OpCode::Show, QString());
|
|
}
|
|
|
|
void SingleInstanceGuard::exit()
|
|
{
|
|
m_online = false;
|
|
|
|
if (m_client) {
|
|
m_client->disconnectFromServer();
|
|
m_client.clear();
|
|
}
|
|
|
|
if (m_server) {
|
|
m_server->close();
|
|
m_server.clear();
|
|
}
|
|
}
|
|
|
|
QSharedPointer<QLocalSocket> SingleInstanceGuard::tryConnect()
|
|
{
|
|
auto socket = QSharedPointer<QLocalSocket>::create();
|
|
socket->connectToServer(c_serverName);
|
|
if (socket->waitForConnected(200)) {
|
|
// Connected.
|
|
qDebug() << "socket connected to server" << c_serverName;
|
|
return socket;
|
|
} else {
|
|
qDebug() << "socket connect timeout";
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
QSharedPointer<QLocalServer> SingleInstanceGuard::tryListen()
|
|
{
|
|
auto server = QSharedPointer<QLocalServer>::create();
|
|
bool ret = server->listen(c_serverName);
|
|
if (!ret && server->serverError() == QAbstractSocket::AddressInUseError) {
|
|
// On Unix, a previous crash may leave a server running.
|
|
// Clean up and try again.
|
|
QLocalServer::removeServer(c_serverName);
|
|
ret = server->listen(c_serverName);
|
|
}
|
|
|
|
if (ret) {
|
|
qDebug() << "local server listening on" << c_serverName;
|
|
return server;
|
|
} else {
|
|
qDebug() << "failed to start local server";
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void SingleInstanceGuard::setupServer()
|
|
{
|
|
if (!m_server) {
|
|
return;
|
|
}
|
|
|
|
connect(m_server.data(), &QLocalServer::newConnection,
|
|
this, [this]() {
|
|
auto socket = m_server->nextPendingConnection();
|
|
if (socket) {
|
|
qInfo() << "local server receives new connect" << socket;
|
|
if (m_ongoingConnect) {
|
|
qWarning() << "drop the connection since there is one ongoing connect";
|
|
socket->disconnectFromServer();
|
|
socket->deleteLater();
|
|
return;
|
|
}
|
|
|
|
m_ongoingConnect = true;
|
|
m_command.clear();
|
|
|
|
connect(socket, &QLocalSocket::disconnected,
|
|
this, [this, socket]() {
|
|
Q_ASSERT(m_ongoingConnect);
|
|
socket->deleteLater();
|
|
m_ongoingConnect = false;
|
|
});
|
|
connect(socket, &QLocalSocket::readyRead,
|
|
this, [this, socket]() {
|
|
receiveCommand(socket);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
void SingleInstanceGuard::receiveCommand(QLocalSocket *p_socket)
|
|
{
|
|
QDataStream inStream;
|
|
inStream.setDevice(p_socket);
|
|
inStream.setVersion(QDataStream::Qt_5_12);
|
|
|
|
if (m_command.m_opCode == OpCode::Null) {
|
|
// Relies on the fact that QDataStream serializes a quint32 into
|
|
// sizeof(quint32) bytes.
|
|
if (p_socket->bytesAvailable() < (int)sizeof(quint32) * 2) {
|
|
return;
|
|
}
|
|
|
|
quint32 opCode = 0;
|
|
inStream >> opCode;
|
|
m_command.m_opCode = static_cast<OpCode>(opCode);
|
|
inStream >> m_command.m_size;
|
|
}
|
|
|
|
if (p_socket->bytesAvailable() < m_command.m_size) {
|
|
return;
|
|
}
|
|
|
|
qDebug() << "op code" << m_command.m_opCode << m_command.m_size;
|
|
|
|
switch (m_command.m_opCode) {
|
|
case OpCode::Show:
|
|
Q_ASSERT(m_command.m_size == 0);
|
|
emit showRequested();
|
|
break;
|
|
|
|
default:
|
|
qWarning() << "unknown op code" << m_command.m_opCode;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SingleInstanceGuard::sendRequest(QLocalSocket *p_socket, OpCode p_code, const QString &p_payload)
|
|
{
|
|
QByteArray block;
|
|
QDataStream out(&block, QIODevice::WriteOnly);
|
|
out.setVersion(QDataStream::Qt_5_12);
|
|
out << static_cast<quint32>(p_code);
|
|
out << static_cast<quint32>(p_payload.size());
|
|
if (p_payload.size() > 0) {
|
|
out << p_payload;
|
|
}
|
|
p_socket->write(block);
|
|
if (p_socket->waitForBytesWritten(3000)) {
|
|
qDebug() << "request sent" << p_code << p_payload.size();
|
|
} else {
|
|
qWarning() << "failed to send request" << p_code;
|
|
}
|
|
}
|