From b7798a09a34266f8b5be9ebb278a80b61c0f3a95 Mon Sep 17 00:00:00 2001 From: SwartzMss Date: Thu, 20 Mar 2025 22:14:20 +0800 Subject: [PATCH 1/3] related #62 --- ReadMe.md | 10 +++++++++- RendezvousProto/proto/rendezvous.proto | 17 ++++++++++++++++ diagrams/ctrlcFromControl.puml | 27 ++++++++++++++++++++++++++ diagrams/ctrlcFromServer.puml | 27 ++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 diagrams/ctrlcFromControl.puml create mode 100644 diagrams/ctrlcFromServer.puml diff --git a/ReadMe.md b/ReadMe.md index 8b4115c..9e60a57 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -2,7 +2,6 @@ [![CI Build](https://github.com/SwartzMss/SimpleRustDesk/actions/workflows/msbuild.yml/badge.svg)](https://github.com/SwartzMss/SimpleRustDesk/actions/workflows/msbuild.yml) [![Render PlantUML Diagrams](https://github.com/SwartzMss/SimpleRustDesk/actions/workflows/plantuml-render.yml/badge.svg)](https://github.com/SwartzMss/SimpleRustDesk/actions/workflows/plantuml-render.yml) -[![Package Status](https://github.com/SwartzMss/SimpleRustDesk/actions/workflows/package.yml/badge.svg)](https://github.com/SwartzMss/SimpleRustDesk/actions/workflows/package.yml) SimpleRustDesk 是一个远程桌面控制系统示例项目,参考 RustDesk 的设计思想,展示了如何构建一个完整的远程控制框架。系统通过信令交互和数据中继,实现被控端与控制端之间的远程连接与数据传输,主要包含以下模块: @@ -29,6 +28,15 @@ SimpleRustDesk 是一个远程桌面控制系统示例项目,参考 RustDesk ![鼠标键盘控制 UML](diagrams/output/MouseKeyInput.svg) +## 文件传输-从控制端复制示意图 (ctrlcFromControl.puml) + +![ctrlcFromControl](./diagrams/ctrlcFromControl.png) + +## 文件传输-从被控制端复制示意图 (ctrlcFromServer.puml) + +![ctrlcFromServer](./diagrams/ctrlcFromServer.png) + + ## 注意事项 - 本项目为示例性质,主要展示远程控制系统的整体架构和基本实现。 diff --git a/RendezvousProto/proto/rendezvous.proto b/RendezvousProto/proto/rendezvous.proto index 60c5ba6..62a40b6 100644 --- a/RendezvousProto/proto/rendezvous.proto +++ b/RendezvousProto/proto/rendezvous.proto @@ -56,6 +56,22 @@ message InputControlEvent { } } +message ClipboardEvent { + oneof event { + TextContent text = 1; + FileContent file = 2; + } +} + +message TextContent { + string text_data = 1; +} + +message FileContent { + bytes file_data = 1; + string file_name = 2; +} + message InpuVideoFrame{ bytes data = 1; @@ -87,6 +103,7 @@ message RendezvousMessage { PunchHoleSent punch_hole_sent = 8; InpuVideoFrame inpuVideoFrame = 9; InputControlEvent inputControlEvent = 10; + ClipboardEvent clipboardEvent =11; } } \ No newline at end of file diff --git a/diagrams/ctrlcFromControl.puml b/diagrams/ctrlcFromControl.puml new file mode 100644 index 0000000..db90f83 --- /dev/null +++ b/diagrams/ctrlcFromControl.puml @@ -0,0 +1,27 @@ +@startuml +actor "控制端用户" as UserC +actor "被控制的DeskServer用户" as UserS + +participant "RemoteClipboard (DeskControl)" as RC_C +participant "RelayManager (DeskControl)" as RM_C +participant "RelayServer" as Network +participant "NetworkWorker (DeskServer)" as NW_S +participant "RemoteClipboard (DeskServer)" as RC_S +participant "系统" as System + +== 控制端发送剪贴板数据 == +UserC -> RC_C: 按 Ctrl+C +RC_C-> RC_C: eventFilter() 捕获 Ctrl+C +RC_C -> RC_C: 读取本地剪贴板数据 +RC_C -> RM_C: sendClipboardMessage(ClipboardEvent) +RM_C -> Network: 传输 ClipboardEvent + +== 被控制端接收剪贴板数据 == +Network -> NW_S: 转发 ClipboardEvent +NW_S -> RC_S: 发射 clipboardMessageReceived(ClipboardEvent) +RC_S -> RC_S: receiveClipboardData(ClipboardEvent)\n更新系统剪贴板数据 + +== 被控制端粘贴操作 == +UserS -> RC_S: 按 Ctrl+V +RC_S -> System: 粘贴更新后的剪贴板数据 +@enduml \ No newline at end of file diff --git a/diagrams/ctrlcFromServer.puml b/diagrams/ctrlcFromServer.puml new file mode 100644 index 0000000..bf9c06d --- /dev/null +++ b/diagrams/ctrlcFromServer.puml @@ -0,0 +1,27 @@ +@startuml +actor "被控制的DeskServer" as UserS +actor "控制端DeskControl" as UserC + +participant "DeskServer" as DS +participant "RemoteClipboard (DeskServer)" as RC_S +participant "RelayManager(DeskServer)" as PC_S +participant "RelayServer" as Network +participant "NetworkWorker(DeskControl)" as PC_C +participant "RemoteClipboard (DeskControl)" as RC_C + +== DeskServer 发送剪贴板数据 == +UserS -> DS: 按 Ctrl+C +DS -> RC_S: eventFilter() 捕获 Ctrl+C +RC_S -> RC_S: 读取本地剪贴板数据 +RC_S -> PC_S: sendClipboardMessage(ClipboardEvent) +PC_S -> Network: 传输 ClipboardEvent + +== DeskControl 接收剪贴板数据 == +Network -> PC_C: 接收 ClipboardEvent +PC_C -> RC_C: 发射 clipboardMessageReceived(ClipboardEvent) +RC_C -> RC_C: receiveClipboardData(ClipboardEvent)\n系统剪贴板数据更新 + +== DeskControl 粘贴操作 == +UserC -> RC_C: 按 Ctrl+V +RC_C-> System: 粘贴更新后的剪贴板数据 +@enduml \ No newline at end of file From 6351b8a677452909f47a31e21b09fa8ab15a89ee Mon Sep 17 00:00:00 2001 From: SwartzMss Date: Thu, 20 Mar 2025 14:15:14 +0000 Subject: [PATCH 2/3] Update rendered UML diagrams --- diagrams/output/ctrlcFromControl.svg | 1 + diagrams/output/ctrlcFromServer.svg | 1 + 2 files changed, 2 insertions(+) create mode 100644 diagrams/output/ctrlcFromControl.svg create mode 100644 diagrams/output/ctrlcFromServer.svg diff --git a/diagrams/output/ctrlcFromControl.svg b/diagrams/output/ctrlcFromControl.svg new file mode 100644 index 0000000..440967f --- /dev/null +++ b/diagrams/output/ctrlcFromControl.svg @@ -0,0 +1 @@ +控制端用户控制端用户被控制的DeskServer用户被控制的DeskServer用户RemoteClipboard (DeskControl)RemoteClipboard (DeskControl)RelayManager (DeskControl)RelayManager (DeskControl)RelayServerRelayServerNetworkWorker (DeskServer)NetworkWorker (DeskServer)RemoteClipboard (DeskServer)RemoteClipboard (DeskServer)系统系统控制端发送剪贴板数据按 Ctrl+CeventFilter() 捕获 Ctrl+C读取本地剪贴板数据sendClipboardMessage(ClipboardEvent)传输 ClipboardEvent被控制端接收剪贴板数据转发 ClipboardEvent发射 clipboardMessageReceived(ClipboardEvent)receiveClipboardData(ClipboardEvent)更新系统剪贴板数据被控制端粘贴操作按 Ctrl+V粘贴更新后的剪贴板数据 \ No newline at end of file diff --git a/diagrams/output/ctrlcFromServer.svg b/diagrams/output/ctrlcFromServer.svg new file mode 100644 index 0000000..85f1bac --- /dev/null +++ b/diagrams/output/ctrlcFromServer.svg @@ -0,0 +1 @@ +被控制的DeskServer被控制的DeskServer控制端DeskControl控制端DeskControlDeskServerDeskServerRemoteClipboard (DeskServer)RemoteClipboard (DeskServer)RelayManager(DeskServer)RelayManager(DeskServer)RelayServerRelayServerNetworkWorker(DeskControl)NetworkWorker(DeskControl)RemoteClipboard (DeskControl)RemoteClipboard (DeskControl)SystemSystemDeskServer 发送剪贴板数据按 Ctrl+CeventFilter() 捕获 Ctrl+C读取本地剪贴板数据sendClipboardMessage(ClipboardEvent)传输 ClipboardEventDeskControl 接收剪贴板数据接收 ClipboardEvent发射 clipboardMessageReceived(ClipboardEvent)receiveClipboardData(ClipboardEvent)系统剪贴板数据更新DeskControl 粘贴操作按 Ctrl+V粘贴更新后的剪贴板数据 \ No newline at end of file From 38c3cdc8fddfba0479e7342a6d970a138ccdc95b Mon Sep 17 00:00:00 2001 From: SwartzMss Date: Thu, 20 Mar 2025 23:16:09 +0800 Subject: [PATCH 3/3] related #62 --- DeskControler/DeskControler.cpp | 6 +++ DeskControler/DeskControler.vcxproj | 4 ++ DeskControler/NetworkWorker.cpp | 34 +++++++++++++++ DeskControler/NetworkWorker.h | 1 + DeskControler/RemoteClipboard.cpp | 67 +++++++++++++++++++++++++++++ DeskControler/RemoteClipboard.h | 25 +++++++++++ DeskControler/VideoReceiver.cpp | 10 ++++- DeskControler/VideoReceiver.h | 2 + DeskControler/VideoWidget.cpp | 1 + DeskControler/VideoWidget.h | 1 + ReadMe.md | 8 ++-- 11 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 DeskControler/RemoteClipboard.cpp create mode 100644 DeskControler/RemoteClipboard.h diff --git a/DeskControler/DeskControler.cpp b/DeskControler/DeskControler.cpp index 882a5eb..3a84ada 100644 --- a/DeskControler/DeskControler.cpp +++ b/DeskControler/DeskControler.cpp @@ -4,6 +4,7 @@ #include #include #include "VideoWidget.h" +#include "RemoteClipboard.h" #include "LogWidget.h" DeskControler::DeskControler(QWidget* parent) @@ -201,6 +202,11 @@ void DeskControler::setupVideoSession(const QString& relayServer, quint16 relayP m_videoReceiver = new VideoReceiver(this); + RemoteClipboard* remoteClipboard = new RemoteClipboard(this); + + connect(remoteClipboard, &RemoteClipboard::clipboardDataReady, + m_videoReceiver, &VideoReceiver::clipboardDataCaptured); + connect(videoWidget, &VideoWidget::mouseEventCaptured, m_videoReceiver, &VideoReceiver::mouseEventCaptured); diff --git a/DeskControler/DeskControler.vcxproj b/DeskControler/DeskControler.vcxproj index 054de3d..2dfa9bd 100644 --- a/DeskControler/DeskControler.vcxproj +++ b/DeskControler/DeskControler.vcxproj @@ -73,6 +73,7 @@ + @@ -104,6 +105,9 @@ + + + diff --git a/DeskControler/NetworkWorker.cpp b/DeskControler/NetworkWorker.cpp index f183aab..e383782 100644 --- a/DeskControler/NetworkWorker.cpp +++ b/DeskControler/NetworkWorker.cpp @@ -228,6 +228,40 @@ void NetworkWorker::sendKeyEventToServer(int key, bool pressed) } } +void NetworkWorker::sendClipboardEventToServer(const ClipboardEvent& clipboardEvent) +{ + if (m_socket->state() != QAbstractSocket::ConnectedState) { + return; // û RelayServerͲ + } + + // װ ClipboardEvent Ϣ RendezvousMessage + RendezvousMessage msg; + *msg.mutable_clipboardevent() = clipboardEvent; + + std::string serialized; + if (!msg.SerializeToString(&serialized)) { + LogWidget::instance()->addLog("Failed to serialize ClipboardEvent message", LogWidget::Error); + return; + } + + QByteArray protobufData(serialized.data(), serialized.size()); + + // 㳤ͷ + quint32 len = static_cast(protobufData.size()); + quint32 len_be = qToBigEndian(len); // ת + + // + QByteArray sendData; + sendData.append(reinterpret_cast(&len_be), sizeof(len_be)); + sendData.append(protobufData); + + if (m_socket && m_socket->state() == QAbstractSocket::ConnectedState) { + m_socket->write(sendData); + m_socket->flush(); + } +} + + void NetworkWorker::onSocketError(QAbstractSocket::SocketError socketError) { diff --git a/DeskControler/NetworkWorker.h b/DeskControler/NetworkWorker.h index 5018736..fee59bb 100644 --- a/DeskControler/NetworkWorker.h +++ b/DeskControler/NetworkWorker.h @@ -19,6 +19,7 @@ public slots: void cleanup(); void sendMouseEventToServer(int x, int y, int mask); void sendKeyEventToServer(int key, bool pressed); + void sendClipboardEventToServer(const ClipboardEvent& clipboardEvent); signals: diff --git a/DeskControler/RemoteClipboard.cpp b/DeskControler/RemoteClipboard.cpp new file mode 100644 index 0000000..c2da3d5 --- /dev/null +++ b/DeskControler/RemoteClipboard.cpp @@ -0,0 +1,67 @@ +#include "RemoteClipboard.h" +#include +#include +#include +#include +#include +#include "LogWidget.h" + +RemoteClipboard::RemoteClipboard(QObject* parent) + : QObject(parent) +{ + qApp->installEventFilter(this); +} + + +bool RemoteClipboard::eventFilter(QObject* /*obj*/, QEvent* event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(event); + if ((keyEvent->modifiers() & Qt::ControlModifier) && keyEvent->key() == Qt::Key_C) { + QClipboard* clipboard = QApplication::clipboard(); + const QMimeData* mimeData = clipboard->mimeData(); + if (mimeData) { + LogWidget::instance()->addLog("Control side: Detected Ctrl+C, sending clipboard data", LogWidget::Info); + sendClipboardData(mimeData); + } + else { + LogWidget::instance()->addLog("Control side: Clipboard is empty", LogWidget::Warning); + } + return true; + } + } + return QObject::eventFilter(nullptr, event); +} + +void RemoteClipboard::sendClipboardData(const QMimeData* mimeData) +{ + ClipboardEvent eventMsg; + // аļ URLȴļ + if (mimeData->hasUrls() && !mimeData->urls().isEmpty()) { + QString filePath = mimeData->urls().first().toLocalFile(); + QFile file(filePath); + if (file.open(QIODevice::ReadOnly)) { + QByteArray data = file.readAll(); + file.close(); + FileContent* fileContent = eventMsg.mutable_file(); + fileContent->set_file_data(data.toStdString()); + QFileInfo fileInfo(filePath); + fileContent->set_file_name(fileInfo.fileName().toStdString()); + LogWidget::instance()->addLog(QString("Control side: Copied file: %1").arg(filePath), LogWidget::Info); + } + else { + LogWidget::instance()->addLog(QString("Control side: Failed to open file: %1").arg(filePath), LogWidget::Error); + return; + } + } + else if (mimeData->hasText()) { + TextContent* textContent = eventMsg.mutable_text(); + textContent->set_text_data(mimeData->text().toStdString()); + LogWidget::instance()->addLog("Control side: Copied text data", LogWidget::Info); + } + else { + LogWidget::instance()->addLog("Control side: Unsupported clipboard data", LogWidget::Warning); + return; + } + emit clipboardDataReady(eventMsg); +} diff --git a/DeskControler/RemoteClipboard.h b/DeskControler/RemoteClipboard.h new file mode 100644 index 0000000..f172620 --- /dev/null +++ b/DeskControler/RemoteClipboard.h @@ -0,0 +1,25 @@ +#ifndef REMOTECLIPBOARD_H +#define REMOTECLIPBOARD_H + +#include +#include +#include +#include "rendezvous.pb.h" + +class RemoteClipboard : public QObject { + Q_OBJECT +public: + explicit RemoteClipboard(QObject* parent = nullptr); + +protected: + bool eventFilter(QObject* obj, QEvent* event) override; + +private: + void sendClipboardData(const QMimeData* mimeData); + + +signals: + void clipboardDataReady(const ClipboardEvent& clipboardEvent); +}; + +#endif // REMOTECLIPBOARD_H diff --git a/DeskControler/VideoReceiver.cpp b/DeskControler/VideoReceiver.cpp index e69c417..cfda9cc 100644 --- a/DeskControler/VideoReceiver.cpp +++ b/DeskControler/VideoReceiver.cpp @@ -102,4 +102,12 @@ void VideoReceiver::keyEventCaptured(int key, bool pressed) Qt::QueuedConnection, Q_ARG(int, key), Q_ARG(bool, pressed)); -} \ No newline at end of file +} + +void VideoReceiver::clipboardDataCaptured(const ClipboardEvent& clipboardEvent) +{ + QMetaObject::invokeMethod(m_netWorker, + "sendClipboardEventToServer", + Qt::QueuedConnection, + Q_ARG(ClipboardEvent, clipboardEvent)); +} diff --git a/DeskControler/VideoReceiver.h b/DeskControler/VideoReceiver.h index 15e9fd4..8505361 100644 --- a/DeskControler/VideoReceiver.h +++ b/DeskControler/VideoReceiver.h @@ -4,6 +4,7 @@ #include #include #include +#include "rendezvous.pb.h" class NetworkWorker; class VideoDecoderWorker; @@ -28,6 +29,7 @@ class VideoReceiver : public QObject public slots: void mouseEventCaptured(int x, int y, int mask); void keyEventCaptured(int key, bool pressed); + void clipboardDataCaptured(const ClipboardEvent& clipboardEvent); private slots: // 当解码线程发出 frameDecoded 时调用 diff --git a/DeskControler/VideoWidget.cpp b/DeskControler/VideoWidget.cpp index f581121..60bb767 100644 --- a/DeskControler/VideoWidget.cpp +++ b/DeskControler/VideoWidget.cpp @@ -20,6 +20,7 @@ void VideoWidget::setFrame(const QImage& image) setMinimumSize(currentFrame.size()); resize(currentFrame.size()); m_firstFrame = false; + } update(); diff --git a/DeskControler/VideoWidget.h b/DeskControler/VideoWidget.h index 03d9948..59c6d35 100644 --- a/DeskControler/VideoWidget.h +++ b/DeskControler/VideoWidget.h @@ -6,6 +6,7 @@ #include #include + enum MouseMask { MouseMove = 0x01, // ƶ MouseLeftDown = 0x02, // diff --git a/ReadMe.md b/ReadMe.md index 9e60a57..6b55153 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -28,13 +28,13 @@ SimpleRustDesk 是一个远程桌面控制系统示例项目,参考 RustDesk ![鼠标键盘控制 UML](diagrams/output/MouseKeyInput.svg) -## 文件传输-从控制端复制示意图 (ctrlcFromControl.puml) +## 文件传输-从控制端复制 UML 图 -![ctrlcFromControl](./diagrams/ctrlcFromControl.png) +![ctrlcFromControl](./diagrams/output/ctrlcFromControl.png) -## 文件传输-从被控制端复制示意图 (ctrlcFromServer.puml) +## 文件传输-从被控制端复制 UML 图 -![ctrlcFromServer](./diagrams/ctrlcFromServer.png) +![ctrlcFromServer](./diagrams/output/ctrlcFromServer.png) ## 注意事项