feat(ipc): Core IPC server and connected client
This commit is contained in:
parent
1ec36645e1
commit
62ccb543dc
14 changed files with 170 additions and 28 deletions
|
|
@ -15,6 +15,7 @@
|
|||
#include "common/ExitCodes.h"
|
||||
#include "deskflow/ClientApp.h"
|
||||
#include "deskflow/ServerApp.h"
|
||||
#include "deskflow/ipc/CoreIpcServer.h"
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#include "arch/win32/ArchMiscWindows.h"
|
||||
|
|
@ -104,6 +105,9 @@ int main(int argc, char **argv)
|
|||
QCoreApplication app(argc, argv);
|
||||
QCoreApplication::setApplicationName(QStringLiteral("%1 Core").arg(kAppName));
|
||||
|
||||
const auto ipcServer = new deskflow::core::ipc::CoreIpcServer(&app); // NOSONAR - Qt managed
|
||||
ipcServer->listen();
|
||||
|
||||
QThread coreThread;
|
||||
QObject::connect(&coreThread, &QThread::finished, &app, &QCoreApplication::quit);
|
||||
coreApp->run(coreThread);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ const auto kCopyright = //
|
|||
"Copyright (C) 2002-2009 Chris Schoeneman";
|
||||
|
||||
const auto kCoreBinName = "@CORE_BINARY@";
|
||||
const auto kCoreIpcName = "@CMAKE_PROJECT_NAME@-core";
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ add_library(${lib_name} STATIC ${PLATFORM_CODE}
|
|||
ipc/IpcServer.h
|
||||
ipc/DaemonIpcServer.cpp
|
||||
ipc/DaemonIpcServer.h
|
||||
ipc/CoreIpcServer.cpp
|
||||
ipc/CoreIpcServer.h
|
||||
)
|
||||
|
||||
target_link_libraries(${lib_name} PUBLIC common Qt6::Core Qt6::Network)
|
||||
|
|
|
|||
28
src/lib/deskflow/ipc/CoreIpcServer.cpp
Normal file
28
src/lib/deskflow/ipc/CoreIpcServer.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 "CoreIpcServer.h"
|
||||
|
||||
#include "base/Log.h"
|
||||
#include "common/Constants.h"
|
||||
|
||||
#include <QLocalSocket>
|
||||
|
||||
namespace deskflow::core::ipc {
|
||||
|
||||
CoreIpcServer::CoreIpcServer(QObject *parent) : IpcServer(parent, kCoreIpcName)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void CoreIpcServer::processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts)
|
||||
{
|
||||
Q_UNUSED(clientSocket)
|
||||
Q_UNUSED(parts)
|
||||
LOG_WARN("core ipc server got unknown command: %s", command.toUtf8().constData());
|
||||
}
|
||||
|
||||
} // namespace deskflow::core::ipc
|
||||
29
src/lib/deskflow/ipc/CoreIpcServer.h
Normal file
29
src/lib/deskflow/ipc/CoreIpcServer.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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 "IpcServer.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
|
||||
class QLocalSocket;
|
||||
|
||||
namespace deskflow::core::ipc {
|
||||
|
||||
class CoreIpcServer : public IpcServer
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CoreIpcServer(QObject *parent);
|
||||
|
||||
private:
|
||||
void processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts) override;
|
||||
};
|
||||
|
||||
} // namespace deskflow::core::ipc
|
||||
|
|
@ -23,29 +23,14 @@ DaemonIpcServer::DaemonIpcServer(QObject *parent, const QString &logFilename)
|
|||
// do nothing
|
||||
}
|
||||
|
||||
void DaemonIpcServer::processMessage(QLocalSocket *clientSocket, const QString &message)
|
||||
void DaemonIpcServer::processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts)
|
||||
{
|
||||
LOG_DEBUG1("daemon ipc server got message: %s", message.toUtf8().constData());
|
||||
const auto parts = message.split('=');
|
||||
if (parts.size() < 1) {
|
||||
LOG_ERR("daemon ipc server got invalid message: %s", message.toUtf8().constData());
|
||||
writeToClientSocket(clientSocket, kErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &command = parts[0];
|
||||
if (command == "hello") { // NOSONAR - if-init is confusing here
|
||||
LOG_DEBUG("daemon ipc server got hello message, sending hello back");
|
||||
writeToClientSocket(clientSocket, "hello");
|
||||
} else if (command == "noop") {
|
||||
LOG_DEBUG("daemon ipc server got noop message");
|
||||
writeToClientSocket(clientSocket, kAckMessage);
|
||||
} else if (command == "logLevel") {
|
||||
if (command == "logLevel") {
|
||||
processLogLevel(clientSocket, parts);
|
||||
} else if (command == "elevate") {
|
||||
processElevate(clientSocket, parts);
|
||||
} else if (command == "command") {
|
||||
processCommand(clientSocket, parts);
|
||||
processCommandMessage(clientSocket, parts);
|
||||
} else if (command == "start") {
|
||||
LOG_DEBUG("daemon ipc server got start message");
|
||||
Q_EMIT startProcessRequested();
|
||||
|
|
@ -62,10 +47,8 @@ void DaemonIpcServer::processMessage(QLocalSocket *clientSocket, const QString &
|
|||
Q_EMIT clearSettingsRequested();
|
||||
writeToClientSocket(clientSocket, kAckMessage);
|
||||
} else {
|
||||
LOG_WARN("daemon ipc server got unknown message: %s", message.toUtf8().constData());
|
||||
LOG_WARN("daemon ipc server got unknown command: %s", command.toUtf8().constData());
|
||||
}
|
||||
|
||||
clientSocket->flush();
|
||||
}
|
||||
|
||||
void DaemonIpcServer::processLogLevel(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
||||
|
|
@ -108,7 +91,7 @@ void DaemonIpcServer::processElevate(QLocalSocket *&clientSocket, const QStringL
|
|||
writeToClientSocket(clientSocket, kAckMessage);
|
||||
}
|
||||
|
||||
void DaemonIpcServer::processCommand(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
||||
void DaemonIpcServer::processCommandMessage(QLocalSocket *&clientSocket, const QStringList &messageParts)
|
||||
{
|
||||
if (messageParts.size() < 2) {
|
||||
LOG_ERR("daemon ipc server got invalid command message");
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ public:
|
|||
explicit DaemonIpcServer(QObject *parent, const QString &logFilename);
|
||||
|
||||
private:
|
||||
void processMessage(QLocalSocket *clientSocket, const QString &message) override;
|
||||
void processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts) override;
|
||||
void processLogLevel(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||
void processElevate(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||
void processCommand(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||
void processCommandMessage(QLocalSocket *&clientSocket, const QStringList &messageParts);
|
||||
|
||||
private:
|
||||
const QString m_logFilename;
|
||||
|
|
|
|||
|
|
@ -13,9 +13,6 @@
|
|||
|
||||
namespace deskflow::core::ipc {
|
||||
|
||||
const auto kAckMessage = "ok";
|
||||
const auto kErrorMessage = "error";
|
||||
|
||||
IpcServer::IpcServer(QObject *parent, const QString &serverName)
|
||||
: QObject(parent),
|
||||
m_server{new QLocalServer(this)}, // NOSONAR - Qt memory
|
||||
|
|
@ -102,6 +99,29 @@ void IpcServer::handleErrorOccurred()
|
|||
clientSocket->deleteLater();
|
||||
}
|
||||
|
||||
void IpcServer::processMessage(QLocalSocket *clientSocket, const QString &message)
|
||||
{
|
||||
LOG_DEBUG1("ipc server got message: %s", message.toUtf8().constData());
|
||||
const auto parts = message.split('=');
|
||||
if (parts.isEmpty()) {
|
||||
LOG_ERR("ipc server got invalid message: %s", message.toUtf8().constData());
|
||||
writeToClientSocket(clientSocket, "error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto &command = parts.at(0); command == "hello") {
|
||||
LOG_DEBUG("ipc server got hello message, sending hello back");
|
||||
writeToClientSocket(clientSocket, "hello");
|
||||
} else if (command == "noop") {
|
||||
LOG_DEBUG("ipc server got noop message");
|
||||
writeToClientSocket(clientSocket, "ok");
|
||||
} else {
|
||||
processCommand(clientSocket, command, parts);
|
||||
}
|
||||
|
||||
clientSocket->flush();
|
||||
}
|
||||
|
||||
void IpcServer::writeToClientSocket(QLocalSocket *&clientSocket, const QString &message) const
|
||||
{
|
||||
QByteArray messageData = message.toUtf8() + '\n';
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ protected:
|
|||
void writeToClientSocket(QLocalSocket *&clientSocket, const QString &message) const;
|
||||
|
||||
private:
|
||||
virtual void processMessage(QLocalSocket *clientSocket, const QString &message) = 0;
|
||||
void processMessage(QLocalSocket *clientSocket, const QString &message);
|
||||
virtual void processCommand(QLocalSocket *clientSocket, const QString &command, const QStringList &parts) = 0;
|
||||
void handleNewConnection();
|
||||
void handleReadyRead();
|
||||
void handleDisconnected();
|
||||
|
|
|
|||
|
|
@ -85,6 +85,8 @@ add_library(${target} STATIC
|
|||
ipc/IpcClient.h
|
||||
ipc/DaemonIpcClient.cpp
|
||||
ipc/DaemonIpcClient.h
|
||||
ipc/CoreIpcClient.cpp
|
||||
ipc/CoreIpcClient.h
|
||||
validators/AliasValidator.cpp
|
||||
validators/AliasValidator.h
|
||||
validators/ComputerNameValidator.cpp
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "CoreProcess.h"
|
||||
|
||||
#include "common/ExitCodes.h"
|
||||
#include "gui/ipc/CoreIpcClient.h"
|
||||
#include "gui/ipc/DaemonIpcClient.h"
|
||||
|
||||
#if defined(Q_OS_MACOS)
|
||||
|
|
@ -372,6 +373,21 @@ void CoreProcess::start(std::optional<ProcessMode> processModeOption)
|
|||
startProcessFromDaemon(args);
|
||||
}
|
||||
|
||||
// Don't block the main GUI render thread when connecting to the Core IPC server.
|
||||
QTimer::singleShot(kRetryDelay, this, [this] {
|
||||
if (m_processState != ProcessState::Started) {
|
||||
qWarning("core process failed to start, skipping core ipc connection");
|
||||
return;
|
||||
}
|
||||
|
||||
m_coreIpcClient = new ipc::CoreIpcClient(this);
|
||||
if (m_coreIpcClient->connectToServer()) {
|
||||
qInfo("connected to core ipc server");
|
||||
} else {
|
||||
qWarning("failed to establish core ipc connection");
|
||||
}
|
||||
});
|
||||
|
||||
m_lastProcessMode = processMode;
|
||||
}
|
||||
|
||||
|
|
@ -384,6 +400,12 @@ void CoreProcess::stop(std::optional<ProcessMode> processModeOption)
|
|||
|
||||
qInfo("stopping core process (%s mode)", qPrintable(processModeToString(processMode)));
|
||||
|
||||
if (m_coreIpcClient) {
|
||||
m_coreIpcClient->disconnectFromServer();
|
||||
delete m_coreIpcClient;
|
||||
m_coreIpcClient = nullptr;
|
||||
}
|
||||
|
||||
if (m_processState == ProcessState::Starting) {
|
||||
qDebug("core process is starting, cancelling");
|
||||
setProcessState(ProcessState::Stopped);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
namespace deskflow::gui {
|
||||
|
||||
namespace ipc {
|
||||
class CoreIpcClient;
|
||||
class DaemonIpcClient;
|
||||
}
|
||||
|
||||
|
|
@ -128,6 +129,7 @@ private:
|
|||
std::optional<ProcessMode> m_lastProcessMode = std::nullopt;
|
||||
QTimer m_retryTimer;
|
||||
int m_connections = 0;
|
||||
deskflow::gui::ipc::CoreIpcClient *m_coreIpcClient = nullptr;
|
||||
deskflow::gui::ipc::DaemonIpcClient *m_daemonIpcClient = nullptr;
|
||||
FileTail *m_daemonFileTail = nullptr;
|
||||
QProcess *m_process = nullptr;
|
||||
|
|
|
|||
25
src/lib/gui/ipc/CoreIpcClient.cpp
Normal file
25
src/lib/gui/ipc/CoreIpcClient.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 "CoreIpcClient.h"
|
||||
|
||||
#include "common/Constants.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QLocalSocket>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
namespace deskflow::gui::ipc {
|
||||
|
||||
const auto kTimeout = 1000;
|
||||
|
||||
CoreIpcClient::CoreIpcClient(QObject *parent) : IpcClient(parent, kCoreIpcName)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
} // namespace deskflow::gui::ipc
|
||||
23
src/lib/gui/ipc/CoreIpcClient.h
Normal file
23
src/lib/gui/ipc/CoreIpcClient.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 "IpcClient.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace deskflow::gui::ipc {
|
||||
|
||||
class CoreIpcClient : public IpcClient
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CoreIpcClient(QObject *parent = nullptr);
|
||||
};
|
||||
|
||||
} // namespace deskflow::gui::ipc
|
||||
Loading…
Reference in a new issue