Skip to content
Merged

63 #67

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions DeskControler/DeskControler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
4 changes: 4 additions & 0 deletions DeskControler/MessageHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
2 changes: 2 additions & 0 deletions DeskControler/MessageHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ class MessageHandler : public QObject {
// 消息解析错误
void parseError(const QString& error);

void onClipboardMessageReceived(const ClipboardEvent& clipboardEvent);

};
3 changes: 3 additions & 0 deletions DeskControler/NetworkWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ NetworkWorker::NetworkWorker(QObject* parent)
{
connect(&messageHandler, &MessageHandler::InpuVideoFrameReceived,
this, &NetworkWorker::packetReady);

connect(&messageHandler, &MessageHandler::onClipboardMessageReceived,
this, &NetworkWorker::onClipboardMessageReceived);
}

NetworkWorker::~NetworkWorker()
Expand Down
1 change: 1 addition & 0 deletions DeskControler/NetworkWorker.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public slots:
// 网络出错、断开等信号,可以通知主线程
void networkError(const QString& error);
void connectedToServer();
void onClipboardMessageReceived(const ClipboardEvent& clipboardEvent);

private slots:
void onSocketConnected();
Expand Down
44 changes: 43 additions & 1 deletion DeskControler/RemoteClipboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <QClipboard>
#include <QKeyEvent>
#include <QFile>
#include <QDir>
#include <QFileInfo>
#include <QMetaObject>
#include <QtCore/QMimeData>
Expand Down Expand Up @@ -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<QUrl> 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) {
Expand Down Expand Up @@ -116,4 +158,4 @@ LRESULT RemoteClipboard::handleKeyEvent(int nCode, WPARAM wParam, LPARAM lParam)
}
}
return CallNextHookEx(s_hook, nCode, wParam, lParam);
}
}
7 changes: 6 additions & 1 deletion DeskControler/RemoteClipboard.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef REMOTECLIPBOARD_H
#define REMOTECLIPBOARD_H


#include <QObject>
#include <windows.h>
#include "rendezvous.pb.h"
Expand All @@ -20,6 +21,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);
Expand All @@ -29,4 +34,4 @@ class RemoteClipboard : public QObject {
QWidget* m_remoteWindow = nullptr;
};

#endif // REMOTECLIPBOARD_H
#endif // REMOTECLIPBOARD_H
4 changes: 4 additions & 0 deletions DeskControler/VideoReceiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions DeskControler/VideoReceiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
30 changes: 30 additions & 0 deletions DeskServer/RelayManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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() {
Expand Down Expand Up @@ -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<int>(serialized.size()));
quint32 packetSize = static_cast<quint32>(data.size());
quint32 bigEndianSize = qToBigEndian(packetSize);
QByteArray header(reinterpret_cast<const char*>(&bigEndianSize), sizeof(bigEndianSize));
QByteArray fullData;
fullData.append(header);
fullData.append(data);
QMetaObject::invokeMethod(m_socketWorker, "sendData", Qt::QueuedConnection,
Q_ARG(QByteArray, fullData));
}
2 changes: 2 additions & 0 deletions DeskServer/RelayManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
101 changes: 100 additions & 1 deletion DeskServer/RemoteClipboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,113 @@
#include <QApplication>
#include <QClipboard>
#include <QMimeData>
#include <QKeyEvent>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QMetaObject>
#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<KBDLLHOOKSTRUCT*>(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)
Expand Down Expand Up @@ -50,4 +149,4 @@ void RemoteClipboard::onClipboardMessageReceived(const ClipboardEvent& clipboard
LogWidget::instance()->addLog("RemoteClipboard: Received unknown clipboard event", LogWidget::Warning);
break;
}
}
}
17 changes: 16 additions & 1 deletion DeskServer/RemoteClipboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,31 @@
#define REMOTECLIPBOARD_H

#include <QObject>
#include <windows.h>
#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
#endif // REMOTECLIPBOARD_H
Loading