玉兔远程控制 0.1.0-bate11
载入中...
搜索中...
未找到
FrmViewer.cpp
1// Author: Kang Lin <kl222@126.com>
2
3#include "FrmViewer.h"
4
5#include <QPainter>
6#include <QKeyEvent>
7#include <QResizeEvent>
8#include <QCursor>
9#include <QLoggingCategory>
10
11#if defined(Q_OS_WIN)
12 #include <Windows.h>
13#elif ! (defined(Q_OS_ANDROID) || defined(Q_OS_WIN) || defined(Q_OS_APPLE) || defined(Q_OS_MACOS))
14 #include <X11/Xlib.h>
15 #include <X11/XKBlib.h>
16 #define XK_MISCELLANY
17 #include <X11/keysymdef.h>
18#endif
19
20#undef KeyPress
21
22static Q_LOGGING_CATEGORY(log, "Client.FrmViewer")
23static Q_LOGGING_CATEGORY(logKey, "Client.FrmViewer.Key")
24static Q_LOGGING_CATEGORY(logMouse, "Client.FrmViewer.Mouse")
25static Q_LOGGING_CATEGORY(logInputMethod, "Client.FrmViewer.InputMethod")
26
27CFrmViewer::CFrmViewer(QWidget *parent)
28 : QWidget(parent)
29 , m_bRecordVideo(false)
30{
31 qDebug(log) << Q_FUNC_INFO;
32 setAttribute(Qt::WA_DeleteOnClose);
33
34 //qDebug(log) << "autoFillBackground:" << autoFillBackground();
35 //setAutoFillBackground(true);
36 //setAttribute(Qt::WA_OpaquePaintEvent);
37 //setAttribute(Qt::WA_NoSystemBackground);
38
39 slotSetAdaptWindows(ADAPT_WINDOWS::ZoomToWindow);
40 slotSetZoomFactor(1);
41
42 setMouseTracking(true);
43 setFocusPolicy(Qt::WheelFocus);
44 setFocus();
45
46 // When the operate is not run, don't accept keyboard and mouse event
47 // When the COperate::sigRunning() set true. accept keyboard and mouse event
48 // \see COperate::sigRunning()
49 setEnabled(false);
50
51 bool check = connect(&m_TimerRecordVideo, SIGNAL(timeout()),
52 this, SLOT(slotRecordVideo()));
53 Q_ASSERT(check);
54}
55
56CFrmViewer::~CFrmViewer()
57{
58 qDebug(log) << Q_FUNC_INFO;
59}
60
61QRectF CFrmViewer::GetAspectRationRect()
62{
63 QRectF dstRect = rect();
64 qreal newW = dstRect.width();
65 qreal newH = dstRect.height();
66 qreal newT = 0;
67 qreal newL = 0;
68
69 qreal rateW = static_cast<qreal>(rect().width())
70 / static_cast<qreal>(m_DesktopSize.width());
71 qreal rateH = static_cast<qreal>(rect().height())
72 / static_cast<qreal>(m_DesktopSize.height());
73 if(rateW < rateH)
74 {
75 newW = m_DesktopSize.width() * rateW;
76 newH = m_DesktopSize.height() * rateW;
77 newT = (static_cast<qreal>(rect().height()) - newH)
78 / static_cast<qreal>(2);
79 } else if(rateW > rateH) {
80 newW = m_DesktopSize.width() * rateH;
81 newH = m_DesktopSize.height() * rateH;
82 newL = (static_cast<qreal>(rect().width()) - newW)
83 / static_cast<qreal>(2);
84 }
85 dstRect = QRectF(newL, newT, newW, newH);
86 return dstRect;
87}
88
89void CFrmViewer::paintDesktop()
90{
91 QRectF dstRect = rect();
92
93 switch (m_AdaptWindows) {
95 case ADAPT_WINDOWS::Auto:
98 break;
100 dstRect.setLeft((rect().width() - m_DesktopSize.width()) >> 1);
101 dstRect.setTop((rect().height() - m_DesktopSize.height()) >> 1);
102 dstRect.setWidth(m_DesktopSize.width());
103 dstRect.setHeight(m_DesktopSize.height());
104 break;
106 {
107 dstRect = GetAspectRationRect();
108 break;
109 }
110 default:
111 break;
112 }
113
114 if(m_Desktop.isNull()) return;
115
116 QPainter painter(this);
117 // Clear background
118 if(ADAPT_WINDOWS::KeepAspectRationToWindow == m_AdaptWindows)
119 {
120#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
121 painter.fillRect(rect(), QBrush(palette().color(QPalette::Window)));
122#else
123 painter.fillRect(rect(), QBrush(palette().color(QPalette::Background)));
124#endif
125 } //*/
126
127 // 设置平滑模式
128 painter.setRenderHint(QPainter::SmoothPixmapTransform);
129 painter.drawImage(dstRect, m_Desktop);
130
131}
132
133void CFrmViewer::paintEvent(QPaintEvent *event)
134{
135 //qqDebug(log) << "CFrmViewer::paintEvent";
136 Q_UNUSED(event)
137
138 paintDesktop();
139}
140
141int CFrmViewer::PointToRemote(const QPointF &inPos, QPointF &outPos)
142{
143 //qDebug(logMouse) << "PointToRemote x:" << inPos.x() << ";y:" << inPos.y();
144
145 switch (m_AdaptWindows) {
146 case ADAPT_WINDOWS::Auto:
149 outPos = inPos;
150 break;
152 outPos.setX(inPos.x() / GetZoomFactor());
153 outPos.setY(inPos.y() / GetZoomFactor());
154 break;
156 outPos.setX(m_DesktopSize.width() * inPos.x() / width());
157 outPos.setY(m_DesktopSize.height() * inPos.y() / height());
158 break;
160 {
161 QRectF r = GetAspectRationRect();
162 if(inPos.x() < r.left()
163 || inPos.x() > r.right()
164 || inPos.y() < r.top()
165 || inPos.y() > r.bottom())
166 return -1;
167 outPos.setX(m_DesktopSize.width() * (inPos.x() - r.left()) / r.width());
168 outPos.setY(m_DesktopSize.height() * (inPos.y() - r.top()) / r.height());
169 break;
170 }
171 default:
172 qWarning(logMouse) << "Unsupport:" << m_AdaptWindows;
173 break;
174 }
175
176 return 0;
177}
178
179int CFrmViewer::PointFromRemote(const QPointF &inPos, QPointF &outPos)
180{
181 //qDebug(logMouse) << "PointFromRemote x:" << inPos.x() << ";y:" << inPos.y();
182
183 switch (m_AdaptWindows) {
184 case ADAPT_WINDOWS::Auto:
186 outPos = inPos;
187 break;
189 outPos.setX(inPos.x() * GetZoomFactor());
190 outPos.setY(inPos.y() * GetZoomFactor());
191 break;
193 outPos.setX(width() * inPos.x() / m_DesktopSize.width());
194 outPos.setY(height() * inPos.y() / m_DesktopSize.height());
195 break;
197 {
198 QRectF r = GetAspectRationRect();
199 outPos.setX(r.width() * inPos.x() / m_DesktopSize.width() + r.left());
200 outPos.setY(r.height() * inPos.y() / m_DesktopSize.height() + r.top());
201 break;
202 }
203 default:
204 qWarning(logMouse) << "Unsupport:" << m_AdaptWindows;
205 break;
206 }
207 return 0;
208}
209
210void CFrmViewer::mousePressEvent(QMouseEvent *event)
211{
212 QPointF pos =
213#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
214 event->position();
215#else
216 event->pos();
217#endif
218
219 if(PointToRemote(pos, pos)) return;
220 // event->buttons() 产生事件时,按键的状态
221 // event->button() 触发当前事件的按键
222 qDebug(logMouse) << "CFrmViewer::mousePressEvent"
223 << event << event->button() << event->buttons() << pos;
224 emit sigMousePressEvent(event, QPoint(pos.x(), pos.y()));
225 event->accept();
226 if(testAttribute(Qt::WA_InputMethodEnabled)
227 && event->button() == Qt::LeftButton) {
228 qDebug(logInputMethod) << Q_FUNC_INFO << "Update micro focus";
229 updateMicroFocus();
230 }
231}
232
233void CFrmViewer::mouseReleaseEvent(QMouseEvent *event)
234{
235 QPointF pos =
236#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
237 event->position();
238#else
239 event->pos();
240#endif
241 if(PointToRemote(pos, pos)) return;
242 // event->buttons() 产生事件时,按键的状态
243 // event->button() 触发当前事件的按键
244 qDebug(logMouse) << "CFrmViewer::mouseReleaseEvent"
245 << event << event->button() << event->buttons() << pos;
246 emit sigMouseReleaseEvent(event, QPoint(pos.x(), pos.y()));
247 event->accept();
248}
249
250void CFrmViewer::mouseMoveEvent(QMouseEvent *event)
251{
252 QPointF pos =
253#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
254 event->position();
255#else
256 event->pos();
257#endif
258 QPointF outPos = pos;
259 if(PointToRemote(pos, outPos)) return;
260 // event->buttons() 产生事件时,按键的状态
261 // event->button() 触发当前事件的按键
262 qDebug(logMouse) << "CFrmViewer::mouseMoveEvent"
263 << event->button() << event->buttons()
264 << pos << "outPos:" << outPos
265 << "mapToGlobal(pos):" << mapToGlobal(pos.toPoint());
266 emit sigMouseMoveEvent(event, QPoint(outPos.x(), outPos.y()));
267 emit sigMouseMoveEvent(event);
268 event->accept();
269}
270
271void CFrmViewer::wheelEvent(QWheelEvent *event)
272{
273 QPointF pos =
274#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
275 event->position();
276#else
277 event->pos();
278#endif
279 if(PointToRemote(pos, pos)) return;
280 qDebug(logMouse) << "CFrmViewer::wheelEvent"
281 << event->buttons() << event->angleDelta() << pos;
282 emit sigWheelEvent(event, QPoint(pos.x(), pos.y()));
283 event->accept();
284}
285
286void CFrmViewer::keyPressEvent(QKeyEvent *event)
287{
288 qDebug(logKey) << "CFrmViewer::keyPressEvent" << event;
289 emit sigKeyPressEvent(event);
290 event->accept();
291}
292
293void CFrmViewer::keyReleaseEvent(QKeyEvent *event)
294{
295 qDebug(logKey) << "CFrmViewer::keyReleaseEvent" << event;
296 emit sigKeyReleaseEvent(event);
297 event->accept();
298}
299
300void CFrmViewer::inputMethodEvent(QInputMethodEvent *event)
301{
302 qDebug(logInputMethod) << Q_FUNC_INFO << event
303 << event->preeditString() << event->commitString()
304 << event->replacementStart() << event->replacementLength();
305 emit sigInputMethodEvent(event);
306 event->accept();
307 /*
308 if(event->commitString().isEmpty()) return;
309 QPoint p(cursor().pos().x() + fontMetrics().horizontalAdvance(event->commitString()), cursor().pos().y());
310 qDebug(logKey) << cursor().pos() << fontMetrics().horizontalAdvance(event->commitString());
311 slotUpdateCursorPosition(p);//*/
312}
313
314QVariant CFrmViewer::inputMethodQuery(Qt::InputMethodQuery query) const
315{
316 //*
317 qDebug(logInputMethod) << Q_FUNC_INFO << query;
318 switch ( query )
319 {
320 case Qt::ImCursorRectangle: {
321 QPoint p = mapFromGlobal(cursor().pos());
322 QRect r(p.x(), p.y() , 1, fontMetrics().height());
323 qDebug(logInputMethod) << "Qt::ImCursorRectangle:" << r;
324 return r;
325 }
326 // case Qt::ImCursorPosition: {
327 // QPoint p = cursor().pos();
328 // qDebug(logInputMethod) << "Qt::ImCursorPosition:" << p;
329 // return p;
330 // }
331 default:
332 break;
333 }//*/
334 QVariant r = QWidget::inputMethodQuery(query);
335 qDebug(logInputMethod) << Q_FUNC_INFO << query << r;
336 return r;
337}
338
340{
341 qDebug(logInputMethod) << Q_FUNC_INFO << bEnable;
342 setAttribute(Qt::WA_InputMethodEnabled, bEnable);
343}
344
345QSize CFrmViewer::GetDesktopSize()
346{
347 return m_DesktopSize;
348}
349
351{
352 return m_dbZoomFactor;
353}
354
355int CFrmViewer::slotSetZoomFactor(double newZoomFactor)
356{
357 if(newZoomFactor < 0) return -1;
358 if (qFuzzyCompare(m_dbZoomFactor, newZoomFactor))
359 return 0;
360 m_dbZoomFactor = newZoomFactor;
361 return 0;
362}
363
364int CFrmViewer::ReSize(int width, int height)
365{
366 int w = width * GetZoomFactor();
367 int h = height * GetZoomFactor();
368 resize(w, h);
369 return 0;
370}
371
372void CFrmViewer::slotSetAdaptWindows(ADAPT_WINDOWS aw)
373{
374 m_AdaptWindows = aw;
375 if(!m_Desktop.isNull())
376 {
377 switch (m_AdaptWindows) {
380 slotSetZoomFactor(1);
382 ReSize(m_DesktopSize.width(), m_DesktopSize.height());
383 break;
384 default:
385 break;
386 }
387 }
388 update();
389 //setFocus();
390}
391
392CFrmViewer::ADAPT_WINDOWS CFrmViewer::GetAdaptWindows()
393{
394 return m_AdaptWindows;
395}
396
397void CFrmViewer::slotSetDesktopSize(int width, int height)
398{
399 m_DesktopSize = QSize(width, height);
400 m_Desktop = QImage(width, height, QImage::Format_RGB32);
401
402 if(ADAPT_WINDOWS::Original == m_AdaptWindows
403 || ADAPT_WINDOWS::OriginalCenter == m_AdaptWindows
404 || ADAPT_WINDOWS::Zoom == m_AdaptWindows)
405 ReSize(width, height);
406
407 return;
408}
409
411{
412 setEnabled(true);
413}
414
415void CFrmViewer::slotSetName(const QString& szName)
416{
417 this->setWindowTitle(szName);
418 emit sigServerName(szName);
419}
420
421void CFrmViewer::slotUpdateRect(const QImage& image)
422{
423 //qDebug(log) << "void CFrmViewer::slotUpdateRect(const QImage& image)" << image;
424 m_Desktop = image;
425
426 if(m_bRecordVideo)
427 emit sigRecordVideo(m_Desktop);
428
429 update();
430 return;
431}
432
433void CFrmViewer::slotUpdateRect(const QRect& r, const QImage& image)
434{
435#if DEBUG
436 if(r.width() != image.rect().width() || r.height() != image.rect().height())
437 {
438 qWarning(log) << "Image is error";
439 }
440#endif
441 //qDebug(log) << "void CFrmViewer::slotUpdateRect(const QRect& r, const QImage& image)" << r << image;
442 if(m_Desktop.isNull() || m_Desktop.rect() == r)
443 {
444 m_Desktop = image;
445 //qDebug(log) << "Update image size is same old image size";
446 } else {
447 QPainter painter(&m_Desktop);
448 painter.drawImage(r, image);
449 //qDebug(log) << "Update image size isn't same old image size" << r << image.rect() << image;
450 }
451
452 if(m_bRecordVideo)
453 emit sigRecordVideo(m_Desktop);
454
455 update();
456}
457
458void CFrmViewer::slotUpdateCursor(const QCursor& cursor)
459{
460 qDebug(logMouse) << Q_FUNC_INFO << cursor << cursor.pos();
461 setCursor(cursor);
462 if(testAttribute(Qt::WA_InputMethodEnabled)) {
463 qDebug(logInputMethod) << Q_FUNC_INFO << "Update micro focus";
464 updateMicroFocus();
465 }
466 return;
467}
468
470{
471 qDebug(logMouse) << Q_FUNC_INFO << pos;
472 QPointF outPos;
473 if(PointFromRemote(pos, outPos)) return;
474 cursor().setPos(mapToGlobal(outPos.toPoint()));
475 qDebug(logMouse) << "Cursor Point:" << pos << "Out:" << outPos
476 << "cursor().pos():" << cursor().pos();
477 if(testAttribute(Qt::WA_InputMethodEnabled)) {
478 qDebug(logInputMethod) << Q_FUNC_INFO << "Update micro focus";
479 updateMicroFocus();
480 }
481}
482
483#if ! (defined(Q_OS_WIN) || defined(Q_OS_APPLE) || defined(Q_OS_ANDROID) || defined(Q_OS_APPLE) || defined(Q_OS_MACOS))
484unsigned int getModifierMask(unsigned int keysym)
485{
486 XkbDescPtr xkb;
487 unsigned int mask, keycode;
488 XkbAction *act;
489
490 mask = 0;
491 Display *dpy = XOpenDisplay(0);
492 xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
493 if (xkb == nullptr)
494 return 0;
495
496 for (keycode = xkb->min_key_code; keycode <= xkb->max_key_code; keycode++) {
497 unsigned int state_out;
498 KeySym ks;
499
500 XkbTranslateKeyCode(xkb, keycode, 0, &state_out, &ks);
501 if (ks == NoSymbol)
502 continue;
503
504 if (ks == keysym)
505 break;
506 }
507
508 // KeySym not mapped?
509 if (keycode > xkb->max_key_code)
510 goto out;
511
512 act = XkbKeyAction(xkb, keycode, 0);
513 if (act == nullptr)
514 goto out;
515 if (act->type != XkbSA_LockMods)
516 goto out;
517
518 if (act->mods.flags & XkbSA_UseModMapMods)
519 mask = xkb->map->modmap[keycode];
520 else
521 mask = act->mods.mask;
522
523out:
524 XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
525
526 return mask;
527}
528#endif
529
530void CFrmViewer::slotUpdateLedState(unsigned int state)
531{
532 qDebug(log, "Got server LED state: 0x%08x", state);
533
534 if (!hasFocus())
535 return;
536
537 //TODO: test led
538
539#if defined(WIN32)
540 INPUT input[6];
541 UINT count;
542 UINT ret;
543
544 memset(input, 0, sizeof(input));
545 count = 0;
546
547 if (!!(state & CapsLock) != !!(GetKeyState(VK_CAPITAL) & 0x1)) {
548 input[count].type = input[count+1].type = INPUT_KEYBOARD;
549 input[count].ki.wVk = input[count+1].ki.wVk = VK_CAPITAL;
550 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
551 input[count].ki.dwFlags = 0;
552 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
553 count += 2;
554 }
555
556 if (!!(state & NumLock) != !!(GetKeyState(VK_NUMLOCK) & 0x1)) {
557 input[count].type = input[count+1].type = INPUT_KEYBOARD;
558 input[count].ki.wVk = input[count+1].ki.wVk = VK_NUMLOCK;
559 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
560 input[count].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
561 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
562 count += 2;
563 }
564
565 if (!!(state & ScrollLock) != !!(GetKeyState(VK_SCROLL) & 0x1)) {
566 input[count].type = input[count+1].type = INPUT_KEYBOARD;
567 input[count].ki.wVk = input[count+1].ki.wVk = VK_SCROLL;
568 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
569 input[count].ki.dwFlags = 0;
570 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
571 count += 2;
572 }
573
574 if (count == 0)
575 return;
576
577 ret = SendInput(count, input, sizeof(*input));
578 if (ret < count)
579 qCritical(log) << "Failed to update keyboard LED state:" << GetLastError();
580#elif defined(Q_OS_APPLE)
581
582 // No support for Scroll Lock //
583 ;
584
585#elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_APPLE)
586 unsigned int affect, values;
587 unsigned int mask;
588
589 Bool ret;
590
591 affect = values = 0;
592
593 affect |= LockMask;
594 if (state & CapsLock)
595 values |= LockMask;
596
597 mask = getModifierMask(XK_Num_Lock);
598 affect |= mask;
599 if (state & NumLock)
600 values |= mask;
601
602 mask = getModifierMask(XK_Scroll_Lock);
603 affect |= mask;
604 if (state & ScrollLock)
605 values |= mask;
606 Display *dpy = XOpenDisplay(0);
607 ret = XkbLockModifiers(dpy, XkbUseCoreKbd, affect, values);
608 if (!ret)
609 qCritical(log) << tr("Failed to update keyboard LED state");
610 XFlush(dpy);
611 XCloseDisplay(dpy);
612#endif
613}
614
615QImage CFrmViewer::GrabImage(int x, int y, int w, int h)
616{
617 int width = w, height = h;
618 if(-1 == w)
619 width = m_DesktopSize.width();
620 if(-1 == w)
621 height = m_DesktopSize.height();
622 return m_Desktop.copy(x, y, width, height);
623}
624
625void CFrmViewer::slotRecordVideo(bool bRecord, qreal nRate)
626{
627 m_bRecordVideo = bRecord;
628 if(0 == nRate) {
629 qWarning(log) << "The video frame rate is 0. it is not record static remote desktop."
630 << "If need record static remote desktop,"
631 << "please 'Record->Video->Encoding mode' don't select 'Constant quality' in settings dialog,"
632 << "then set 'Record->Video->Video frame'";
633 return;
634 }
635
636 m_bRecordVideo = false;
637 if(bRecord) {
638 qreal ms = 1000 / nRate;
639 //qDebug(log) << "Rate:" << nRate << ms << "ms";
640 m_TimerRecordVideo.start(ms);
641 }
642 else
643 m_TimerRecordVideo.stop();
644}
645
646void CFrmViewer::slotRecordVideo()
647{
648 emit sigRecordVideo(m_Desktop);
649}
650
651void CFrmViewer::focusInEvent(QFocusEvent *event)
652{
653 qDebug(log) << Q_FUNC_INFO << event << this;
654 emit sigViewerFocusIn(this);
655}
656
657void CFrmViewer::focusOutEvent(QFocusEvent *event)
658{
659 qDebug(log) << Q_FUNC_INFO << event << this;
660}
用于显示从 CConnectDesktop 输出的图像,和向 CConnectDesktop 发送键盘、鼠标事件。
void slotSetDesktopSize(int width, int height)
Update desktop size
ADAPT_WINDOWS
窗口适配枚举常量
@ Original
原始桌面大小,桌面的左上点与窗口的左上点对齐
@ Zoom
缩放窗口大小等于桌面大小 * 系数
@ KeepAspectRationToWindow
保持长宽比缩放到窗口大小,窗口是固定的
@ OriginalCenter
原始桌面大小,桌面中心点与窗口中心点对齐
@ ZoomToWindow
桌面缩放到窗口大小,窗口是固定的
@ Disable
禁用适配窗口
void sigViewerFocusIn(QWidget *pView)
视图获得焦点
void slotUpdateCursor(const QCursor &cursor)
Update cursor
void slotUpdateRect(const QRect &r, const QImage &image)
Update image
void slotRunning()
Enable mouse and keyboard
void slotSetName(const QString &szName)
Update desktop name
void slotUpdateCursorPosition(const QPoint &pos)
Update cursor position
void slotEnableInputMethod(bool bEnable)
Enable input method
double GetZoomFactor() const
调整缩放系数。 调整完成后需要调用 SetAdaptWindows(FrmViewer::Zoom) 缩放窗口大小。