Rabbit Remote Control 0.0.36
Loading...
Searching...
No Matches
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 }
26 {
27 qDebug(log) << Q_FUNC_INFO;
28 }
29
30 QImage GetImage()
31 {
32 return m_Image;
33 }
34private:
35 QImage m_Image;
36};
37
38int g_QtKeyboardModifiers = qRegisterMetaType<Qt::KeyboardModifiers>("KeyboardModifiers");
39int g_QtMouseButtons = qRegisterMetaType<Qt::MouseButtons>("MouseButtons");
40int g_QtMouseButton = qRegisterMetaType<Qt::MouseButton>("MouseButton");
41int g_QMessageBox_Icon = qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
42
43CConnectDesktop::CConnectDesktop(CConnecter *pConnecter, bool bDirectConnection)
44 : CConnect(pConnecter)
45#if HAVE_QT6_RECORD
46 , m_pParameterRecord(nullptr)
47 , m_VideoFrameInput(this)
48 , m_AudioBufferInput(this)
49 , m_AudioBufferOutput(this)
50#endif
51{
52 if(pConnecter) {
53 CFrmScroll* pScroll = qobject_cast<CFrmScroll*>(pConnecter->GetViewer());
54 if(pScroll) {
55 CFrmViewer* pView = pScroll->GetViewer();
56 if(pView)
57 SetViewer(pView, pConnecter, bDirectConnection);
58 else {
59 QString szErr = pConnecter->metaObject()->className();
60 szErr += "::GetViewer() is not CFrmViewer";
61 qWarning(log) << szErr.toStdString().c_str();
62 }
63 } else {
64 QString szErr = pConnecter->metaObject()->className();
65 szErr += "::GetViewer() is not CFrmScroll";
66 qWarning(log) << szErr.toStdString().c_str();
67 }
68 SetConnecter(pConnecter);
69 }
70
71#if HAVE_QT6_RECORD
72 bool check = connect(
73 &m_Recorder, &QMediaRecorder::errorOccurred,
74 this, [&](QMediaRecorder::Error error, const QString &errorString) {
75 qDebug(log) << "Recorder error occurred:" << error << errorString;
76 slotRecord(false);
77 emit sigError(error, errorString);
78 });
79 Q_ASSERT(check);
80 check = connect(
81 &m_Recorder, &QMediaRecorder::recorderStateChanged,
82 this, [&](QMediaRecorder::RecorderState state){
83 qDebug(log) << "Recorder state changed:" << state;
84 if(QMediaRecorder::StoppedState == state)
85 {
86 slotRecord(false);
87 if(m_pParameterRecord) {
88 qDebug(log) << "End action:"
89 << m_pParameterRecord->GetEndAction()
90 << m_Recorder.actualLocation();
91 switch(m_pParameterRecord->GetEndAction())
92 {
93 case CParameterRecord::ENDACTION::OpenFile:
94 QDesktopServices::openUrl(m_Recorder.actualLocation());
95 break;
96 case CParameterRecord::ENDACTION::OpenFolder: {
97 QFileInfo fi(m_Recorder.actualLocation().toLocalFile());
98 QDesktopServices::openUrl(
99 QUrl::fromLocalFile(fi.absolutePath()));
100 break;
101 }
102 default:
103 break;
104 }
105 }
106 }
107 });
108 Q_ASSERT(check);
109 check = connect(&m_Recorder, &QMediaRecorder::actualLocationChanged,
110 this, [&](const QUrl &location){
111 qInfo(log) << "Recorder actual location changed:" << location;
112 });
113 Q_ASSERT(check);
114#endif
115}
116
117CConnectDesktop::~CConnectDesktop()
118{
119 qDebug(log) << "CConnectDesktop::~CConnectDesktop()";
120}
121
122int CConnectDesktop::SetConnecter(CConnecter* pConnecter)
123{
124 qDebug(log) << "CConnectDesktop::SetConnecter" << pConnecter;
125 Q_ASSERT(pConnecter);
126 if(!pConnecter) return -1;
127
128 bool check = false;
129 check = connect(this, SIGNAL(sigServerName(const QString&)),
130 pConnecter, SLOT(slotSetServerName(const QString&)));
131 Q_ASSERT(check);
132 check = connect(pConnecter, SIGNAL(sigClipBoardChanged()),
133 this, SLOT(slotClipBoardChanged()));
134 Q_ASSERT(check);
135 check = connect(this, SIGNAL(sigSetClipboard(QMimeData*)),
136 pConnecter, SLOT(slotSetClipboard(QMimeData*)));
137 Q_ASSERT(check);
138#if HAVE_QT6_RECORD
139 CConnecterThread* p = qobject_cast<CConnecterThread*>(pConnecter);
140 if(p) {
141 m_pParameterRecord = &p->GetParameter()->m_Record;
142 check = connect(p, SIGNAL(sigRecord(bool)),
143 this, SLOT(slotRecord(bool)));
144 Q_ASSERT(check);
145
146 check = connect(p, SIGNAL(sigRecordPause(bool)),
147 this, SLOT(slotRecordPause(bool)));
148 Q_ASSERT(check);
149 check = connect(
150 &m_Recorder,
151 SIGNAL(recorderStateChanged(QMediaRecorder::RecorderState)),
152 p, SLOT(slotRecorderStateChanged(QMediaRecorder::RecorderState)));
153 Q_ASSERT(check);
154 }
155#endif
156 return 0;
157}
158
159int CConnectDesktop::SetViewer(CFrmViewer *pView,
160 CConnecter* pConnecter, bool bDirectConnection)
161{
162 Q_ASSERT(pView);
163 if(!pView) return -1;
164
165 bool check = false;
166 check = connect(this, SIGNAL(sigConnected()), pView, SLOT(slotConnected()));
167 Q_ASSERT(check);
168 check = connect(this, SIGNAL(sigSetDesktopSize(int, int)),
169 pView, SLOT(slotSetDesktopSize(int, int)));
170 Q_ASSERT(check);
171 check = connect(this, SIGNAL(sigServerName(const QString&)),
172 pView, SLOT(slotSetName(const QString&)));
173 Q_ASSERT(check);
174
175 check = connect(this, SIGNAL(sigUpdateRect(const QRect&, const QImage&)),
176 pView, SLOT(slotUpdateRect(const QRect&, const QImage&)));
177 Q_ASSERT(check);
178 check = connect(this, SIGNAL(sigUpdateRect(const QImage&)),
179 pView, SLOT(slotUpdateRect(const QImage&)));
180 Q_ASSERT(check);
181 check = connect(this, SIGNAL(sigUpdateCursor(const QCursor&)),
182 pView, SLOT(slotUpdateCursor(const QCursor&)));
183 Q_ASSERT(check);
184 check = connect(this, SIGNAL(sigUpdateCursorPosition(const QPoint&)),
185 pView, SLOT(slotUpdateCursorPosition(const QPoint&)));
186 Q_ASSERT(check);
187 check = connect(this, SIGNAL(sigUpdateLedState(unsigned int)),
188 pView, SLOT(slotUpdateLedState(unsigned int)));
189 Q_ASSERT(check);
190
191#if HAVE_QT6_RECORD
192 check = connect(this, SIGNAL(sigRecordVideo(bool, qreal)),
193 pView, SLOT(slotRecordVideo(bool, qreal)));
194 Q_ASSERT(check);
195 check = connect(pView, SIGNAL(sigRecordVideo(QImage)),
196 this, SLOT(slotRecordVideo(QImage)),
197 Qt::DirectConnection);
198 Q_ASSERT(check);
199#endif
200 if(bDirectConnection)
201 {
202 /* \~chinese 因为连接可能是在另一个线程中的非Qt事件处理,
203 * 它可能会阻塞线程,那会导致键盘、鼠标事件延迟,
204 * 所以这里用 Qt::DirectConnection
205 * \~english Because the connection may be a non-Qt event processing in another thread,
206 * it may block the thread, which will cause the keyboard and mouse events to be delayed.
207 * So here use Qt::DirectConnection
208 */
209 check = connect(pView, SIGNAL(sigMousePressEvent(QMouseEvent*, QPoint)),
210 this, SLOT(slotMousePressEvent(QMouseEvent*, QPoint)),
211 Qt::DirectConnection);
212 Q_ASSERT(check);
213 check = connect(pView, SIGNAL(sigMouseReleaseEvent(QMouseEvent*, QPoint)),
214 this, SLOT(slotMouseReleaseEvent(QMouseEvent*, QPoint)),
215 Qt::DirectConnection);
216 Q_ASSERT(check);
217 check = connect(pView, SIGNAL(sigMouseMoveEvent(QMouseEvent*, QPoint)),
218 this, SLOT(slotMouseMoveEvent(QMouseEvent*, QPoint)),
219 Qt::DirectConnection);
220 Q_ASSERT(check);
221 check = connect(pView, SIGNAL(sigWheelEvent(QWheelEvent*, QPoint)),
222 this, SLOT(slotWheelEvent(QWheelEvent*, QPoint)),
223 Qt::DirectConnection);
224 Q_ASSERT(check);
225 check = connect(pView, SIGNAL(sigKeyPressEvent(QKeyEvent*)),
226 this, SLOT(slotKeyPressEvent(QKeyEvent*)),
227 Qt::DirectConnection);
228 Q_ASSERT(check);
229 check = connect(pView, SIGNAL(sigKeyReleaseEvent(QKeyEvent*)),
230 this, SLOT(slotKeyReleaseEvent(QKeyEvent*)),
231 Qt::DirectConnection);
232 Q_ASSERT(check);
233 } else {
234 check = connect(pView, SIGNAL(sigMousePressEvent(QMouseEvent*, QPoint)),
235 this, SLOT(slotMousePressEvent(QMouseEvent*, QPoint)));
236 Q_ASSERT(check);
237 check = connect(pView, SIGNAL(sigMouseReleaseEvent(QMouseEvent*, QPoint)),
238 this, SLOT(slotMouseReleaseEvent(QMouseEvent*, QPoint)));
239 Q_ASSERT(check);
240 check = connect(pView, SIGNAL(sigMouseMoveEvent(QMouseEvent*, QPoint)),
241 this, SLOT(slotMouseMoveEvent(QMouseEvent*, QPoint)));
242 Q_ASSERT(check);
243 check = connect(pView, SIGNAL(sigWheelEvent(QWheelEvent*, QPoint)),
244 this, SLOT(slotWheelEvent(QWheelEvent*, QPoint)));
245 Q_ASSERT(check);
246 check = connect(pView, SIGNAL(sigKeyPressEvent(QKeyEvent*)),
247 this, SLOT(slotKeyPressEvent(QKeyEvent*)));
248 Q_ASSERT(check);
249 check = connect(pView, SIGNAL(sigKeyReleaseEvent(QKeyEvent*)),
250 this, SLOT(slotKeyReleaseEvent(QKeyEvent*)));
251 Q_ASSERT(check);
252 }
253
254 return 0;
255}
256
257void CConnectDesktop::slotWheelEvent(QWheelEvent *event, QPoint pos)
258{
259 QWheelEvent* e = new QWheelEvent(
260 pos,
261#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
262 event->globalPosition(),
263#else
264 event->globalPos(),
265#endif
266 event->pixelDelta(), event->angleDelta(), event->buttons(),
267 event->modifiers(), event->phase(), event->inverted(), event->source());
268 QCoreApplication::postEvent(this, e);
269 WakeUp();
270}
271
272void CConnectDesktop::slotMouseMoveEvent(QMouseEvent *event, QPoint pos)
273{
274#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
275 QMouseEvent* e = new QMouseEvent(event->type(), pos, event->button(),
276 event->buttons(), event->modifiers());
277#else
278 QMouseEvent* e = new QMouseEvent(event->type(), pos, pos, event->button(),
279 event->buttons(), event->modifiers());
280#endif
281 QCoreApplication::postEvent(this, e);
282 WakeUp();
283}
284
285void CConnectDesktop::slotMousePressEvent(QMouseEvent *event, QPoint pos)
286{
287#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
288 QMouseEvent* e = new QMouseEvent(event->type(), pos, event->button(),
289 event->buttons(), event->modifiers());
290#else
291 QMouseEvent* e = new QMouseEvent(event->type(), pos, pos, event->button(),
292 event->buttons(), event->modifiers());
293#endif
294 QCoreApplication::postEvent(this, e);
295 WakeUp();
296}
297
298void CConnectDesktop::slotMouseReleaseEvent(QMouseEvent *event, QPoint pos)
299{
300#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
301 QMouseEvent* e = new QMouseEvent(event->type(), pos, event->button(),
302 event->buttons(), event->modifiers());
303#else
304 QMouseEvent* e = new QMouseEvent(event->type(), pos, pos, event->button(),
305 event->buttons(), event->modifiers());
306#endif
307 QCoreApplication::postEvent(this, e);
308 WakeUp();
309}
310
311void CConnectDesktop::slotKeyPressEvent(QKeyEvent *event)
312{
313 QKeyEvent* e = new QKeyEvent(event->type(), event->key(),
314 event->modifiers(), event->text());
315 QCoreApplication::postEvent(this, e);
316 WakeUp();
317}
318
319void CConnectDesktop::slotKeyReleaseEvent(QKeyEvent *event)
320{
321 QKeyEvent* e = new QKeyEvent(event->type(), event->key(),
322 event->modifiers(), event->text());
323 QCoreApplication::postEvent(this, e);
324 WakeUp();
325}
326
327void CConnectDesktop::mouseMoveEvent(QMouseEvent *event)
328{
329 qDebug(logMouse) << "Need to implement CConnectDesktop::mouseMoveEvent";
330}
331
332void CConnectDesktop::mousePressEvent(QMouseEvent *event)
333{
334 qDebug(logMouse) << "Need to implement CConnectDesktop::mousePressEvent";
335}
336
337void CConnectDesktop::mouseReleaseEvent(QMouseEvent *event)
338{
339 qDebug(logMouse) << "Need to implement CConnectDesktop::mouseReleaseEvent";
340}
341
342void CConnectDesktop::wheelEvent(QWheelEvent *event)
343{
344 qDebug(logMouse) << "Need to implement CConnectDesktop::wheelEvent";
345}
346
347void CConnectDesktop::keyPressEvent(QKeyEvent *event)
348{
349 qDebug(logMouse) << "Need to implement CConnectDesktop::keyPressEvent";
350}
351
352void CConnectDesktop::keyReleaseEvent(QKeyEvent *event)
353{
354 qDebug(logMouse) << "Need to implement CConnectDesktop::keyReleaseEvent";
355}
356
358{
359 return 0;
360}
361
362bool CConnectDesktop::event(QEvent *event)
363{
364 //qDebug(log) << "CConnectDesktop::event" << event;
365 switch (event->type()) {
366 case QEvent::MouseButtonPress:
367 case QEvent::MouseButtonDblClick:
368 mousePressEvent((QMouseEvent*)event);
369 break;
370 case QEvent::MouseButtonRelease:
371 mouseReleaseEvent((QMouseEvent*)event);
372 break;
373 case QEvent::MouseMove:
374 mouseMoveEvent((QMouseEvent*)event);
375 break;
376 case QEvent::Wheel:
377 wheelEvent((QWheelEvent*)event);
378 break;
379 case QEvent::KeyPress:
380 keyPressEvent((QKeyEvent*)event);
381 break;
382 case QEvent::KeyRelease:
383 keyReleaseEvent((QKeyEvent*)event);
384 break;
385#if HAVE_QT6_RECORD
386 case TypeRecordVideo:
387 RecordVideo((QRecordVideoEvent*)event);
388 break;
389#endif
390 default:
391 return QObject::event(event);
392 }
393
394 event->accept();
395 return true;
396}
397
398#if HAVE_QT6_RECORD
399void CConnectDesktop::slotRecord(bool bRecord)
400{
401 qDebug(log) << Q_FUNC_INFO << bRecord;
402 if(bRecord) {
403 if(QMediaRecorder::RecordingState == m_Recorder.recorderState())
404 return;
405 (*m_pParameterRecord) >> m_Recorder;
406 m_CaptureSession.setVideoFrameInput(&m_VideoFrameInput);
407 m_CaptureSession.setRecorder(&m_Recorder);
408 m_Recorder.record();
409 } else {
410 m_Recorder.stop();
411 m_CaptureSession.setVideoFrameInput(nullptr);
412 m_CaptureSession.setAudioBufferInput(nullptr);
413 m_CaptureSession.setRecorder(nullptr);
414 }
415 emit sigRecordVideo(bRecord, m_pParameterRecord->GetVideoFrameRate());
416}
417
418void CConnectDesktop::slotRecordPause(bool bPause)
419{
420 qDebug(log) << Q_FUNC_INFO << bPause;
421 if(bPause) {
422 if(m_Recorder.recorderState() == QMediaRecorder::RecordingState)
423 m_Recorder.pause();
424 } else {
425 if(m_Recorder.recorderState() == QMediaRecorder::PausedState)
426 m_Recorder.record();
427 }
428}
429
430void CConnectDesktop::slotRecordVideo(const QImage &img)
431{
433 if(!e) return;
434 QCoreApplication::postEvent(this, e);
435 WakeUp();
436}
437
438void CConnectDesktop::RecordVideo(QRecordVideoEvent *e)
439{
440 qDebug(log) << "Update image";
441 if(!e) return;
442 if(QMediaRecorder::RecordingState != m_Recorder.recorderState()) {
443 qCritical(log) << "Recorder is inavailable";
444 return;
445 }
446 QVideoFrame frame(e->GetImage());
447 bool bRet = m_VideoFrameInput.sendVideoFrame(frame);
448 if(!bRet) {
449 //TODO: 放入未成功发送队列,
450 // 当 QVideoFrameInput::readyToSendVideoFrame() 时,再发送
451 qDebug(log) << "m_VideoFrameInput.sendVideoFrame fail";
452 }
453}
454
456{
457 slotRecord(false);
458 return CConnect::Disconnect();
459}
460#endif
CConnectDesktop(CConnecter *pConnecter, bool bDirectConnection=true)
void sigUpdateRect(const QRect &r, const QImage &image)
Notify the CFrmView update image.
virtual int WakeUp()
Wake up Connect thread(background thread)
virtual void slotClipBoardChanged()=0
Be called when the clip board change.
Connect interface.
Definition Connect.h:45
void sigError(const int nError, const QString &szError=QString())
Triggered when an error is generated.
void sigConnected()
Emitted when the plugin is successfully connected.
virtual int Disconnect()
Disconnect.
Definition Connect.cpp:89
virtual CParameterBase * GetParameter()
Get parameter.
It starts a background thread by default.
Connecter interface.
Definition Connecter.h:62
virtual QWidget * GetViewer()=0
Get Viewer.
The scroll form class.
Definition FrmScroll.h:17
A widget which displays output image from a CConnectDesktop and sends input keypresses and mouse acti...
Definition FrmViewer.h:48