玉兔远程控制 0.1.0-bate11
载入中...
搜索中...
未找到
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(logKey, "Backend.Desktop.Key")
16static Q_LOGGING_CATEGORY(logMouse, "Backend.Desktop.Mouse")
17static Q_LOGGING_CATEGORY(logInputMethod, "Backend.Desktop.InputMethod")
18
19#define TypeRecordVideo (QEvent::User + 1)
20class QRecordVideoEvent : public QEvent
21{
22public:
23 QRecordVideoEvent(const QImage& img): QEvent((QEvent::Type)TypeRecordVideo)
24 {
25 m_Image = img;
26 }
27 ~QRecordVideoEvent()
28 {
29 //qDebug(log) << Q_FUNC_INFO;
30 }
31
32 QImage GetImage()
33 {
34 return m_Image;
35 }
36private:
37 QImage m_Image;
38};
39
40int g_QtKeyboardModifiers = qRegisterMetaType<Qt::KeyboardModifiers>("KeyboardModifiers");
41int g_QtMouseButtons = qRegisterMetaType<Qt::MouseButtons>("MouseButtons");
42int g_QtMouseButton = qRegisterMetaType<Qt::MouseButton>("MouseButton");
43int g_QMessageBox_Icon = qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
44
45CBackendDesktop::CBackendDesktop(COperateDesktop *pOperate)
46 : CBackend(pOperate)
47#if HAVE_QT6_RECORD
48 , m_pParameterRecord(nullptr)
49 , m_VideoFrameInput(this)
50 , m_AudioBufferInput(this)
51 , m_AudioBufferOutput(this)
52#endif
53{
54 qDebug(log) << Q_FUNC_INFO;
55 if(pOperate) {
56 CFrmScroll* pScroll = qobject_cast<CFrmScroll*>(pOperate->GetViewer());
57 if(pScroll) {
58 CFrmViewer* pView = pScroll->GetViewer();
59 if(pView)
60 SetViewer(pView);
61 else {
62 QString szErr = pOperate->metaObject()->className();
63 szErr += "::GetViewer() is not CFrmViewer";
64 qWarning(log) << szErr.toStdString().c_str();
65 }
66 } else {
67 QString szErr = pOperate->metaObject()->className();
68 szErr += "::GetViewer() is not CFrmScroll";
69 qWarning(log) << szErr.toStdString().c_str();
70 }
71 SetConnect(pOperate);
72 }
73
74#if HAVE_QT6_RECORD
75 bool check = connect(
76 &m_Recorder, &QMediaRecorder::errorOccurred,
77 this, [&](QMediaRecorder::Error error, const QString &errorString) {
78 qDebug(log) << "Recorder error occurred:" << error << errorString;
79 slotRecord(false);
80 emit sigError(error, errorString);
81 });
82 Q_ASSERT(check);
83 check = connect(
84 &m_Recorder, &QMediaRecorder::recorderStateChanged,
85 this, [&](QMediaRecorder::RecorderState state){
86 qDebug(log) << "Recorder state changed:" << state;
87 if(QMediaRecorder::StoppedState == state)
88 {
89 slotRecord(false);
90 if(m_pParameterRecord) {
91 qDebug(log) << "End action:"
92 << m_pParameterRecord->GetEndAction()
93 << m_Recorder.actualLocation();
94 switch(m_pParameterRecord->GetEndAction())
95 {
96 case CParameterRecord::ENDACTION::OpenFile:
97 QDesktopServices::openUrl(m_Recorder.actualLocation());
98 break;
99 case CParameterRecord::ENDACTION::OpenFolder: {
100 QFileInfo fi(m_Recorder.actualLocation().toLocalFile());
101 QDesktopServices::openUrl(
102 QUrl::fromLocalFile(fi.absolutePath()));
103 break;
104 }
105 default:
106 break;
107 }
108 }
109 }
110 });
111 Q_ASSERT(check);
112 check = connect(&m_Recorder, &QMediaRecorder::actualLocationChanged,
113 this, [&](const QUrl &location){
114 qInfo(log) << "Recorder actual location changed:" << location;
115 });
116 Q_ASSERT(check);
117#endif
118}
119
120CBackendDesktop::~CBackendDesktop()
121{
122 qDebug(log) << Q_FUNC_INFO;
123}
124
125int CBackendDesktop::SetConnect(COperateDesktop *pOperate)
126{
127 //qDebug(log) << "CBackendDesktop::SetConnect:" << pOperate;
128 Q_ASSERT(pOperate);
129 if(!pOperate) return -1;
130
131 bool check = false;
132 check = connect(this, SIGNAL(sigServerName(const QString&)),
133 pOperate, SLOT(slotSetServerName(const QString&)));
134 Q_ASSERT(check);
135 check = connect(pOperate, SIGNAL(sigClipBoardChanged()),
136 this, SLOT(slotClipBoardChanged()));
137 Q_ASSERT(check);
138 check = connect(this, SIGNAL(sigSetClipboard(QMimeData*)),
139 pOperate, SLOT(slotSetClipboard(QMimeData*)));
140 Q_ASSERT(check);
141#if HAVE_QT6_RECORD
142 if(pOperate) {
143 m_pParameterRecord = &pOperate->GetParameter()->m_Record;
144 check = connect(pOperate, SIGNAL(sigRecord(bool)),
145 this, SLOT(slotRecord(bool)));
146 Q_ASSERT(check);
147
148 check = connect(pOperate, SIGNAL(sigRecordPause(bool)),
149 this, SLOT(slotRecordPause(bool)));
150 Q_ASSERT(check);
151 check = connect(
152 &m_Recorder,
153 SIGNAL(recorderStateChanged(QMediaRecorder::RecorderState)),
154 pOperate, SLOT(slotRecorderStateChanged(QMediaRecorder::RecorderState)));
155 Q_ASSERT(check);
156 }
157#endif
158 return 0;
159}
160
162{
163 Q_ASSERT(pView);
164 if(!pView) return -1;
165
166 bool check = false;
167 check = connect(this, SIGNAL(sigRunning()), pView, SLOT(slotRunning()));
168 Q_ASSERT(check);
169 check = connect(this, SIGNAL(sigSetDesktopSize(int, int)),
170 pView, SLOT(slotSetDesktopSize(int, int)));
171 Q_ASSERT(check);
172 check = connect(this, SIGNAL(sigServerName(const QString&)),
173 pView, SLOT(slotSetName(const QString&)));
174 Q_ASSERT(check);
175
176 check = connect(this, SIGNAL(sigUpdateRect(const QRect&, const QImage&)),
177 pView, SLOT(slotUpdateRect(const QRect&, const QImage&)));
178 Q_ASSERT(check);
179 check = connect(this, SIGNAL(sigUpdateRect(const QImage&)),
180 pView, SLOT(slotUpdateRect(const QImage&)));
181 Q_ASSERT(check);
182 check = connect(this, SIGNAL(sigUpdateCursor(const QCursor&)),
183 pView, SLOT(slotUpdateCursor(const QCursor&)));
184 Q_ASSERT(check);
185 check = connect(this, SIGNAL(sigUpdateCursorPosition(const QPoint&)),
186 pView, SLOT(slotUpdateCursorPosition(const QPoint&)));
187 Q_ASSERT(check);
188 check = connect(this, SIGNAL(sigUpdateLedState(unsigned int)),
189 pView, SLOT(slotUpdateLedState(unsigned int)));
190 Q_ASSERT(check);
191
192#if HAVE_QT6_RECORD
193 check = connect(this, SIGNAL(sigRecordVideo(bool, qreal)),
194 pView, SLOT(slotRecordVideo(bool, qreal)));
195 Q_ASSERT(check);
196 check = connect(pView, SIGNAL(sigRecordVideo(QImage)),
197 this, SLOT(slotRecordVideo(QImage)),
198 Qt::DirectConnection);
199 Q_ASSERT(check);
200#endif
201
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
238 return 0;
239}
240
241void CBackendDesktop::slotWheelEvent(QWheelEvent *event, QPoint pos)
242{
243 QWheelEvent* e = new QWheelEvent(
244 pos,
245#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
246 event->globalPosition(),
247#else
248 event->globalPos(),
249#endif
250 event->pixelDelta(), event->angleDelta(), event->buttons(),
251 event->modifiers(), event->phase(), event->inverted(), event->source());
252 QCoreApplication::postEvent(this, e);
253 WakeUp();
254}
255
256void CBackendDesktop::slotMouseMoveEvent(QMouseEvent *event, QPoint pos)
257{
258#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
259 QMouseEvent* e = new QMouseEvent(event->type(), pos, event->button(),
260 event->buttons(), event->modifiers());
261#else
262 QMouseEvent* e = new QMouseEvent(event->type(), pos, pos, event->button(),
263 event->buttons(), event->modifiers());
264#endif
265 QCoreApplication::postEvent(this, e);
266 WakeUp();
267}
268
269void CBackendDesktop::slotMousePressEvent(QMouseEvent *event, QPoint pos)
270{
271#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
272 QMouseEvent* e = new QMouseEvent(event->type(), pos, event->button(),
273 event->buttons(), event->modifiers());
274#else
275 QMouseEvent* e = new QMouseEvent(event->type(), pos, pos, event->button(),
276 event->buttons(), event->modifiers());
277#endif
278 QCoreApplication::postEvent(this, e);
279 WakeUp();
280}
281
282void CBackendDesktop::slotMouseReleaseEvent(QMouseEvent *event, QPoint pos)
283{
284#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
285 QMouseEvent* e = new QMouseEvent(event->type(), pos, event->button(),
286 event->buttons(), event->modifiers());
287#else
288 QMouseEvent* e = new QMouseEvent(event->type(), pos, pos, event->button(),
289 event->buttons(), event->modifiers());
290#endif
291 QCoreApplication::postEvent(this, e);
292 WakeUp();
293}
294
295void CBackendDesktop::slotKeyPressEvent(QKeyEvent *event)
296{
297 QKeyEvent* e = new QKeyEvent(event->type(), event->key(),
298 event->modifiers(), event->text());
299 QCoreApplication::postEvent(this, e);
300 WakeUp();
301}
302
303void CBackendDesktop::slotKeyReleaseEvent(QKeyEvent *event)
304{
305 QKeyEvent* e = new QKeyEvent(event->type(), event->key(),
306 event->modifiers(), event->text());
307 QCoreApplication::postEvent(this, e);
308 WakeUp();
309}
310
311void CBackendDesktop::slotInputMethodEvent(QInputMethodEvent *event)
312{
313 //qDebug(logInputMethod) << Q_FUNC_INFO << event;
314 if(event->commitString().isEmpty()) return;
315 QInputMethodEvent* e = new QInputMethodEvent(event->preeditString(), event->attributes());
316 e->setCommitString(event->commitString(), event->replacementStart(), event->replacementLength());
317 QCoreApplication::postEvent(this, e);
318 WakeUp();
319}
320
321bool CBackendDesktop::event(QEvent *event)
322{
323 //qDebug(log) << "CBackendDesktop::event" << event;
324 switch (event->type()) {
325 case QEvent::MouseButtonPress:
326 case QEvent::MouseButtonDblClick:
327 mousePressEvent((QMouseEvent*)event);
328 break;
329 case QEvent::MouseButtonRelease:
330 mouseReleaseEvent((QMouseEvent*)event);
331 break;
332 case QEvent::MouseMove:
333 mouseMoveEvent((QMouseEvent*)event);
334 break;
335 case QEvent::Wheel:
336 wheelEvent((QWheelEvent*)event);
337 break;
338 case QEvent::KeyPress:
339 keyPressEvent((QKeyEvent*)event);
340 break;
341 case QEvent::KeyRelease:
342 keyReleaseEvent((QKeyEvent*)event);
343 break;
344 case QEvent::InputMethod:
345 InputMethodEvent((QInputMethodEvent*) event);
346 break;
347#if HAVE_QT6_RECORD
348 case TypeRecordVideo:
349 RecordVideo((QRecordVideoEvent*)event);
350 break;
351#endif
352 default:
353 return QObject::event(event);
354 }
355
356 event->accept();
357 return true;
358}
359
360#if HAVE_QT6_RECORD
361void CBackendDesktop::slotRecord(bool bRecord)
362{
363 qDebug(log) << Q_FUNC_INFO << bRecord;
364 if(bRecord) {
365 if(QMediaRecorder::RecordingState == m_Recorder.recorderState())
366 return;
367 (*m_pParameterRecord) >> m_Recorder;
368 m_CaptureSession.setVideoFrameInput(&m_VideoFrameInput);
369 m_CaptureSession.setRecorder(&m_Recorder);
370 m_Recorder.record();
371 } else {
372 m_Recorder.stop();
373 m_CaptureSession.setVideoFrameInput(nullptr);
374 m_CaptureSession.setAudioBufferInput(nullptr);
375 m_CaptureSession.setRecorder(nullptr);
376 }
377 emit sigRecordVideo(bRecord, m_pParameterRecord->GetVideoFrameRate());
378}
379
380void CBackendDesktop::slotRecordPause(bool bPause)
381{
382 qDebug(log) << Q_FUNC_INFO << bPause;
383 if(bPause) {
384 if(m_Recorder.recorderState() == QMediaRecorder::RecordingState)
385 m_Recorder.pause();
386 } else {
387 if(m_Recorder.recorderState() == QMediaRecorder::PausedState)
388 m_Recorder.record();
389 }
390}
391
392void CBackendDesktop::slotRecordVideo(const QImage &img)
393{
394 QRecordVideoEvent* e = new QRecordVideoEvent(img);
395 if(!e) return;
396 QCoreApplication::postEvent(this, e);
397 WakeUp();
398}
399
400void CBackendDesktop::RecordVideo(QRecordVideoEvent *e)
401{
402 //qDebug(log) << "Update image";
403 if(!e) return;
404 if(QMediaRecorder::RecordingState != m_Recorder.recorderState()) {
405 qCritical(log) << "Recorder is inavailable";
406 return;
407 }
408 QVideoFrame frame(e->GetImage());
409 bool bRet = m_VideoFrameInput.sendVideoFrame(frame);
410 if(!bRet) {
411 //TODO: 放入未成功发送队列,
412 // 当 QVideoFrameInput::readyToSendVideoFrame() 时,再发送
413 qDebug(log) << "m_VideoFrameInput.sendVideoFrame fail";
414 }
415}
416
418{
419 slotRecord(false);
420 return CBackend::Stop();
421}
422#endif
void sigUpdateRect(const QRect &r, const QImage &image)
通知视图,图像更新
int SetViewer(CFrmViewer *pView)
virtual void slotClipBoardChanged()=0
当剪切板发生改变时调用
后端接口。它由协议插件实现。 它默认启动一个定时器来开启一个非 Qt 事件循环(就是普通的循环处理)。 详见: Start()、 slotTimeOut()、 OnProcess() 。 当然,它仍然支...
virtual int Stop()
停止
virtual int WakeUp()=0
唤醒后台线程。
void sigRunning()
当插件开始成功后触发。仅由插件触发
The scroll form class
用于显示从 CConnectDesktop 输出的图像,和向 CConnectDesktop 发送键盘、鼠标事件。
远程桌面操作接口
virtual CParameterDesktop * GetParameter() const
Get parameter
virtual QWidget * GetViewer() override
得到显示视图