玉兔远程控制 0.0.31
载入中...
搜索中...
未找到
ConnectDesktop.cpp
1// Author: Kang Lin <kl222@126.com>
2
3#include <QApplication>
4#include <QClipboard>
5#include <QTimer>
6#include <QLoggingCategory>
7#include <QWheelEvent>
8#include <QVideoFrame>
9#include <QDesktopServices>
10
11#include "ConnectDesktop.h"
12#include "ConnecterThread.h"
13
14static Q_LOGGING_CATEGORY(log, "Client.Connect.Desktop")
15static Q_LOGGING_CATEGORY(logMouse, "Client.Connect.Desktop.Mouse")
16
17#define TypeRecordVideo (QEvent::User + 1)
18class QRecordVideoEvent : public QEvent
19{
20public:
21 QRecordVideoEvent(const QImage& img): QEvent((QEvent::Type)TypeRecordVideo)
22 {
23 m_Image = img;
24 }
25 QImage GetImage()
26 {
27 return m_Image;
28 }
29private:
30 QImage m_Image;
31};
32
33int g_QtKeyboardModifiers = qRegisterMetaType<Qt::KeyboardModifiers>("KeyboardModifiers");
34int g_QtMouseButtons = qRegisterMetaType<Qt::MouseButtons>("MouseButtons");
35int g_QtMouseButton = qRegisterMetaType<Qt::MouseButton>("MouseButton");
36int g_QMessageBox_Icon = qRegisterMetaType<Qt::MouseButton>("QMessageBox::Icon");
37
38CConnectDesktop::CConnectDesktop(CConnecter *pConnecter, bool bDirectConnection)
39 : CConnect(pConnecter)
40#if HAVE_QT6_RECORD
41 , m_pParameterRecord(nullptr)
42 , m_VideoFrameInput(this)
43 , m_AudioBufferInput(this)
44 , m_AudioBufferOutput(this)
45#endif
46{
47 if(pConnecter) {
48 CFrmScroll* pScroll = qobject_cast<CFrmScroll*>(pConnecter->GetViewer());
49 if(pScroll) {
50 CFrmViewer* pView = pScroll->GetViewer();
51 if(pView)
52 SetViewer(pView, bDirectConnection);
53 else {
54 QString szErr = pConnecter->metaObject()->className();
55 szErr += "::GetViewer() is not CFrmViewer";
56 qWarning(log) << szErr.toStdString().c_str();
57 }
58 } else {
59 QString szErr = pConnecter->metaObject()->className();
60 szErr += "::GetViewer() is not CFrmScroll";
61 qWarning(log) << szErr.toStdString().c_str();
62 }
63 SetConnecter(pConnecter);
64 }
65
66#if HAVE_QT6_RECORD
67 bool check = connect(
68 &m_Recorder, &QMediaRecorder::errorOccurred,
69 this, [&](QMediaRecorder::Error error, const QString &errorString) {
70 qDebug(log) << "Recorder error occurred:" << error << errorString;
71 slotRecord(false);
72 emit sigError(error, errorString);
73 });
74 Q_ASSERT(check);
75 check = connect(
76 &m_Recorder, &QMediaRecorder::recorderStateChanged,
77 this, [&](QMediaRecorder::RecorderState state){
78 qDebug(log) << "Recorder state changed:" << state;
79 if(QMediaRecorder::StoppedState == state)
80 {
81 slotRecord(false);
82 if(m_pParameterRecord) {
83 qDebug(log) << "End action:"
84 << m_pParameterRecord->GetEndAction()
85 << m_Recorder.actualLocation();
86 switch(m_pParameterRecord->GetEndAction())
87 {
88 case CParameterRecord::ENDACTION::OpenFile:
89 QDesktopServices::openUrl(m_Recorder.actualLocation());
90 break;
91 case CParameterRecord::ENDACTION::OpenFolder: {
92 QFileInfo fi(m_Recorder.actualLocation().toLocalFile());
93 QDesktopServices::openUrl(
94 QUrl::fromLocalFile(fi.absolutePath()));
95 break;
96 }
97 default:
98 break;
99 }
100 }
101 }
102 });
103 Q_ASSERT(check);
104 check = connect(&m_Recorder, &QMediaRecorder::actualLocationChanged,
105 this, [&](const QUrl &location){
106 qInfo(log) << "Recorder actual location changed:" << location;
107 });
108 Q_ASSERT(check);
109#endif
110}
111
112CConnectDesktop::~CConnectDesktop()
113{
114 qDebug(log) << "CConnectDesktop::~CConnectDesktop()";
115}
116
117int CConnectDesktop::SetConnecter(CConnecter* pConnecter)
118{
119 qDebug(log) << "CConnectDesktop::SetConnecter" << pConnecter;
120 Q_ASSERT(pConnecter);
121 if(!pConnecter) return -1;
122
123 bool check = false;
124 check = connect(this, SIGNAL(sigServerName(const QString&)),
125 pConnecter, SLOT(slotSetServerName(const QString&)));
126 Q_ASSERT(check);
127 check = connect(pConnecter, SIGNAL(sigClipBoardChanged()),
128 this, SLOT(slotClipBoardChanged()));
129 Q_ASSERT(check);
130 check = connect(this, SIGNAL(sigSetClipboard(QMimeData*)),
131 pConnecter, SLOT(slotSetClipboard(QMimeData*)));
132 Q_ASSERT(check);
133#if HAVE_QT6_RECORD
134 CConnecterThread* p = qobject_cast<CConnecterThread*>(pConnecter);
135 if(p) {
136 m_pParameterRecord = &p->GetParameter()->m_Record;
137 check = connect(p, SIGNAL(sigRecord(bool)),
138 this, SLOT(slotRecord(bool)));
139 Q_ASSERT(check);
140
141 check = connect(p, SIGNAL(sigRecordPause(bool)),
142 this, SLOT(slotRecordPause(bool)));
143 Q_ASSERT(check);
144 check = connect(
145 &m_Recorder,
146 SIGNAL(recorderStateChanged(QMediaRecorder::RecorderState)),
147 p, SLOT(slotRecorderStateChanged(QMediaRecorder::RecorderState)));
148 Q_ASSERT(check);
149 }
150#endif
151 return 0;
152}
153
154int CConnectDesktop::SetViewer(CFrmViewer *pView, bool bDirectConnection)
155{
156 Q_ASSERT(pView);
157 if(!pView) return -1;
158
159 bool check = false;
160 check = connect(this, SIGNAL(sigConnected()), pView, SLOT(slotConnected()));
161 Q_ASSERT(check);
162 check = connect(this, SIGNAL(sigSetDesktopSize(int, int)),
163 pView, SLOT(slotSetDesktopSize(int, int)));
164 Q_ASSERT(check);
165 check = connect(this, SIGNAL(sigServerName(const QString&)),
166 pView, SLOT(slotSetName(const QString&)));
167 Q_ASSERT(check);
168
169 check = connect(this, SIGNAL(sigUpdateRect(const QRect&, const QImage&)),
170 pView, SLOT(slotUpdateRect(const QRect&, const QImage&)));
171 Q_ASSERT(check);
172 check = connect(this, SIGNAL(sigUpdateRect(const QImage&)),
173 pView, SLOT(slotUpdateRect(const QImage&)));
174 Q_ASSERT(check);
175 check = connect(this, SIGNAL(sigUpdateCursor(const QCursor&)),
176 pView, SLOT(slotUpdateCursor(const QCursor&)));
177 Q_ASSERT(check);
178 check = connect(this, SIGNAL(sigUpdateCursorPosition(const QPoint&)),
179 pView, SLOT(slotUpdateCursorPosition(const QPoint&)));
180 Q_ASSERT(check);
181 check = connect(this, SIGNAL(sigUpdateLedState(unsigned int)),
182 pView, SLOT(slotUpdateLedState(unsigned int)));
183 Q_ASSERT(check);
184#if HAVE_QT6_RECORD
185 check = connect(this, SIGNAL(sigRecordVideo(bool)),
186 pView, SLOT(slotRecordVideo(bool)));
187 Q_ASSERT(check);
188 check = connect(pView, SIGNAL(sigRecordVideo(QImage)),
189 this, SLOT(slotRecordVideo(QImage)),
190 Qt::DirectConnection);
191 Q_ASSERT(check);
192#endif
193 if(bDirectConnection)
194 {
195 /* \~chinese 因为连接可能是在另一个线程中的非Qt事件处理,
196 * 它可能会阻塞线程,那会导致键盘、鼠标事件延迟,
197 * 所以这里用 Qt::DirectConnection
198 * \~english Because the connection may be a non-Qt event processing in another thread,
199 * it may block the thread, which will cause the keyboard and mouse events to be delayed.
200 * So here use Qt::DirectConnection
201 */
202 check = connect(pView, SIGNAL(sigMousePressEvent(QMouseEvent*, QPoint)),
203 this, SLOT(slotMousePressEvent(QMouseEvent*, QPoint)),
204 Qt::DirectConnection);
205 Q_ASSERT(check);
206 check = connect(pView, SIGNAL(sigMouseReleaseEvent(QMouseEvent*, QPoint)),
207 this, SLOT(slotMouseReleaseEvent(QMouseEvent*, QPoint)),
208 Qt::DirectConnection);
209 Q_ASSERT(check);
210 check = connect(pView, SIGNAL(sigMouseMoveEvent(QMouseEvent*, QPoint)),
211 this, SLOT(slotMouseMoveEvent(QMouseEvent*, QPoint)),
212 Qt::DirectConnection);
213 Q_ASSERT(check);
214 check = connect(pView, SIGNAL(sigWheelEvent(QWheelEvent*, QPoint)),
215 this, SLOT(slotWheelEvent(QWheelEvent*, QPoint)),
216 Qt::DirectConnection);
217 Q_ASSERT(check);
218 check = connect(pView, SIGNAL(sigKeyPressEvent(QKeyEvent*)),
219 this, SLOT(slotKeyPressEvent(QKeyEvent*)),
220 Qt::DirectConnection);
221 Q_ASSERT(check);
222 check = connect(pView, SIGNAL(sigKeyReleaseEvent(QKeyEvent*)),
223 this, SLOT(slotKeyReleaseEvent(QKeyEvent*)),
224 Qt::DirectConnection);
225 Q_ASSERT(check);
226 } else {
227 check = connect(pView, SIGNAL(sigMousePressEvent(QMouseEvent*, QPoint)),
228 this, SLOT(slotMousePressEvent(QMouseEvent*, QPoint)));
229 Q_ASSERT(check);
230 check = connect(pView, SIGNAL(sigMouseReleaseEvent(QMouseEvent*, QPoint)),
231 this, SLOT(slotMouseReleaseEvent(QMouseEvent*, QPoint)));
232 Q_ASSERT(check);
233 check = connect(pView, SIGNAL(sigMouseMoveEvent(QMouseEvent*, QPoint)),
234 this, SLOT(slotMouseMoveEvent(QMouseEvent*, QPoint)));
235 Q_ASSERT(check);
236 check = connect(pView, SIGNAL(sigWheelEvent(QWheelEvent*, QPoint)),
237 this, SLOT(slotWheelEvent(QWheelEvent*, QPoint)));
238 Q_ASSERT(check);
239 check = connect(pView, SIGNAL(sigKeyPressEvent(QKeyEvent*)),
240 this, SLOT(slotKeyPressEvent(QKeyEvent*)));
241 Q_ASSERT(check);
242 check = connect(pView, SIGNAL(sigKeyReleaseEvent(QKeyEvent*)),
243 this, SLOT(slotKeyReleaseEvent(QKeyEvent*)));
244 Q_ASSERT(check);
245 }
246
247 return 0;
248}
249
250void CConnectDesktop::slotWheelEvent(QWheelEvent *event, QPoint pos)
251{
252 QWheelEvent* e = new QWheelEvent(
253 pos,
254#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
255 event->globalPosition(),
256#else
257 event->globalPos(),
258#endif
259 event->pixelDelta(), event->angleDelta(), event->buttons(),
260 event->modifiers(), event->phase(), event->inverted(), event->source());
261 QCoreApplication::postEvent(this, e);
262 WakeUp();
263}
264
265void CConnectDesktop::slotMouseMoveEvent(QMouseEvent *event, QPoint pos)
266{
267 QMouseEvent* e = new QMouseEvent(event->type(), pos, event->button(),
268 event->buttons(), event->modifiers());
269 QCoreApplication::postEvent(this, e);
270 WakeUp();
271}
272
273void CConnectDesktop::slotMousePressEvent(QMouseEvent *event, QPoint pos)
274{
275 QMouseEvent* e = new QMouseEvent(event->type(), pos, event->button(),
276 event->buttons(), event->modifiers());
277 QCoreApplication::postEvent(this, e);
278 WakeUp();
279}
280
281void CConnectDesktop::slotMouseReleaseEvent(QMouseEvent *event, QPoint pos)
282{
283 QMouseEvent* e = new QMouseEvent(event->type(), pos, event->button(),
284 event->buttons(), event->modifiers());
285 QCoreApplication::postEvent(this, e);
286 WakeUp();
287}
288
289void CConnectDesktop::slotKeyPressEvent(QKeyEvent *event)
290{
291 QKeyEvent* e = new QKeyEvent(event->type(), event->key(),
292 event->modifiers(), event->text());
293 QCoreApplication::postEvent(this, e);
294 WakeUp();
295}
296
297void CConnectDesktop::slotKeyReleaseEvent(QKeyEvent *event)
298{
299 QKeyEvent* e = new QKeyEvent(event->type(), event->key(),
300 event->modifiers(), event->text());
301 QCoreApplication::postEvent(this, e);
302 WakeUp();
303}
304
305void CConnectDesktop::mouseMoveEvent(QMouseEvent *event)
306{
307 qDebug(logMouse) << "Need to implement CConnectDesktop::mouseMoveEvent";
308}
309
310void CConnectDesktop::mousePressEvent(QMouseEvent *event)
311{
312 qDebug(logMouse) << "Need to implement CConnectDesktop::mousePressEvent";
313}
314
315void CConnectDesktop::mouseReleaseEvent(QMouseEvent *event)
316{
317 qDebug(logMouse) << "Need to implement CConnectDesktop::mouseReleaseEvent";
318}
319
320void CConnectDesktop::wheelEvent(QWheelEvent *event)
321{
322 qDebug(logMouse) << "Need to implement CConnectDesktop::wheelEvent";
323}
324
325void CConnectDesktop::keyPressEvent(QKeyEvent *event)
326{
327 qDebug(logMouse) << "Need to implement CConnectDesktop::keyPressEvent";
328}
329
330void CConnectDesktop::keyReleaseEvent(QKeyEvent *event)
331{
332 qDebug(logMouse) << "Need to implement CConnectDesktop::keyReleaseEvent";
333}
334
336{
337 return 0;
338}
339
340bool CConnectDesktop::event(QEvent *event)
341{
342 //qDebug(log) << "CConnectDesktop::event" << event;
343 switch (event->type()) {
344 case QEvent::MouseButtonPress:
345 case QEvent::MouseButtonDblClick:
346 mousePressEvent((QMouseEvent*)event);
347 break;
348 case QEvent::MouseButtonRelease:
349 mouseReleaseEvent((QMouseEvent*)event);
350 break;
351 case QEvent::MouseMove:
352 mouseMoveEvent((QMouseEvent*)event);
353 break;
354 case QEvent::Wheel:
355 wheelEvent((QWheelEvent*)event);
356 break;
357 case QEvent::KeyPress:
358 keyPressEvent((QKeyEvent*)event);
359 break;
360 case QEvent::KeyRelease:
361 keyReleaseEvent((QKeyEvent*)event);
362 break;
363#if HAVE_QT6_RECORD
364 case TypeRecordVideo:
365 RecordVideo((QRecordVideoEvent*)event);
366 break;
367#endif
368 default:
369 return QObject::event(event);
370 }
371
372 event->accept();
373 return true;
374}
375
376#if HAVE_QT6_RECORD
377void CConnectDesktop::slotRecord(bool bRecord)
378{
379 qDebug(log) << Q_FUNC_INFO << bRecord;
380 if(bRecord) {
381 if(QMediaRecorder::RecordingState == m_Recorder.recorderState())
382 return;
383 (*m_pParameterRecord) >> m_Recorder;
384 m_CaptureSession.setVideoFrameInput(&m_VideoFrameInput);
385 m_CaptureSession.setRecorder(&m_Recorder);
386 m_Recorder.record();
387 } else {
388 m_Recorder.stop();
389 m_CaptureSession.setVideoFrameInput(nullptr);
390 m_CaptureSession.setAudioBufferInput(nullptr);
391 m_CaptureSession.setRecorder(nullptr);
392 }
393 emit sigRecordVideo(bRecord);
394}
395
396void CConnectDesktop::slotRecordPause(bool bPause)
397{
398 qDebug(log) << Q_FUNC_INFO << bPause;
399 if(bPause) {
400 if(m_Recorder.recorderState() == QMediaRecorder::RecordingState)
401 m_Recorder.pause();
402 } else {
403 if(m_Recorder.recorderState() == QMediaRecorder::PausedState)
404 m_Recorder.record();
405 }
406}
407
408void CConnectDesktop::slotRecordVideo(const QImage &img)
409{
411 if(!e) return;
412 QCoreApplication::postEvent(this, e);
413 WakeUp();
414}
415
416void CConnectDesktop::RecordVideo(QRecordVideoEvent *e)
417{
418 qDebug(log) << "Update image";
419 if(!e) return;
420 if(QMediaRecorder::RecordingState != m_Recorder.recorderState()) {
421 qCritical(log) << "Recorder is inavailable";
422 return;
423 }
424 QVideoFrame frame(e->GetImage());
425 bool bRet = m_VideoFrameInput.sendVideoFrame(frame);
426 if(!bRet) {
427 //TODO: 放入未成功发送队列,
428 // 当 QVideoFrameInput::readyToSendVideoFrame() 时,再发送
429 qDebug(log) << "m_VideoFrameInput.sendVideoFrame fail";
430 }
431}
432
434{
435 slotRecord(false);
436 return CConnect::Disconnect();
437}
438#endif
CConnectDesktop(CConnecter *pConnecter, bool bDirectConnection=true)
void sigUpdateRect(const QRect &r, const QImage &image)
通知视图,图像更新
virtual int WakeUp()
唤醒连接线程(后台线程)
virtual void slotClipBoardChanged()=0
当剪切板发生改变时调用
连接接口。它由协议插件实现。 它默认启动一个定时器来开启一个非 Qt 事件循环(就是普通的循环处理)。 详见: Connect()、 slotTimeOut()、 OnProcess() 。 当然,它仍...
Definition Connect.h:45
void sigError(const int nError, const QString &szError=QString())
当有错误产生时触发
void sigConnected()
当插件连接成功后触发。仅由插件触发
virtual int Disconnect()
断开
Definition Connect.cpp:89
virtual CParameterBase * GetParameter()
Get parameter
默认启动一个后台线程。实现一个后台线程处理一个连接。 可与插件接口从 CPluginClient 派生的插件一起使用,用于连接是阻塞模型的。
连接者应用接口。
Definition Connecter.h:62
virtual QWidget * GetViewer()=0
得到显示视图
The scroll form class
Definition FrmScroll.h:17
用于显示从 CConnectDesktop 输出的图像,和向 CConnectDesktop 发送键盘、鼠标事件。
Definition FrmViewer.h:49