From 64c4bd1c8047e0465a781db03cd7be761311bdf4 Mon Sep 17 00:00:00 2001 From: SwartzMss Date: Sat, 22 Mar 2025 22:39:18 +0800 Subject: [PATCH 1/4] related #62 --- DeskControler/DeskControler.cpp | 19 +++- DeskControler/DeskControler.h | 2 + DeskControler/NetworkWorker.cpp | 1 + DeskControler/RemoteClipboard.cpp | 140 ++++++++++++++++++++---------- DeskControler/RemoteClipboard.h | 23 +++-- DeskServer/DeskServer.vcxproj | 4 + DeskServer/RelayManager.cpp | 11 +++ DeskServer/RelayManager.h | 2 + 8 files changed, 146 insertions(+), 56 deletions(-) diff --git a/DeskControler/DeskControler.cpp b/DeskControler/DeskControler.cpp index 3a84ada..5ba4e81 100644 --- a/DeskControler/DeskControler.cpp +++ b/DeskControler/DeskControler.cpp @@ -4,7 +4,6 @@ #include #include #include "VideoWidget.h" -#include "RemoteClipboard.h" #include "LogWidget.h" DeskControler::DeskControler(QWidget* parent) @@ -201,10 +200,16 @@ void DeskControler::setupVideoSession(const QString& relayServer, quint16 relayP m_videoReceiver = new VideoReceiver(this); + m_remoteClipboard = new RemoteClipboard(this); + m_remoteClipboard->setRemoteWindow(scrollArea); - RemoteClipboard* remoteClipboard = new RemoteClipboard(this); - - connect(remoteClipboard, &RemoteClipboard::clipboardDataReady, + if (m_remoteClipboard->start()) { + LogWidget::instance()->addLog("Global keyboard hook installed", LogWidget::Info); + } + else { + LogWidget::instance()->addLog("Failed to install global keyboard hook", LogWidget::Error); + } + connect(m_remoteClipboard, &RemoteClipboard::ctrlCPressed, m_videoReceiver, &VideoReceiver::clipboardDataCaptured); connect(videoWidget, &VideoWidget::mouseEventCaptured, @@ -231,6 +236,12 @@ void DeskControler::setupVideoSession(const QString& relayServer, quint16 relayP void DeskControler::destroyVideoSession() { + if (m_remoteClipboard) { + m_remoteClipboard->stop(); + delete m_remoteClipboard; + m_remoteClipboard = nullptr; + } + ui.pushButton->setEnabled(true); ui.ipLineEdit_->setEnabled(true); ui.portLineEdit_->setEnabled(true); diff --git a/DeskControler/DeskControler.h b/DeskControler/DeskControler.h index 488a459..4ad5916 100644 --- a/DeskControler/DeskControler.h +++ b/DeskControler/DeskControler.h @@ -4,6 +4,7 @@ #include "NetworkManager.h" #include "VideoReceiver.h" #include "ui_DeskControler.h" +#include "RemoteClipboard.h" class NetworkManager; @@ -31,4 +32,5 @@ private slots: Ui::DeskControlerClass ui; NetworkManager* m_networkManager; VideoReceiver* m_videoReceiver; + RemoteClipboard* m_remoteClipboard = nullptr; }; diff --git a/DeskControler/NetworkWorker.cpp b/DeskControler/NetworkWorker.cpp index e383782..1456cdf 100644 --- a/DeskControler/NetworkWorker.cpp +++ b/DeskControler/NetworkWorker.cpp @@ -255,6 +255,7 @@ void NetworkWorker::sendClipboardEventToServer(const ClipboardEvent& clipboardEv sendData.append(reinterpret_cast(&len_be), sizeof(len_be)); sendData.append(protobufData); + LogWidget::instance()->addLog("sendClipboardEventToServer", LogWidget::Error); if (m_socket && m_socket->state() == QAbstractSocket::ConnectedState) { m_socket->write(sendData); m_socket->flush(); diff --git a/DeskControler/RemoteClipboard.cpp b/DeskControler/RemoteClipboard.cpp index c2da3d5..123fe1a 100644 --- a/DeskControler/RemoteClipboard.cpp +++ b/DeskControler/RemoteClipboard.cpp @@ -1,67 +1,119 @@ #include "RemoteClipboard.h" +#include "LogWidget.h" #include #include #include #include #include -#include "LogWidget.h" +#include +#include + +HHOOK RemoteClipboard::s_hook = nullptr; +RemoteClipboard* RemoteClipboard::s_instance = nullptr; RemoteClipboard::RemoteClipboard(QObject* parent) : QObject(parent) { - qApp->installEventFilter(this); + s_instance = this; } +RemoteClipboard::~RemoteClipboard() +{ + stop(); + s_instance = nullptr; +} -bool RemoteClipboard::eventFilter(QObject* /*obj*/, QEvent* event) +bool RemoteClipboard::start() { - 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; + if (!s_hook) { + s_hook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(nullptr), 0); + if (!s_hook) { + LogWidget::instance()->addLog("RemoteClipboard: Failed to install global keyboard hook", LogWidget::Error); + return false; } + LogWidget::instance()->addLog("RemoteClipboard: Global keyboard hook installed", LogWidget::Info); } - return QObject::eventFilter(nullptr, event); + return true; } -void RemoteClipboard::sendClipboardData(const QMimeData* mimeData) +void RemoteClipboard::stop() { - 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; - } + if (s_hook) { + UnhookWindowsHookEx(s_hook); + s_hook = nullptr; + LogWidget::instance()->addLog("RemoteClipboard: Global keyboard hook removed", LogWidget::Info); } - 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); +} + +void RemoteClipboard::setRemoteWindow(QWidget* remoteWindow) +{ + m_remoteWindow = remoteWindow; +} + +LRESULT CALLBACK RemoteClipboard::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (s_instance) { + return s_instance->handleKeyEvent(nCode, wParam, lParam); } - else { - LogWidget::instance()->addLog("Control side: Unsupported clipboard data", LogWidget::Warning); - return; + return CallNextHookEx(s_hook, nCode, wParam, lParam); +} + +LRESULT RemoteClipboard::handleKeyEvent(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (nCode == HC_ACTION) { + KBDLLHOOKSTRUCT* pKeyboard = reinterpret_cast(lParam); + // 仅处理按下事件 + if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) { + bool ctrlPressed = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0; + if (ctrlPressed && pKeyboard->vkCode == 'C') { + QWidget* activeWnd = QApplication::activeWindow(); + if (m_remoteWindow && activeWnd == m_remoteWindow) { + // 如果激活窗口是远程桌面,则不处理 Ctrl+C,交由远程桌面处理 + LogWidget::instance()->addLog("Remote window active, ignoring Ctrl+C on control side", LogWidget::Info); + return CallNextHookEx(s_hook, nCode, wParam, lParam); + } + // 读取剪贴板数据,并构造 ClipboardEvent + ClipboardEvent eventMsg; + QClipboard* clipboard = QApplication::clipboard(); + const QMimeData* mimeData = clipboard->mimeData(); + if (mimeData) { + // 如果剪贴板包含文件 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("RemoteClipboard: Captured file data: %1").arg(filePath), LogWidget::Info); + } + else { + LogWidget::instance()->addLog(QString("RemoteClipboard: Failed to open file: %1").arg(filePath), LogWidget::Error); + } + } + // 否则处理文本数据 + else if (mimeData->hasText()) { + TextContent* textContent = eventMsg.mutable_text(); + textContent->set_text_data(mimeData->text().toStdString()); + LogWidget::instance()->addLog("RemoteClipboard: Captured text data", LogWidget::Info); + } + else { + LogWidget::instance()->addLog("RemoteClipboard: Unsupported clipboard data", LogWidget::Warning); + } + } + else { + LogWidget::instance()->addLog("RemoteClipboard: Clipboard is empty", LogWidget::Warning); + } + // 使用 queued 方式发射带参数的信号,确保在 Qt 主线程中处理 + QMetaObject::invokeMethod(this, "ctrlCPressed", Qt::QueuedConnection, + Q_ARG(ClipboardEvent, eventMsg)); + LogWidget::instance()->addLog("RemoteClipboard: Global Ctrl+C detected and clipboard data captured", LogWidget::Info); + // 可选择拦截此事件,或返回 CallNextHookEx 继续传递 + } + } } - emit clipboardDataReady(eventMsg); + return CallNextHookEx(s_hook, nCode, wParam, lParam); } diff --git a/DeskControler/RemoteClipboard.h b/DeskControler/RemoteClipboard.h index f172620..5a4622f 100644 --- a/DeskControler/RemoteClipboard.h +++ b/DeskControler/RemoteClipboard.h @@ -2,24 +2,31 @@ #define REMOTECLIPBOARD_H #include -#include -#include +#include #include "rendezvous.pb.h" +Q_DECLARE_METATYPE(ClipboardEvent) + class RemoteClipboard : public QObject { Q_OBJECT public: explicit RemoteClipboard(QObject* parent = nullptr); + ~RemoteClipboard(); -protected: - bool eventFilter(QObject* obj, QEvent* event) override; + bool start(); + void stop(); -private: - void sendClipboardData(const QMimeData* mimeData); + void setRemoteWindow(QWidget* remoteWindow); +signals: + void ctrlCPressed(const ClipboardEvent& clipboardEvent); +private: + static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); + LRESULT handleKeyEvent(int nCode, WPARAM wParam, LPARAM lParam); -signals: - void clipboardDataReady(const ClipboardEvent& clipboardEvent); + static HHOOK s_hook; + static RemoteClipboard* s_instance; + QWidget* m_remoteWindow = nullptr; }; #endif // REMOTECLIPBOARD_H diff --git a/DeskServer/DeskServer.vcxproj b/DeskServer/DeskServer.vcxproj index c498496..2d18ddb 100644 --- a/DeskServer/DeskServer.vcxproj +++ b/DeskServer/DeskServer.vcxproj @@ -74,6 +74,7 @@ + @@ -104,6 +105,9 @@ + + + diff --git a/DeskServer/RelayManager.cpp b/DeskServer/RelayManager.cpp index 796e4c9..28ef739 100644 --- a/DeskServer/RelayManager.cpp +++ b/DeskServer/RelayManager.cpp @@ -14,6 +14,7 @@ RelayManager::RelayManager(QObject* parent) m_encoder(nullptr) { m_inputSimulator = new RemoteInputSimulator(nullptr); + m_remoteClipboard = new RemoteClipboard(nullptr); } RelayManager::~RelayManager() { @@ -134,6 +135,7 @@ void RelayManager::processReceivedData(const QByteArray& packetData) { LogWidget::instance()->addLog("Failed to parse RendezvousMessage message from RelayManager", LogWidget::Warning); return; } + LogWidget::instance()->addLog("processReceivedData", LogWidget::Warning); if (msg.has_inputcontrolevent()) { const InputControlEvent& event = msg.inputcontrolevent(); if (event.has_mouse_event()) { @@ -152,6 +154,15 @@ void RelayManager::processReceivedData(const QByteArray& packetData) { Q_ARG(int, key), Q_ARG(bool, pressed)); } } + else if (msg.has_clipboardevent()) { + + const ClipboardEvent& clipboardEvent = msg.clipboardevent(); + QMetaObject::invokeMethod(m_remoteClipboard, "onClipboardMessageReceived", Qt::QueuedConnection, + Q_ARG(ClipboardEvent, clipboardEvent)); + } + else { + LogWidget::instance()->addLog("Received unknown message type in RendezvousMessage", LogWidget::Warning); + } } void RelayManager::onEncodedPacketReady(const QByteArray& packet) { diff --git a/DeskServer/RelayManager.h b/DeskServer/RelayManager.h index d5bac23..2a902ca 100644 --- a/DeskServer/RelayManager.h +++ b/DeskServer/RelayManager.h @@ -7,6 +7,7 @@ #include "RemoteInputSimulator.h" #include "ScreenCaptureEncoder.h" #include "RelaySocketWorker.h" +#include "RemoteClipboard.h" class RelayManager : public QObject { Q_OBJECT @@ -44,6 +45,7 @@ private slots: ScreenCaptureEncoder* m_encoder; RemoteInputSimulator* m_inputSimulator; QThread* m_encoderThread; + RemoteClipboard* m_remoteClipboard; }; #endif // RELAYMANAGER_H From 96612f83b8bfab5ce920c325cc55df513edac5bb Mon Sep 17 00:00:00 2001 From: SwartzMss Date: Sat, 22 Mar 2025 22:39:18 +0800 Subject: [PATCH 2/4] related #62 --- DeskControler/DeskControler.cpp | 19 +++- DeskControler/DeskControler.h | 2 + DeskControler/NetworkWorker.cpp | 1 + DeskControler/RemoteClipboard.cpp | 140 ++++++++++++++++++++---------- DeskControler/RemoteClipboard.h | 23 +++-- DeskServer/DeskServer.vcxproj | 4 + DeskServer/RelayManager.cpp | 11 +++ DeskServer/RelayManager.h | 2 + DeskServer/RemoteClipboard.cpp | 53 +++++++++++ DeskServer/RemoteClipboard.h | 17 ++++ 10 files changed, 216 insertions(+), 56 deletions(-) create mode 100644 DeskServer/RemoteClipboard.cpp create mode 100644 DeskServer/RemoteClipboard.h diff --git a/DeskControler/DeskControler.cpp b/DeskControler/DeskControler.cpp index 3a84ada..5ba4e81 100644 --- a/DeskControler/DeskControler.cpp +++ b/DeskControler/DeskControler.cpp @@ -4,7 +4,6 @@ #include #include #include "VideoWidget.h" -#include "RemoteClipboard.h" #include "LogWidget.h" DeskControler::DeskControler(QWidget* parent) @@ -201,10 +200,16 @@ void DeskControler::setupVideoSession(const QString& relayServer, quint16 relayP m_videoReceiver = new VideoReceiver(this); + m_remoteClipboard = new RemoteClipboard(this); + m_remoteClipboard->setRemoteWindow(scrollArea); - RemoteClipboard* remoteClipboard = new RemoteClipboard(this); - - connect(remoteClipboard, &RemoteClipboard::clipboardDataReady, + if (m_remoteClipboard->start()) { + LogWidget::instance()->addLog("Global keyboard hook installed", LogWidget::Info); + } + else { + LogWidget::instance()->addLog("Failed to install global keyboard hook", LogWidget::Error); + } + connect(m_remoteClipboard, &RemoteClipboard::ctrlCPressed, m_videoReceiver, &VideoReceiver::clipboardDataCaptured); connect(videoWidget, &VideoWidget::mouseEventCaptured, @@ -231,6 +236,12 @@ void DeskControler::setupVideoSession(const QString& relayServer, quint16 relayP void DeskControler::destroyVideoSession() { + if (m_remoteClipboard) { + m_remoteClipboard->stop(); + delete m_remoteClipboard; + m_remoteClipboard = nullptr; + } + ui.pushButton->setEnabled(true); ui.ipLineEdit_->setEnabled(true); ui.portLineEdit_->setEnabled(true); diff --git a/DeskControler/DeskControler.h b/DeskControler/DeskControler.h index 488a459..4ad5916 100644 --- a/DeskControler/DeskControler.h +++ b/DeskControler/DeskControler.h @@ -4,6 +4,7 @@ #include "NetworkManager.h" #include "VideoReceiver.h" #include "ui_DeskControler.h" +#include "RemoteClipboard.h" class NetworkManager; @@ -31,4 +32,5 @@ private slots: Ui::DeskControlerClass ui; NetworkManager* m_networkManager; VideoReceiver* m_videoReceiver; + RemoteClipboard* m_remoteClipboard = nullptr; }; diff --git a/DeskControler/NetworkWorker.cpp b/DeskControler/NetworkWorker.cpp index e383782..1456cdf 100644 --- a/DeskControler/NetworkWorker.cpp +++ b/DeskControler/NetworkWorker.cpp @@ -255,6 +255,7 @@ void NetworkWorker::sendClipboardEventToServer(const ClipboardEvent& clipboardEv sendData.append(reinterpret_cast(&len_be), sizeof(len_be)); sendData.append(protobufData); + LogWidget::instance()->addLog("sendClipboardEventToServer", LogWidget::Error); if (m_socket && m_socket->state() == QAbstractSocket::ConnectedState) { m_socket->write(sendData); m_socket->flush(); diff --git a/DeskControler/RemoteClipboard.cpp b/DeskControler/RemoteClipboard.cpp index c2da3d5..123fe1a 100644 --- a/DeskControler/RemoteClipboard.cpp +++ b/DeskControler/RemoteClipboard.cpp @@ -1,67 +1,119 @@ #include "RemoteClipboard.h" +#include "LogWidget.h" #include #include #include #include #include -#include "LogWidget.h" +#include +#include + +HHOOK RemoteClipboard::s_hook = nullptr; +RemoteClipboard* RemoteClipboard::s_instance = nullptr; RemoteClipboard::RemoteClipboard(QObject* parent) : QObject(parent) { - qApp->installEventFilter(this); + s_instance = this; } +RemoteClipboard::~RemoteClipboard() +{ + stop(); + s_instance = nullptr; +} -bool RemoteClipboard::eventFilter(QObject* /*obj*/, QEvent* event) +bool RemoteClipboard::start() { - 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; + if (!s_hook) { + s_hook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(nullptr), 0); + if (!s_hook) { + LogWidget::instance()->addLog("RemoteClipboard: Failed to install global keyboard hook", LogWidget::Error); + return false; } + LogWidget::instance()->addLog("RemoteClipboard: Global keyboard hook installed", LogWidget::Info); } - return QObject::eventFilter(nullptr, event); + return true; } -void RemoteClipboard::sendClipboardData(const QMimeData* mimeData) +void RemoteClipboard::stop() { - 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; - } + if (s_hook) { + UnhookWindowsHookEx(s_hook); + s_hook = nullptr; + LogWidget::instance()->addLog("RemoteClipboard: Global keyboard hook removed", LogWidget::Info); } - 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); +} + +void RemoteClipboard::setRemoteWindow(QWidget* remoteWindow) +{ + m_remoteWindow = remoteWindow; +} + +LRESULT CALLBACK RemoteClipboard::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (s_instance) { + return s_instance->handleKeyEvent(nCode, wParam, lParam); } - else { - LogWidget::instance()->addLog("Control side: Unsupported clipboard data", LogWidget::Warning); - return; + return CallNextHookEx(s_hook, nCode, wParam, lParam); +} + +LRESULT RemoteClipboard::handleKeyEvent(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (nCode == HC_ACTION) { + KBDLLHOOKSTRUCT* pKeyboard = reinterpret_cast(lParam); + // 仅处理按下事件 + if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) { + bool ctrlPressed = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0; + if (ctrlPressed && pKeyboard->vkCode == 'C') { + QWidget* activeWnd = QApplication::activeWindow(); + if (m_remoteWindow && activeWnd == m_remoteWindow) { + // 如果激活窗口是远程桌面,则不处理 Ctrl+C,交由远程桌面处理 + LogWidget::instance()->addLog("Remote window active, ignoring Ctrl+C on control side", LogWidget::Info); + return CallNextHookEx(s_hook, nCode, wParam, lParam); + } + // 读取剪贴板数据,并构造 ClipboardEvent + ClipboardEvent eventMsg; + QClipboard* clipboard = QApplication::clipboard(); + const QMimeData* mimeData = clipboard->mimeData(); + if (mimeData) { + // 如果剪贴板包含文件 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("RemoteClipboard: Captured file data: %1").arg(filePath), LogWidget::Info); + } + else { + LogWidget::instance()->addLog(QString("RemoteClipboard: Failed to open file: %1").arg(filePath), LogWidget::Error); + } + } + // 否则处理文本数据 + else if (mimeData->hasText()) { + TextContent* textContent = eventMsg.mutable_text(); + textContent->set_text_data(mimeData->text().toStdString()); + LogWidget::instance()->addLog("RemoteClipboard: Captured text data", LogWidget::Info); + } + else { + LogWidget::instance()->addLog("RemoteClipboard: Unsupported clipboard data", LogWidget::Warning); + } + } + else { + LogWidget::instance()->addLog("RemoteClipboard: Clipboard is empty", LogWidget::Warning); + } + // 使用 queued 方式发射带参数的信号,确保在 Qt 主线程中处理 + QMetaObject::invokeMethod(this, "ctrlCPressed", Qt::QueuedConnection, + Q_ARG(ClipboardEvent, eventMsg)); + LogWidget::instance()->addLog("RemoteClipboard: Global Ctrl+C detected and clipboard data captured", LogWidget::Info); + // 可选择拦截此事件,或返回 CallNextHookEx 继续传递 + } + } } - emit clipboardDataReady(eventMsg); + return CallNextHookEx(s_hook, nCode, wParam, lParam); } diff --git a/DeskControler/RemoteClipboard.h b/DeskControler/RemoteClipboard.h index f172620..5a4622f 100644 --- a/DeskControler/RemoteClipboard.h +++ b/DeskControler/RemoteClipboard.h @@ -2,24 +2,31 @@ #define REMOTECLIPBOARD_H #include -#include -#include +#include #include "rendezvous.pb.h" +Q_DECLARE_METATYPE(ClipboardEvent) + class RemoteClipboard : public QObject { Q_OBJECT public: explicit RemoteClipboard(QObject* parent = nullptr); + ~RemoteClipboard(); -protected: - bool eventFilter(QObject* obj, QEvent* event) override; + bool start(); + void stop(); -private: - void sendClipboardData(const QMimeData* mimeData); + void setRemoteWindow(QWidget* remoteWindow); +signals: + void ctrlCPressed(const ClipboardEvent& clipboardEvent); +private: + static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); + LRESULT handleKeyEvent(int nCode, WPARAM wParam, LPARAM lParam); -signals: - void clipboardDataReady(const ClipboardEvent& clipboardEvent); + static HHOOK s_hook; + static RemoteClipboard* s_instance; + QWidget* m_remoteWindow = nullptr; }; #endif // REMOTECLIPBOARD_H diff --git a/DeskServer/DeskServer.vcxproj b/DeskServer/DeskServer.vcxproj index c498496..2d18ddb 100644 --- a/DeskServer/DeskServer.vcxproj +++ b/DeskServer/DeskServer.vcxproj @@ -74,6 +74,7 @@ + @@ -104,6 +105,9 @@ + + + diff --git a/DeskServer/RelayManager.cpp b/DeskServer/RelayManager.cpp index 796e4c9..28ef739 100644 --- a/DeskServer/RelayManager.cpp +++ b/DeskServer/RelayManager.cpp @@ -14,6 +14,7 @@ RelayManager::RelayManager(QObject* parent) m_encoder(nullptr) { m_inputSimulator = new RemoteInputSimulator(nullptr); + m_remoteClipboard = new RemoteClipboard(nullptr); } RelayManager::~RelayManager() { @@ -134,6 +135,7 @@ void RelayManager::processReceivedData(const QByteArray& packetData) { LogWidget::instance()->addLog("Failed to parse RendezvousMessage message from RelayManager", LogWidget::Warning); return; } + LogWidget::instance()->addLog("processReceivedData", LogWidget::Warning); if (msg.has_inputcontrolevent()) { const InputControlEvent& event = msg.inputcontrolevent(); if (event.has_mouse_event()) { @@ -152,6 +154,15 @@ void RelayManager::processReceivedData(const QByteArray& packetData) { Q_ARG(int, key), Q_ARG(bool, pressed)); } } + else if (msg.has_clipboardevent()) { + + const ClipboardEvent& clipboardEvent = msg.clipboardevent(); + QMetaObject::invokeMethod(m_remoteClipboard, "onClipboardMessageReceived", Qt::QueuedConnection, + Q_ARG(ClipboardEvent, clipboardEvent)); + } + else { + LogWidget::instance()->addLog("Received unknown message type in RendezvousMessage", LogWidget::Warning); + } } void RelayManager::onEncodedPacketReady(const QByteArray& packet) { diff --git a/DeskServer/RelayManager.h b/DeskServer/RelayManager.h index d5bac23..2a902ca 100644 --- a/DeskServer/RelayManager.h +++ b/DeskServer/RelayManager.h @@ -7,6 +7,7 @@ #include "RemoteInputSimulator.h" #include "ScreenCaptureEncoder.h" #include "RelaySocketWorker.h" +#include "RemoteClipboard.h" class RelayManager : public QObject { Q_OBJECT @@ -44,6 +45,7 @@ private slots: ScreenCaptureEncoder* m_encoder; RemoteInputSimulator* m_inputSimulator; QThread* m_encoderThread; + RemoteClipboard* m_remoteClipboard; }; #endif // RELAYMANAGER_H diff --git a/DeskServer/RemoteClipboard.cpp b/DeskServer/RemoteClipboard.cpp new file mode 100644 index 0000000..98eae32 --- /dev/null +++ b/DeskServer/RemoteClipboard.cpp @@ -0,0 +1,53 @@ +#include "RemoteClipboard.h" +#include +#include +#include +#include +#include +#include +#include "LogWidget.h" + +RemoteClipboard::RemoteClipboard(QObject* parent) + : QObject(parent) +{ +} + +void RemoteClipboard::onClipboardMessageReceived(const ClipboardEvent& clipboardEvent) +{ + LogWidget::instance()->addLog("onClipboardMessageReceived", LogWidget::Error); + switch (clipboardEvent.event_case()) { + case ClipboardEvent::kText: { + // 处理文本数据 + QString text = QString::fromStdString(clipboardEvent.text().text_data()); + QApplication::clipboard()->setText(text); + LogWidget::instance()->addLog("RemoteClipboard: Updated clipboard with text data", LogWidget::Info); + break; + } + case ClipboardEvent::kFile: { + // 处理文件数据 + const FileContent& fileContent = clipboardEvent.file(); + QString fileName = QString::fromStdString(fileContent.file_name()); + // 保存文件到临时目录 + QString tempPath = QDir::tempPath() + "/" + fileName; + QFile file(tempPath); + if (file.open(QIODevice::WriteOnly)) { + file.write(QByteArray::fromStdString(fileContent.file_data())); + file.close(); + LogWidget::instance()->addLog(QString("RemoteClipboard: File saved to %1").arg(tempPath), LogWidget::Info); + // 更新剪贴板为文件 URL,使得粘贴操作可以获取该文件 + QMimeData* mimeData = new QMimeData; + QList urls; + urls.append(QUrl::fromLocalFile(tempPath)); + mimeData->setUrls(urls); + QApplication::clipboard()->setMimeData(mimeData); + } + else { + LogWidget::instance()->addLog("RemoteClipboard: Failed to save file", LogWidget::Error); + } + break; + } + default: + LogWidget::instance()->addLog("RemoteClipboard: Received unknown clipboard event", LogWidget::Warning); + break; + } +} diff --git a/DeskServer/RemoteClipboard.h b/DeskServer/RemoteClipboard.h new file mode 100644 index 0000000..b82b08b --- /dev/null +++ b/DeskServer/RemoteClipboard.h @@ -0,0 +1,17 @@ +#ifndef REMOTECLIPBOARD_H +#define REMOTECLIPBOARD_H + +#include +#include "rendezvous.pb.h" + +class RemoteClipboard : public QObject { + Q_OBJECT +public: + explicit RemoteClipboard(QObject* parent = nullptr); + +public slots: + // 接收远程传来的 ClipboardEvent 消息,并更新系统剪贴板数据 + void onClipboardMessageReceived(const ClipboardEvent& clipboardEvent); +}; + +#endif // REMOTECLIPBOARD_H From 70fd3ad51c9e6d4211fdc7f0d47cf3c07df66daf Mon Sep 17 00:00:00 2001 From: SwartzMss Date: Sun, 23 Mar 2025 17:07:25 +0800 Subject: [PATCH 3/4] fixed #62 Signed-off-by: SwartzMss --- DeskControler/DeskControler.cpp | 4 ++ DeskControler/MessageHandler.cpp | 4 ++ DeskControler/MessageHandler.h | 2 + DeskControler/NetworkWorker.cpp | 3 + DeskControler/NetworkWorker.h | 1 + DeskControler/RemoteClipboard.cpp | 42 +++++++++++++ DeskControler/RemoteClipboard.h | 4 ++ DeskControler/VideoReceiver.cpp | 4 ++ DeskControler/VideoReceiver.h | 2 + DeskServer/RelayManager.cpp | 30 ++++++++++ DeskServer/RelayManager.h | 2 + DeskServer/RemoteClipboard.cpp | 99 +++++++++++++++++++++++++++++++ DeskServer/RemoteClipboard.h | 15 +++++ 13 files changed, 212 insertions(+) diff --git a/DeskControler/DeskControler.cpp b/DeskControler/DeskControler.cpp index 5ba4e81..bdc1ce5 100644 --- a/DeskControler/DeskControler.cpp +++ b/DeskControler/DeskControler.cpp @@ -218,6 +218,10 @@ void DeskControler::setupVideoSession(const QString& relayServer, quint16 relayP QObject::connect(videoWidget, &VideoWidget::keyEventCaptured, m_videoReceiver, &VideoReceiver::keyEventCaptured); + QObject::connect(m_videoReceiver, &VideoReceiver::onClipboardMessageReceived, + m_remoteClipboard, &RemoteClipboard::onClipboardMessageReceived); + + connect(m_videoReceiver, &VideoReceiver::frameReady, this, [videoWidget, scrollArea](const QImage& img) { static bool firstFrame = true; if (firstFrame && !img.isNull()) diff --git a/DeskControler/MessageHandler.cpp b/DeskControler/MessageHandler.cpp index 1ffc967..713faaf 100644 --- a/DeskControler/MessageHandler.cpp +++ b/DeskControler/MessageHandler.cpp @@ -46,6 +46,10 @@ void MessageHandler::processReceivedData(const QByteArray& data) const InpuVideoFrame& frame = msg.inpuvideoframe(); emit InpuVideoFrameReceived(QByteArray::fromStdString(frame.data())); } + else if (msg.has_clipboardevent()) { + const ClipboardEvent& clipboardEvent = msg.clipboardevent(); + emit onClipboardMessageReceived(clipboardEvent); + } else { emit parseError("Received unknown message type"); } diff --git a/DeskControler/MessageHandler.h b/DeskControler/MessageHandler.h index 0d00915..1218384 100644 --- a/DeskControler/MessageHandler.h +++ b/DeskControler/MessageHandler.h @@ -23,4 +23,6 @@ class MessageHandler : public QObject { // 消息解析错误 void parseError(const QString& error); + void onClipboardMessageReceived(const ClipboardEvent& clipboardEvent); + }; diff --git a/DeskControler/NetworkWorker.cpp b/DeskControler/NetworkWorker.cpp index 1456cdf..2efe071 100644 --- a/DeskControler/NetworkWorker.cpp +++ b/DeskControler/NetworkWorker.cpp @@ -11,6 +11,9 @@ NetworkWorker::NetworkWorker(QObject* parent) { connect(&messageHandler, &MessageHandler::InpuVideoFrameReceived, this, &NetworkWorker::packetReady); + + connect(&messageHandler, &MessageHandler::onClipboardMessageReceived, + this, &NetworkWorker::onClipboardMessageReceived); } NetworkWorker::~NetworkWorker() diff --git a/DeskControler/NetworkWorker.h b/DeskControler/NetworkWorker.h index fee59bb..257f444 100644 --- a/DeskControler/NetworkWorker.h +++ b/DeskControler/NetworkWorker.h @@ -28,6 +28,7 @@ public slots: // 网络出错、断开等信号,可以通知主线程 void networkError(const QString& error); void connectedToServer(); + void onClipboardMessageReceived(const ClipboardEvent& clipboardEvent); private slots: void onSocketConnected(); diff --git a/DeskControler/RemoteClipboard.cpp b/DeskControler/RemoteClipboard.cpp index 123fe1a..4c17dea 100644 --- a/DeskControler/RemoteClipboard.cpp +++ b/DeskControler/RemoteClipboard.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,47 @@ void RemoteClipboard::setRemoteWindow(QWidget* remoteWindow) m_remoteWindow = remoteWindow; } + +void RemoteClipboard::onClipboardMessageReceived(const ClipboardEvent& clipboardEvent) +{ + LogWidget::instance()->addLog("onClipboardMessageReceived", LogWidget::Error); + switch (clipboardEvent.event_case()) { + case ClipboardEvent::kText: { + // 处理文本数据 + QString text = QString::fromStdString(clipboardEvent.text().text_data()); + QApplication::clipboard()->setText(text); + LogWidget::instance()->addLog("RemoteClipboard: Updated clipboard with text data", LogWidget::Info); + break; + } + case ClipboardEvent::kFile: { + // 处理文件数据 + const FileContent& fileContent = clipboardEvent.file(); + QString fileName = QString::fromStdString(fileContent.file_name()); + // 保存文件到临时目录 + QString tempPath = QDir::tempPath() + "/" + fileName; + QFile file(tempPath); + if (file.open(QIODevice::WriteOnly)) { + file.write(QByteArray::fromStdString(fileContent.file_data())); + file.close(); + LogWidget::instance()->addLog(QString("RemoteClipboard: File saved to %1").arg(tempPath), LogWidget::Info); + // 更新剪贴板为文件 URL,使得粘贴操作可以获取该文件 + QMimeData* mimeData = new QMimeData; + QList urls; + urls.append(QUrl::fromLocalFile(tempPath)); + mimeData->setUrls(urls); + QApplication::clipboard()->setMimeData(mimeData); + } + else { + LogWidget::instance()->addLog("RemoteClipboard: Failed to save file", LogWidget::Error); + } + break; + } + default: + LogWidget::instance()->addLog("RemoteClipboard: Received unknown clipboard event", LogWidget::Warning); + break; + } +} + LRESULT CALLBACK RemoteClipboard::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (s_instance) { diff --git a/DeskControler/RemoteClipboard.h b/DeskControler/RemoteClipboard.h index 5a4622f..6bfe0f6 100644 --- a/DeskControler/RemoteClipboard.h +++ b/DeskControler/RemoteClipboard.h @@ -20,6 +20,10 @@ class RemoteClipboard : public QObject { signals: void ctrlCPressed(const ClipboardEvent& clipboardEvent); +public slots: + // 接收远程传来的 ClipboardEvent 消息,并更新系统剪贴板数据 + void onClipboardMessageReceived(const ClipboardEvent& clipboardEvent); + private: static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); LRESULT handleKeyEvent(int nCode, WPARAM wParam, LPARAM lParam); diff --git a/DeskControler/VideoReceiver.cpp b/DeskControler/VideoReceiver.cpp index cfda9cc..7629cf5 100644 --- a/DeskControler/VideoReceiver.cpp +++ b/DeskControler/VideoReceiver.cpp @@ -28,6 +28,10 @@ VideoReceiver::VideoReceiver(QObject* parent) m_decoderWorker, &VideoDecoderWorker::decodePacket, Qt::QueuedConnection); + connect(m_netWorker, &NetworkWorker::onClipboardMessageReceived, + this, &VideoReceiver::onClipboardMessageReceived, + Qt::QueuedConnection); + // 瑙g爜瀹屾垚鍚庡洖鍒颁富绾跨▼ connect(m_decoderWorker, &VideoDecoderWorker::frameDecoded, this, &VideoReceiver::onFrameDecoded, diff --git a/DeskControler/VideoReceiver.h b/DeskControler/VideoReceiver.h index 8505361..32a7d3f 100644 --- a/DeskControler/VideoReceiver.h +++ b/DeskControler/VideoReceiver.h @@ -26,6 +26,8 @@ class VideoReceiver : public QObject void frameReady(const QImage& image); // 鍙互鎶 NetworkWorker 鐨勯敊璇浆鍙戝嚭鍘 void networkError(const QString& error); + void onClipboardMessageReceived(const ClipboardEvent& clipboardEvent); + public slots: void mouseEventCaptured(int x, int y, int mask); void keyEventCaptured(int key, bool pressed); diff --git a/DeskServer/RelayManager.cpp b/DeskServer/RelayManager.cpp index 28ef739..b760522 100644 --- a/DeskServer/RelayManager.cpp +++ b/DeskServer/RelayManager.cpp @@ -49,7 +49,9 @@ void RelayManager::start(const QHostAddress& relayAddress, quint16 relayPort, co connect(m_encoderThread, &QThread::started, m_encoder, &ScreenCaptureEncoder::startCapture); connect(m_encoder, &ScreenCaptureEncoder::encodedPacketReady, this, &RelayManager::onEncodedPacketReady); connect(m_encoderThread, &QThread::finished, m_encoder, &QObject::deleteLater); + connect(m_remoteClipboard, &RemoteClipboard::ctrlCPressed, this, &RelayManager::sendClipboardEvent); m_encoderThread->start(); + m_remoteClipboard->start(); } void RelayManager::stop() { @@ -72,6 +74,11 @@ void RelayManager::stop() { m_encoderThread->deleteLater(); m_encoderThread = nullptr; } + if (m_remoteClipboard) { + m_remoteClipboard->stop(); + m_remoteClipboard->deleteLater(); + m_remoteClipboard = nullptr; + } } void RelayManager::onWorkerSocketConnected() { @@ -190,3 +197,26 @@ void RelayManager::onEncodedPacketReady(const QByteArray& packet) { LogWidget::instance()->addLog("RelayManager: Cannot forward packet, TCP connection not established", LogWidget::Warning); } } + +void RelayManager::sendClipboardEvent(const ClipboardEvent& clipboardEvent) +{ + // 组装 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 data(serialized.data(), static_cast(serialized.size())); + quint32 packetSize = static_cast(data.size()); + quint32 bigEndianSize = qToBigEndian(packetSize); + QByteArray header(reinterpret_cast(&bigEndianSize), sizeof(bigEndianSize)); + QByteArray fullData; + fullData.append(header); + fullData.append(data); + QMetaObject::invokeMethod(m_socketWorker, "sendData", Qt::QueuedConnection, + Q_ARG(QByteArray, fullData)); +} diff --git a/DeskServer/RelayManager.h b/DeskServer/RelayManager.h index 2a902ca..6241c0c 100644 --- a/DeskServer/RelayManager.h +++ b/DeskServer/RelayManager.h @@ -30,6 +30,8 @@ private slots: void onWorkerDataReceived(const QByteArray& data); void onWorkerSocketError(const QString& errMsg); void onEncodedPacketReady(const QByteArray& packet); + void sendClipboardEvent(const ClipboardEvent& clipboardEvent); + private: void processReceivedData(const QByteArray& packetData); diff --git a/DeskServer/RemoteClipboard.cpp b/DeskServer/RemoteClipboard.cpp index 98eae32..04e7e86 100644 --- a/DeskServer/RemoteClipboard.cpp +++ b/DeskServer/RemoteClipboard.cpp @@ -2,14 +2,113 @@ #include #include #include +#include #include #include #include +#include #include "LogWidget.h" +HHOOK RemoteClipboard::s_hook = nullptr; +RemoteClipboard* RemoteClipboard::s_instance = nullptr; + RemoteClipboard::RemoteClipboard(QObject* parent) : QObject(parent) { + s_instance = this; + +} + +RemoteClipboard::~RemoteClipboard() +{ + stop(); + s_instance = nullptr; +} + + +bool RemoteClipboard::start() +{ + if (!s_hook) { + s_hook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(nullptr), 0); + if (!s_hook) { + LogWidget::instance()->addLog("RemoteClipboard: Failed to install global keyboard hook", LogWidget::Error); + return false; + } + LogWidget::instance()->addLog("RemoteClipboard: Global keyboard hook installed", LogWidget::Info); + } + return true; +} + +void RemoteClipboard::stop() +{ + if (s_hook) { + UnhookWindowsHookEx(s_hook); + s_hook = nullptr; + LogWidget::instance()->addLog("RemoteClipboard: Global keyboard hook removed", LogWidget::Info); + } +} + + +LRESULT CALLBACK RemoteClipboard::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (s_instance) { + return s_instance->handleKeyEvent(nCode, wParam, lParam); + } + return CallNextHookEx(s_hook, nCode, wParam, lParam); +} + +LRESULT RemoteClipboard::handleKeyEvent(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (nCode == HC_ACTION) { + KBDLLHOOKSTRUCT* pKeyboard = reinterpret_cast(lParam); + // 仅处理按下事件 + if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) { + bool ctrlPressed = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0; + if (ctrlPressed && pKeyboard->vkCode == 'C') { + // 读取剪贴板数据,并构造 ClipboardEvent + ClipboardEvent eventMsg; + QClipboard* clipboard = QApplication::clipboard(); + const QMimeData* mimeData = clipboard->mimeData(); + if (mimeData) { + // 如果剪贴板包含文件 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("RemoteClipboard: Captured file data: %1").arg(filePath), LogWidget::Info); + } + else { + LogWidget::instance()->addLog(QString("RemoteClipboard: Failed to open file: %1").arg(filePath), LogWidget::Error); + } + } + // 否则处理文本数据 + else if (mimeData->hasText()) { + TextContent* textContent = eventMsg.mutable_text(); + textContent->set_text_data(mimeData->text().toStdString()); + LogWidget::instance()->addLog("RemoteClipboard: Captured text data", LogWidget::Info); + } + else { + LogWidget::instance()->addLog("RemoteClipboard: Unsupported clipboard data", LogWidget::Warning); + } + } + else { + LogWidget::instance()->addLog("RemoteClipboard: Clipboard is empty", LogWidget::Warning); + } + // 使用 queued 方式发射带参数的信号,确保在 Qt 主线程中处理 + QMetaObject::invokeMethod(this, "ctrlCPressed", Qt::QueuedConnection, + Q_ARG(ClipboardEvent, eventMsg)); + LogWidget::instance()->addLog("RemoteClipboard: Global Ctrl+C detected and clipboard data captured", LogWidget::Info); + // 可选择拦截此事件,或返回 CallNextHookEx 继续传递 + } + } + } + return CallNextHookEx(s_hook, nCode, wParam, lParam); } void RemoteClipboard::onClipboardMessageReceived(const ClipboardEvent& clipboardEvent) diff --git a/DeskServer/RemoteClipboard.h b/DeskServer/RemoteClipboard.h index b82b08b..193488a 100644 --- a/DeskServer/RemoteClipboard.h +++ b/DeskServer/RemoteClipboard.h @@ -2,16 +2,31 @@ #define REMOTECLIPBOARD_H #include +#include #include "rendezvous.pb.h" class RemoteClipboard : public QObject { Q_OBJECT public: explicit RemoteClipboard(QObject* parent = nullptr); + ~RemoteClipboard(); + + bool start(); + void stop(); + +signals: + void ctrlCPressed(const ClipboardEvent& clipboardEvent); public slots: // 接收远程传来的 ClipboardEvent 消息,并更新系统剪贴板数据 void onClipboardMessageReceived(const ClipboardEvent& clipboardEvent); + +private: + static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); + LRESULT handleKeyEvent(int nCode, WPARAM wParam, LPARAM lParam); + + static HHOOK s_hook; + static RemoteClipboard* s_instance; }; #endif // REMOTECLIPBOARD_H From b5a387cc8c4c7c9380cae92a1d031fb52ddf2ca8 Mon Sep 17 00:00:00 2001 From: SwartzMss Date: Sun, 23 Mar 2025 17:32:17 +0800 Subject: [PATCH 4/4] sa --- DeskControler/RemoteClipboard.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DeskControler/RemoteClipboard.h b/DeskControler/RemoteClipboard.h index 711f97f..704e3a8 100644 --- a/DeskControler/RemoteClipboard.h +++ b/DeskControler/RemoteClipboard.h @@ -1,5 +1,7 @@ +#ifndef REMOTECLIPBOARD_H #define REMOTECLIPBOARD_H + #include #include #include "rendezvous.pb.h"