refactor(ipc): Abstract IPC client logic
This commit is contained in:
parent
2e13074580
commit
1ec36645e1
7 changed files with 258 additions and 201 deletions
|
|
@ -81,6 +81,8 @@ add_library(${target} STATIC
|
||||||
dialogs/SettingsDialog.cpp
|
dialogs/SettingsDialog.cpp
|
||||||
dialogs/SettingsDialog.h
|
dialogs/SettingsDialog.h
|
||||||
dialogs/SettingsDialog.ui
|
dialogs/SettingsDialog.ui
|
||||||
|
ipc/IpcClient.cpp
|
||||||
|
ipc/IpcClient.h
|
||||||
ipc/DaemonIpcClient.cpp
|
ipc/DaemonIpcClient.cpp
|
||||||
ipc/DaemonIpcClient.h
|
ipc/DaemonIpcClient.h
|
||||||
validators/AliasValidator.cpp
|
validators/AliasValidator.cpp
|
||||||
|
|
|
||||||
|
|
@ -916,7 +916,7 @@ void MainWindow::coreProcessStateChanged(ProcessState state)
|
||||||
|
|
||||||
void MainWindow::coreConnectionStateChanged(ConnectionState state)
|
void MainWindow::coreConnectionStateChanged(ConnectionState state)
|
||||||
{
|
{
|
||||||
qDebug() << "core connection state changed: " << static_cast<int>(state);
|
qDebug() << "core connection state changed:" << static_cast<int>(state);
|
||||||
|
|
||||||
updateStatus();
|
updateStatus();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ bool generateCertificate()
|
||||||
try {
|
try {
|
||||||
deskflow::generatePemSelfSignedCert(certPath, keyLength);
|
deskflow::generatePemSelfSignedCert(certPath, keyLength);
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
qCritical() << "failed to generate self-signed pem cert: " << e.what();
|
qCritical() << "failed to generate self-signed pem cert:" << e.what();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
qDebug("tls certificate generated");
|
qDebug("tls certificate generated");
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Deskflow -- mouse and keyboard sharing utility
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
* SPDX-FileCopyrightText: (C) 2025 Symless Ltd.
|
* SPDX-FileCopyrightText: (C) 2025-2026 Symless Ltd.
|
||||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -17,164 +17,9 @@
|
||||||
namespace deskflow::gui::ipc {
|
namespace deskflow::gui::ipc {
|
||||||
|
|
||||||
const auto kTimeout = 1000;
|
const auto kTimeout = 1000;
|
||||||
const auto kRetryLimit = 3;
|
|
||||||
|
|
||||||
DaemonIpcClient::DaemonIpcClient(QObject *parent)
|
DaemonIpcClient::DaemonIpcClient(QObject *parent) : IpcClient(parent, kDaemonIpcName)
|
||||||
: QObject(parent),
|
|
||||||
m_socket{new QLocalSocket(this)} // NOSONAR - Qt memory
|
|
||||||
{
|
{
|
||||||
connect(m_socket, &QLocalSocket::disconnected, this, &DaemonIpcClient::handleDisconnected);
|
|
||||||
connect(m_socket, &QLocalSocket::errorOccurred, this, &DaemonIpcClient::handleErrorOccurred);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DaemonIpcClient::connectToServer()
|
|
||||||
{
|
|
||||||
if (m_state == State::Connecting) {
|
|
||||||
qWarning() << "daemon ipc client already connecting to server";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_state != State::Unconnected) {
|
|
||||||
qDebug() << "daemon ipc client not in unconnected state, disconnecting";
|
|
||||||
disconnectFromServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_socket->state() != QLocalSocket::UnconnectedState) {
|
|
||||||
qWarning() << "daemon ipc client socket not in unconnected state, disconnecting";
|
|
||||||
disconnectFromServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < kRetryLimit; ++i) {
|
|
||||||
if (i == 0) {
|
|
||||||
qDebug() << "daemon ipc client connecting to server:" << kDaemonIpcName;
|
|
||||||
} else {
|
|
||||||
qDebug() << "daemon ipc client retrying connection, attempt:" << i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_state = State::Connecting;
|
|
||||||
m_socket->connectToServer(kDaemonIpcName);
|
|
||||||
|
|
||||||
if (!m_socket->waitForConnected(kTimeout)) {
|
|
||||||
qWarning() << "daemon ipc client failed to connect";
|
|
||||||
disconnectFromServer();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sendMessage("hello", "hello", false)) {
|
|
||||||
qWarning() << "daemon ipc client failed to send hello";
|
|
||||||
disconnectFromServer();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_state = State::Connected;
|
|
||||||
qDebug() << "daemon ipc client connected";
|
|
||||||
Q_EMIT connected();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
qWarning() << "daemon ipc client failed to connect after" << kRetryLimit << "attempts";
|
|
||||||
disconnectFromServer();
|
|
||||||
Q_EMIT connectionFailed();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DaemonIpcClient::disconnectFromServer()
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&m_mutex);
|
|
||||||
m_state = State::Disconnecting;
|
|
||||||
qDebug() << "daemon ipc client disconnecting from server";
|
|
||||||
m_socket->disconnectFromServer();
|
|
||||||
|
|
||||||
if (m_socket->state() != QLocalSocket::UnconnectedState) {
|
|
||||||
qDebug() << "daemon ipc client waiting for socket to disconnect";
|
|
||||||
m_socket->waitForDisconnected(kTimeout);
|
|
||||||
qDebug() << "daemon ipc client disconnected from server";
|
|
||||||
} else {
|
|
||||||
qDebug() << "daemon ipc client socket already disconnected";
|
|
||||||
}
|
|
||||||
|
|
||||||
m_state = State::Unconnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DaemonIpcClient::handleDisconnected()
|
|
||||||
{
|
|
||||||
qDebug() << "daemon ipc client disconnected from server";
|
|
||||||
if (m_state == State::Connected) {
|
|
||||||
Q_EMIT connectionFailed();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_state = State::Unconnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DaemonIpcClient::handleErrorOccurred()
|
|
||||||
{
|
|
||||||
qWarning() << "daemon ipc client error:" << m_socket->errorString();
|
|
||||||
disconnectFromServer();
|
|
||||||
|
|
||||||
if (m_state == State::Connected) {
|
|
||||||
Q_EMIT connectionFailed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DaemonIpcClient::sendMessage(const QString &message, const QString &expectAck, const bool expectConnected)
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&m_mutex);
|
|
||||||
if (expectConnected && !isConnected()) {
|
|
||||||
qWarning() << "cannot send command, ipc client not connected";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray messageData = message.toUtf8() + "\n";
|
|
||||||
m_socket->write(messageData);
|
|
||||||
if (!m_socket->waitForBytesWritten(kTimeout)) {
|
|
||||||
qWarning() << "daemon ipc client failed to write command";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!expectAck.isEmpty()) {
|
|
||||||
qDebug() << "daemon ipc client waiting for ack: " << expectAck;
|
|
||||||
|
|
||||||
if (!m_socket->waitForReadyRead(kTimeout)) {
|
|
||||||
qWarning() << "daemon ipc client socket ready read timed out";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray response = m_socket->readAll();
|
|
||||||
if (response.isEmpty()) {
|
|
||||||
qWarning() << "daemon ipc client got empty response";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString responseData = QString::fromUtf8(response);
|
|
||||||
if (responseData.isEmpty()) {
|
|
||||||
qWarning() << "daemon ipc client failed to convert response to string";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (responseData != expectAck + "\n") {
|
|
||||||
qWarning() << "daemon ipc client got unexpected response: " << responseData;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "daemon ipc client sent message: " << messageData;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DaemonIpcClient::keepAlive()
|
|
||||||
{
|
|
||||||
if (!isConnected() && !connectToServer()) {
|
|
||||||
qWarning() << "daemon ipc client keep alive failed to connect";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sendMessage("noop")) {
|
|
||||||
qWarning() << "daemon ipc client keep alive ping failed, reconnecting";
|
|
||||||
connectToServer();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DaemonIpcClient::sendLogLevel(const QString &logLevel)
|
bool DaemonIpcClient::sendLogLevel(const QString &logLevel)
|
||||||
|
|
@ -216,12 +61,12 @@ QString DaemonIpcClient::requestLogPath()
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_socket->waitForReadyRead(kTimeout)) {
|
if (!socket()->waitForReadyRead(kTimeout)) {
|
||||||
qWarning() << "daemon ipc client failed to read log path response";
|
qWarning() << "daemon ipc client failed to read log path response";
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray response = m_socket->readAll();
|
QByteArray response = socket()->readAll();
|
||||||
if (response.isEmpty()) {
|
if (response.isEmpty()) {
|
||||||
qWarning() << "daemon ipc client got empty log path response";
|
qWarning() << "daemon ipc client got empty log path response";
|
||||||
return QString();
|
return QString();
|
||||||
|
|
@ -236,12 +81,12 @@ QString DaemonIpcClient::requestLogPath()
|
||||||
// Trimming removes newline from end of message.
|
// Trimming removes newline from end of message.
|
||||||
QStringList parts = responseData.trimmed().split("=");
|
QStringList parts = responseData.trimmed().split("=");
|
||||||
if (parts.size() != 2) {
|
if (parts.size() != 2) {
|
||||||
qWarning() << "daemon ipc client got invalid log path response: " << responseData;
|
qWarning() << "daemon ipc client got invalid log path response:" << responseData;
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parts[0] != "logPath") {
|
if (parts[0] != "logPath") {
|
||||||
qWarning() << "daemon ipc client got unexpected log path response: " << responseData;
|
qWarning() << "daemon ipc client got unexpected log path response:" << responseData;
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,62 +1,28 @@
|
||||||
/*
|
/*
|
||||||
* Deskflow -- mouse and keyboard sharing utility
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
* SPDX-FileCopyrightText: (C) 2025 Symless Ltd.
|
* SPDX-FileCopyrightText: (C) 2025-2026 Symless Ltd.
|
||||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QMutex>
|
#include "IpcClient.h"
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
class QLocalSocket;
|
#include <QObject>
|
||||||
|
|
||||||
namespace deskflow::gui::ipc {
|
namespace deskflow::gui::ipc {
|
||||||
|
|
||||||
class DaemonIpcClient : public QObject
|
class DaemonIpcClient : public IpcClient
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
// Represents underlying socket state and whether the server responded to the hello message.
|
|
||||||
enum class State
|
|
||||||
{
|
|
||||||
Unconnected,
|
|
||||||
Connecting,
|
|
||||||
Connected,
|
|
||||||
Disconnecting,
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit DaemonIpcClient(QObject *parent = nullptr);
|
explicit DaemonIpcClient(QObject *parent = nullptr);
|
||||||
bool connectToServer();
|
|
||||||
void disconnectFromServer();
|
|
||||||
bool sendLogLevel(const QString &logLevel);
|
bool sendLogLevel(const QString &logLevel);
|
||||||
bool sendStartProcess(const QString &command, bool elevate);
|
bool sendStartProcess(const QString &command, bool elevate);
|
||||||
bool sendStopProcess();
|
bool sendStopProcess();
|
||||||
bool sendClearSettings();
|
bool sendClearSettings();
|
||||||
QString requestLogPath();
|
QString requestLogPath();
|
||||||
|
|
||||||
bool isConnected() const
|
|
||||||
{
|
|
||||||
return m_state == State::Connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void connected();
|
|
||||||
void connectionFailed();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void handleDisconnected();
|
|
||||||
void handleErrorOccurred();
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool keepAlive();
|
|
||||||
bool sendMessage(const QString &message, const QString &expectAck = "ok", const bool expectConnected = true);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QLocalSocket *m_socket;
|
|
||||||
QMutex m_mutex;
|
|
||||||
State m_state{State::Unconnected};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace deskflow::gui::ipc
|
} // namespace deskflow::gui::ipc
|
||||||
|
|
|
||||||
181
src/lib/gui/ipc/IpcClient.cpp
Normal file
181
src/lib/gui/ipc/IpcClient.cpp
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
/*
|
||||||
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
|
* SPDX-FileCopyrightText: (C) 2025-2026 Symless Ltd.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "IpcClient.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QLocalSocket>
|
||||||
|
#include <QMutexLocker>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace deskflow::gui::ipc {
|
||||||
|
|
||||||
|
const auto kTimeout = 1000;
|
||||||
|
const auto kRetryLimit = 3;
|
||||||
|
|
||||||
|
IpcClient::IpcClient(QObject *parent, const QString &socketName)
|
||||||
|
: QObject(parent),
|
||||||
|
m_socket{new QLocalSocket(this)},
|
||||||
|
m_socketName(socketName) // NOSONAR - Qt memory
|
||||||
|
{
|
||||||
|
connect(m_socket, &QLocalSocket::disconnected, this, &IpcClient::handleDisconnected);
|
||||||
|
connect(m_socket, &QLocalSocket::errorOccurred, this, &IpcClient::handleErrorOccurred);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpcClient::connectToServer()
|
||||||
|
{
|
||||||
|
if (m_state == State::Connecting) {
|
||||||
|
qWarning() << "ipc client already connecting to server";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_state != State::Unconnected) {
|
||||||
|
qDebug() << "ipc client not in unconnected state, disconnecting";
|
||||||
|
disconnectFromServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_socket->state() != QLocalSocket::UnconnectedState) {
|
||||||
|
qWarning() << "ipc client socket not in unconnected state, disconnecting";
|
||||||
|
disconnectFromServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < kRetryLimit; ++i) {
|
||||||
|
if (i == 0) {
|
||||||
|
qDebug() << "ipc client connecting to server:" << m_socketName;
|
||||||
|
} else {
|
||||||
|
qDebug() << "ipc client retrying connection, attempt:" << i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = State::Connecting;
|
||||||
|
m_socket->connectToServer(m_socketName);
|
||||||
|
|
||||||
|
if (!m_socket->waitForConnected(kTimeout)) {
|
||||||
|
qWarning() << "ipc client failed to connect";
|
||||||
|
disconnectFromServer();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendMessage("hello", "hello", false)) {
|
||||||
|
qWarning() << "ipc client failed to send hello";
|
||||||
|
disconnectFromServer();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = State::Connected;
|
||||||
|
qDebug() << "ipc client connected";
|
||||||
|
Q_EMIT connected();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
qWarning() << "ipc client failed to connect after" << kRetryLimit << "attempts";
|
||||||
|
disconnectFromServer();
|
||||||
|
Q_EMIT connectionFailed();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpcClient::disconnectFromServer()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
m_state = State::Disconnecting;
|
||||||
|
qDebug() << "ipc client disconnecting from server";
|
||||||
|
m_socket->disconnectFromServer();
|
||||||
|
|
||||||
|
if (m_socket->state() != QLocalSocket::UnconnectedState) {
|
||||||
|
qDebug() << "ipc client waiting for socket to disconnect";
|
||||||
|
m_socket->waitForDisconnected(kTimeout);
|
||||||
|
qDebug() << "ipc client disconnected from server";
|
||||||
|
} else {
|
||||||
|
qDebug() << "ipc client socket already disconnected";
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = State::Unconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpcClient::handleDisconnected()
|
||||||
|
{
|
||||||
|
qDebug() << "ipc client disconnected from server";
|
||||||
|
if (m_state == State::Connected) {
|
||||||
|
Q_EMIT connectionFailed();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = State::Unconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpcClient::handleErrorOccurred()
|
||||||
|
{
|
||||||
|
qWarning() << "ipc client error:" << m_socket->errorString();
|
||||||
|
disconnectFromServer();
|
||||||
|
|
||||||
|
if (m_state == State::Connected) {
|
||||||
|
Q_EMIT connectionFailed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpcClient::sendMessage(const QString &message, const QString &expectAck, const bool expectConnected)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
if (expectConnected && !isConnected()) {
|
||||||
|
qWarning() << "cannot send command, ipc client not connected";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray messageData = message.toUtf8() + "\n";
|
||||||
|
m_socket->write(messageData);
|
||||||
|
if (!m_socket->waitForBytesWritten(kTimeout)) {
|
||||||
|
qWarning() << "ipc client failed to write command";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!expectAck.isEmpty()) {
|
||||||
|
qDebug() << "ipc client waiting for ack:" << expectAck;
|
||||||
|
|
||||||
|
if (!m_socket->waitForReadyRead(kTimeout)) {
|
||||||
|
qWarning() << "ipc client socket ready read timed out";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray response = m_socket->readAll();
|
||||||
|
if (response.isEmpty()) {
|
||||||
|
qWarning() << "ipc client got empty response";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString responseData = QString::fromUtf8(response);
|
||||||
|
if (responseData.isEmpty()) {
|
||||||
|
qWarning() << "ipc client failed to convert response to string";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responseData != expectAck + "\n") {
|
||||||
|
qWarning() << "ipc client got unexpected response:" << responseData;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "ipc client sent message:" << messageData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IpcClient::keepAlive()
|
||||||
|
{
|
||||||
|
if (!isConnected() && !connectToServer()) {
|
||||||
|
qWarning() << "ipc client keep alive failed to connect";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendMessage("noop")) {
|
||||||
|
qWarning() << "ipc client keep alive ping failed, reconnecting";
|
||||||
|
connectToServer();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace deskflow::gui::ipc
|
||||||
63
src/lib/gui/ipc/IpcClient.h
Normal file
63
src/lib/gui/ipc/IpcClient.h
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Deskflow -- mouse and keyboard sharing utility
|
||||||
|
* SPDX-FileCopyrightText: (C) 2025-2026 Symless Ltd.
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QLocalSocket;
|
||||||
|
|
||||||
|
namespace deskflow::gui::ipc {
|
||||||
|
|
||||||
|
class IpcClient : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
// Represents underlying socket state and whether the server responded to the hello message.
|
||||||
|
enum class State
|
||||||
|
{
|
||||||
|
Unconnected,
|
||||||
|
Connecting,
|
||||||
|
Connected,
|
||||||
|
Disconnecting,
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit IpcClient(QObject *parent, const QString &socketName);
|
||||||
|
bool connectToServer();
|
||||||
|
void disconnectFromServer();
|
||||||
|
|
||||||
|
bool isConnected() const
|
||||||
|
{
|
||||||
|
return m_state == State::Connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void connected();
|
||||||
|
void connectionFailed();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void handleDisconnected();
|
||||||
|
void handleErrorOccurred();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool keepAlive();
|
||||||
|
bool sendMessage(const QString &message, const QString &expectAck = "ok", const bool expectConnected = true);
|
||||||
|
|
||||||
|
QLocalSocket *socket() const
|
||||||
|
{
|
||||||
|
return m_socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLocalSocket *m_socket;
|
||||||
|
State m_state{State::Unconnected};
|
||||||
|
QString m_socketName;
|
||||||
|
QMutex m_mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace deskflow::gui::ipc
|
||||||
Loading…
Reference in a new issue