Rabbit Remote Control 0.0.37
Loading...
Searching...
No Matches
BackendDesktop.cpp
1// Author: Kang Lin <kl222@126.com>
2
3#include <QApplication>
4#include <QClipboard>
5#include <QWheelEvent>
6#include <QVideoFrame>
7#include <QDesktopServices>
8#include <QLoggingCategory>
9#include <QFileInfo>
10
11#include "BackendDesktop.h"
12#include "FrmScroll.h"
13
14static Q_LOGGING_CATEGORY(log, "Backend.Desktop")
15static Q_LOGGING_CATEGORY(logMouse, "Backend.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
43CBackendDesktop::CBackendDesktop(COperateDesktop *pOperate, bool bDirectConnection)
44 : CBackend(pOperate)
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 qDebug(log) << Q_FUNC_INFO;
53 if(pOperate) {
54 CFrmScroll* pScroll = qobject_cast<CFrmScroll*>(pOperate->GetViewer());
55 if(pScroll) {
56 CFrmViewer* pView = pScroll->GetViewer();
57 if(pView)
58 SetViewer(pView, pOperate, bDirectConnection);
59 else {
60 QString szErr = pOperate->metaObject()->className();
61 szErr += "::GetViewer() is not CFrmViewer";
62 qWarning(log) << szErr.toStdString().c_str();
63 }
64 } else {
65 QString szErr = pOperate->metaObject()->className();
66 szErr += "::GetViewer() is not CFrmScroll";
67 qWarning(log) << szErr.toStdString().c_str();
68 }
69 SetConnect(pOperate);
70 }
71
72#if HAVE_QT6_RECORD
73 bool check = connect(
74 &m_Recorder, &QMediaRecorder::errorOccurred,
75 this, [&](QMediaRecorder::Error error, const QString &errorString) {
76 qDebug(log) << "Recorder error occurred:" << error << errorString;
77 slotRecord(false);
78 emit sigError(error, errorString);
79 });
80 Q_ASSERT(check);
81 check = connect(
82 &m_Recorder, &QMediaRecorder::recorderStateChanged,
83 this, [&](QMediaRecorder::RecorderState state){
84 qDebug(log) << "Recorder state changed:" << state;
85 if(QMediaRecorder::StoppedState == state)
86 {
87 slotRecord(false);
88 if(m_pParameterRecord) {
89 qDebug(log) << "End action:"
90 << m_pParameterRecord->GetEndAction()
91 << m_Recorder.actualLocation();
92 switch(m_pParameterRecord->GetEndAction())
93 {
94 case CParameterRecord::ENDACTION::OpenFile:
95 QDesktopServices::openUrl(m_Recorder.actualLocation());
96 break;
97 case CParameterRecord::ENDACTION::OpenFolder: {
98 QFileInfo fi(m_Recorder.actualLocation().toLocalFile());
99 QDesktopServices::openUrl(
100 QUrl::fromLocalFile(fi.absolutePath()));
101 break;
102 }
103 default:
104 break;
105 }
106 }
107 }
108 });
109 Q_ASSERT(check);
110 check = connect(&m_Recorder, &QMediaRecorder::actualLocationChanged,
111 this, [&](const QUrl &location){
112 qInfo(log) << "Recorder actual location changed:" << location;
113 });
114 Q_ASSERT(check);
115#endif
116}
117
118CBackendDesktop::~CBackendDesktop()
119{
120 qDebug(log) << Q_FUNC_INFO;
121}
122
123int CBackendDesktop::SetConnect(COperateDesktop *pOperate)
124{
125 //qDebug(log) << "CBackendDesktop::SetConnect:" << pOperate;
126 Q_ASSERT(pOperate);
127 if(!pOperate) return -1;
128
129 bool check = false;
130 check = connect(this, SIGNAL(sigServerName(const QString&)),
131 pOperate, SLOT(slotSetServerName(const QString&)));
132 Q_ASSERT(check);
133 check = connect(pOperate, SIGNAL(sigClipBoardChanged()),
134 this, SLOT(slotClipBoardChanged()));
135 Q_ASSERT(check);
136 check = connect(this, SIGNAL(sigSetClipboard(QMimeData*)),
137 pOperate, SLOT(slotSetClipboard(QMimeData*)));
138 Q_ASSERT(check);
139#if HAVE_QT6_RECORD
140 if(pOperate) {
141 m_pParameterRecord = &pOperate->GetParameter()->m_Record;
142 check = connect(pOperate, SIGNAL(sigRecord(bool)),
143 this, SLOT(slotRecord(bool)));
144 Q_ASSERT(check);
145
146 check = connect(pOperate, SIGNAL(sigRecordPause(bool)),
147 this, SLOT(slotRecordPause(bool)));
148 Q_ASSERT(check);
149 check = connect(
150 &m_Recorder,
151 SIGNAL(recorderStateChanged(QMediaRecorder::RecorderState)),
152 pOperate, SLOT(slotRecorderStateChanged(QMediaRecorder::RecorderState)));
153 Q_ASSERT(check);
154 }
155#endif
156 return 0;
157}
158
159int CBackendDesktop::SetViewer(CFrmViewer *pView,
160 COperateDesktop* pOperate, bool bDirectConnection)
161{
162 Q_ASSERT(pView);
163 if(!pView) return -1;
164
165 bool check = false;
166 check = connect(this, SIGNAL(sigRunning()), pView, SLOT(slotRunning()));
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 check = connect(pView, SIGNAL(sigInputMethodEvent(QInputMethodEvent*)),
234 this, SLOT(slotInputMethodEvent(QInputMethodEvent*)),
235 Qt::DirectConnection);
236 Q_ASSERT(check);
237 } else {
238 check = connect(pView, SIGNAL(sigMousePressEvent(QMouseEvent*, QPoint)),
239 this, SLOT(slotMousePressEvent(QMouseEvent*, QPoint)));
240 Q_ASSERT(check);
241 check = connect(pView, SIGNAL(sigMouseReleaseEvent(QMouseEvent*, QPoint)),
242 this, SLOT(slotMouseReleaseEvent(QMouseEvent*, QPoint)));
243 Q_ASSERT(check);
244 check = connect(pView, SIGNAL(sigMouseMoveEvent(QMouseEvent*, QPoint)),
245 this, SLOT(slotMouseMoveEvent(QMouseEvent*, QPoint)));
246 Q_ASSERT(check);
247 check = connect(pView, SIGNAL(sigWheelEvent(QWheelEvent*, QPoint)),
248 this, SLOT(slotWheelEvent(QWheelEvent*, QPoint)));
249 Q_ASSERT(check);
250 check = connect(pView, SIGNAL(sigKeyPressEvent(QKeyEvent*)),
251 this, SLOT(slotKeyPressEvent(QKeyEvent*)));
252 Q_ASSERT(check);
253 check = connect(pView, SIGNAL(sigKeyReleaseEvent(QKeyEvent*)),
254 this, SLOT(slotKeyReleaseEvent(QKeyEvent*)));
255 Q_ASSERT(check);
256 check = connect(pView, SIGNAL(sigInputMethodEvent(QInputMethodEvent*)),
257 this, SLOT(slotInputMethodEvent(QInputMethodEvent*)));
258 Q_ASSERT(check);
259 }
260
261 return 0;
262}
263
264void CBackendDesktop::slotWheelEvent(QWheelEvent *event, QPoint pos)
265{
266 QWheelEvent* e = new QWheelEvent(
267 pos,
268#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
269 event->globalPosition(),
270#else
271 event->globalPos(),
272#endif
273 event->pixelDelta(), event->angleDelta(), event->buttons(),
274 event->modifiers(), event->phase(), event->inverted(), event->source());
275 QCoreApplication::postEvent(this, e);
276 WakeUp();
277}
278
279void CBackendDesktop::slotMouseMoveEvent(QMouseEvent *event, QPoint pos)
280{
281#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
282 QMouseEvent* e = new QMouseEvent(event->type(), pos, event->button(),
283 event->buttons(), event->modifiers());
284#else
285 QMouseEvent* e = new QMouseEvent(event->type(), pos, pos, event->button(),
286 event->buttons(), event->modifiers());
287#endif
288 QCoreApplication::postEvent(this, e);
289 WakeUp();
290}
291
292void CBackendDesktop::slotMousePressEvent(QMouseEvent *event, QPoint pos)
293{
294#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
295 QMouseEvent* e = new QMouseEvent(event->type(), pos, event->button(),
296 event->buttons(), event->modifiers());
297#else
298 QMouseEvent* e = new QMouseEvent(event->type(), pos, pos, event->button(),
299 event->buttons(), event->modifiers());
300#endif
301 QCoreApplication::postEvent(this, e);
302 WakeUp();
303}
304
305void CBackendDesktop::slotMouseReleaseEvent(QMouseEvent *event, QPoint pos)
306{
307#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
308 QMouseEvent* e = new QMouseEvent(event->type(), pos, event->button(),
309 event->buttons(), event->modifiers());
310#else
311 QMouseEvent* e = new QMouseEvent(event->type(), pos, pos, event->button(),
312 event->buttons(), event->modifiers());
313#endif
314 QCoreApplication::postEvent(this, e);
315 WakeUp();
316}
317
318void CBackendDesktop::slotKeyPressEvent(QKeyEvent *event)
319{
320 QKeyEvent* e = new QKeyEvent(event->type(), event->key(),
321 event->modifiers(), event->text());
322 QCoreApplication::postEvent(this, e);
323 WakeUp();
324}
325
326void CBackendDesktop::slotKeyReleaseEvent(QKeyEvent *event)
327{
328 QKeyEvent* e = new QKeyEvent(event->type(), event->key(),
329 event->modifiers(), event->text());
330 QCoreApplication::postEvent(this, e);
331 WakeUp();
332}
333
334void CBackendDesktop::slotInputMethodEvent(QInputMethodEvent *event)
335{
336 if(event->commitString().isEmpty()) return;
337 QInputMethodEvent* e = new QInputMethodEvent(event->preeditString(), event->attributes());
338 e->setCommitString(event->commitString(), event->replacementStart(), event->replacementLength());
339 QCoreApplication::postEvent(this, e);
340 WakeUp();
341}
342
343void CBackendDesktop::mouseMoveEvent(QMouseEvent *event)
344{
345 qDebug(logMouse) << "Need to implement CBackendDesktop::mouseMoveEvent";
346}
347
348void CBackendDesktop::mousePressEvent(QMouseEvent *event)
349{
350 qDebug(logMouse) << "Need to implement CBackendDesktop::mousePressEvent";
351}
352
353void CBackendDesktop::mouseReleaseEvent(QMouseEvent *event)
354{
355 qDebug(logMouse) << "Need to implement CBackendDesktop::mouseReleaseEvent";
356}
357
358void CBackendDesktop::wheelEvent(QWheelEvent *event)
359{
360 qDebug(logMouse) << "Need to implement CBackendDesktop::wheelEvent";
361}
362
363void CBackendDesktop::keyPressEvent(QKeyEvent *event)
364{
365 qDebug(logMouse) << "Need to implement CBackendDesktop::keyPressEvent";
366}
367
368void CBackendDesktop::keyReleaseEvent(QKeyEvent *event)
369{
370 qDebug(logMouse) << "Need to implement CBackendDesktop::keyReleaseEvent";
371}
372
373void CBackendDesktop::InputMethodEvent(QInputMethodEvent *event)
374{
375 qDebug(logMouse) << "Need to implement CBackendDesktop::InputMethodEvent";
376}
377
379{
380 return 0;
381}
382
383bool CBackendDesktop::event(QEvent *event)
384{
385 //qDebug(log) << "CBackendDesktop::event" << event;
386 switch (event->type()) {
387 case QEvent::MouseButtonPress:
388 case QEvent::MouseButtonDblClick:
389 mousePressEvent((QMouseEvent*)event);
390 break;
391 case QEvent::MouseButtonRelease:
392 mouseReleaseEvent((QMouseEvent*)event);
393 break;
394 case QEvent::MouseMove:
395 mouseMoveEvent((QMouseEvent*)event);
396 break;
397 case QEvent::Wheel:
398 wheelEvent((QWheelEvent*)event);
399 break;
400 case QEvent::KeyPress:
401 keyPressEvent((QKeyEvent*)event);
402 break;
403 case QEvent::KeyRelease:
404 keyReleaseEvent((QKeyEvent*)event);
405 break;
406 case QEvent::InputMethod:
407 InputMethodEvent((QInputMethodEvent*) event);
408 break;
409#if HAVE_QT6_RECORD
410 case TypeRecordVideo:
411 RecordVideo((QRecordVideoEvent*)event);
412 break;
413#endif
414 default:
415 return QObject::event(event);
416 }
417
418 event->accept();
419 return true;
420}
421
422#if HAVE_QT6_RECORD
423void CBackendDesktop::slotRecord(bool bRecord)
424{
425 qDebug(log) << Q_FUNC_INFO << bRecord;
426 if(bRecord) {
427 if(QMediaRecorder::RecordingState == m_Recorder.recorderState())
428 return;
429 (*m_pParameterRecord) >> m_Recorder;
430 m_CaptureSession.setVideoFrameInput(&m_VideoFrameInput);
431 m_CaptureSession.setRecorder(&m_Recorder);
432 m_Recorder.record();
433 } else {
434 m_Recorder.stop();
435 m_CaptureSession.setVideoFrameInput(nullptr);
436 m_CaptureSession.setAudioBufferInput(nullptr);
437 m_CaptureSession.setRecorder(nullptr);
438 }
439 emit sigRecordVideo(bRecord, m_pParameterRecord->GetVideoFrameRate());
440}
441
442void CBackendDesktop::slotRecordPause(bool bPause)
443{
444 qDebug(log) << Q_FUNC_INFO << bPause;
445 if(bPause) {
446 if(m_Recorder.recorderState() == QMediaRecorder::RecordingState)
447 m_Recorder.pause();
448 } else {
449 if(m_Recorder.recorderState() == QMediaRecorder::PausedState)
450 m_Recorder.record();
451 }
452}
453
454void CBackendDesktop::slotRecordVideo(const QImage &img)
455{
457 if(!e) return;
458 QCoreApplication::postEvent(this, e);
459 WakeUp();
460}
461
462void CBackendDesktop::RecordVideo(QRecordVideoEvent *e)
463{
464 //qDebug(log) << "Update image";
465 if(!e) return;
466 if(QMediaRecorder::RecordingState != m_Recorder.recorderState()) {
467 qCritical(log) << "Recorder is inavailable";
468 return;
469 }
470 QVideoFrame frame(e->GetImage());
471 bool bRet = m_VideoFrameInput.sendVideoFrame(frame);
472 if(!bRet) {
473 //TODO: 放入未成功发送队列,
474 // 当 QVideoFrameInput::readyToSendVideoFrame() 时,再发送
475 qDebug(log) << "m_VideoFrameInput.sendVideoFrame fail";
476 }
477}
478
480{
481 slotRecord(false);
482 return CBackend::Stop();
483}
484#endif
void sigUpdateRect(const QRect &r, const QImage &image)
Notify the CFrmView update image.
virtual void slotClipBoardChanged()=0
Be called when the clip board change.
virtual int WakeUp() override
Wake up Backend thread(background thread)
Backend interface.
Definition Backend.h:42
virtual int Stop()
Stop.
Definition Backend.cpp:92
void sigRunning()
Emitted when the plugin is successfully started.
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
Remote desktop operate interface.
virtual QWidget * GetViewer() override
Get Viewer.
virtual CParameterBase * GetParameter()
Get parameter.