玉兔远程控制 0.0.31
载入中...
搜索中...
未找到
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))
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")
25
26CFrmViewer::CFrmViewer(QWidget *parent)
27 : QWidget(parent)
28 , m_bRecordVideo(false)
29{
30 qDebug(log) << Q_FUNC_INFO;
31 setAttribute(Qt::WA_DeleteOnClose);
32 //qDebug(log) << "autoFillBackground:" << autoFillBackground();
33 //setAutoFillBackground(true);
34 //setAttribute(Qt::WA_OpaquePaintEvent);
35 //setAttribute(Qt::WA_NoSystemBackground);
36
37 slotSetAdaptWindows(ADAPT_WINDOWS::ZoomToWindow);
38 slotSetZoomFactor(1);
39
40 setMouseTracking(true);
41 setFocusPolicy(Qt::WheelFocus);
42 setFocus();
43
44 // When the connecter is not connected, don't accept keyboard and mouse event
45 // When the CConnecter::sigConnected() set true. accept keyboard and mouse event
46 // \see CConnecter::sigConnected()
47 setEnabled(false);
48}
49
50CFrmViewer::~CFrmViewer()
51{
52 qDebug(log) << Q_FUNC_INFO;
53}
54
55QRectF CFrmViewer::GetAspectRationRect()
56{
57 QRectF dstRect = rect();
58 qreal newW = dstRect.width();
59 qreal newH = dstRect.height();
60 qreal newT = 0;
61 qreal newL = 0;
62
63 qreal rateW = static_cast<qreal>(rect().width())
64 / static_cast<qreal>(m_DesktopSize.width());
65 qreal rateH = static_cast<qreal>(rect().height())
66 / static_cast<qreal>(m_DesktopSize.height());
67 if(rateW < rateH)
68 {
69 newW = m_DesktopSize.width() * rateW;
70 newH = m_DesktopSize.height() * rateW;
71 newT = (static_cast<qreal>(rect().height()) - newH)
72 / static_cast<qreal>(2);
73 } else if(rateW > rateH) {
74 newW = m_DesktopSize.width() * rateH;
75 newH = m_DesktopSize.height() * rateH;
76 newL = (static_cast<qreal>(rect().width()) - newW)
77 / static_cast<qreal>(2);
78 }
79 dstRect = QRectF(newL, newT, newW, newH);
80 return dstRect;
81}
82
83void CFrmViewer::paintDesktop()
84{
85 QRectF dstRect = rect();
86
87 switch (m_AdaptWindows) {
89 case ADAPT_WINDOWS::Auto:
92 break;
94 dstRect.setLeft((rect().width() - m_DesktopSize.width()) >> 1);
95 dstRect.setTop((rect().height() - m_DesktopSize.height()) >> 1);
96 dstRect.setWidth(m_DesktopSize.width());
97 dstRect.setHeight(m_DesktopSize.height());
98 break;
100 {
101 dstRect = GetAspectRationRect();
102 break;
103 }
104 default:
105 break;
106 }
107
108 if(m_Desktop.isNull()) return;
109
110 QPainter painter(this);
111 // Clear background
112 if(ADAPT_WINDOWS::KeepAspectRationToWindow == m_AdaptWindows)
113 {
114#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
115 painter.fillRect(rect(), QBrush(palette().color(QPalette::Window)));
116#else
117 painter.fillRect(rect(), QBrush(palette().color(QPalette::Background)));
118#endif
119 } //*/
120
121 // 设置平滑模式
122 painter.setRenderHint(QPainter::SmoothPixmapTransform);
123 painter.drawImage(dstRect, m_Desktop);
124
125}
126
127void CFrmViewer::paintEvent(QPaintEvent *event)
128{
129 //qqDebug(log) << "CFrmViewer::paintEvent";
130 Q_UNUSED(event)
131
132 paintDesktop();
133}
134
135int CFrmViewer::TranslationMousePoint(QPointF inPos, QPointF &outPos)
136{
137 qDebug(logMouse) << "TranslationPoint x:" << inPos.x() << ";y:" << inPos.y();
138
139 switch (m_AdaptWindows) {
140 case ADAPT_WINDOWS::Auto:
143 outPos = inPos;
144 break;
146 outPos.setX(inPos.x() / GetZoomFactor());
147 outPos.setY(inPos.y() / GetZoomFactor());
148 break;
150 outPos.setX(m_DesktopSize.width() * inPos.x() / width());
151 outPos.setY(m_DesktopSize.height() * inPos.y() / height());
152 break;
154 {
155 QRectF r = GetAspectRationRect();
156 if(inPos.x() < r.left()
157 || inPos.x() > r.right()
158 || inPos.y() < r.top()
159 || inPos.y() > r.bottom())
160 return -1;
161 outPos.setX(m_DesktopSize.width() * (inPos.x() - r.left()) / r.width());
162 outPos.setY(m_DesktopSize.height() * (inPos.y() - r.top()) / r.height());
163 break;
164 }
165 default:
166 break;
167 }
168
169 return 0;
170}
171
172void CFrmViewer::mousePressEvent(QMouseEvent *event)
173{
174 QPointF pos =
175#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
176 event->position();
177#else
178 event->pos();
179#endif
180
181 if(TranslationMousePoint(pos, pos)) return;
182 // event->buttons() 产生事件时,按键的状态
183 // event->button() 触发当前事件的按键
184 qDebug(logMouse) << "CFrmViewer::mousePressEvent"
185 << event << event->button() << event->buttons() << pos;
186 emit sigMousePressEvent(event, QPoint(pos.x(), pos.y()));
187 event->accept();
188}
189
190void CFrmViewer::mouseReleaseEvent(QMouseEvent *event)
191{
192 QPointF pos =
193#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
194 event->position();
195#else
196 event->pos();
197#endif
198 if(TranslationMousePoint(pos, pos)) return;
199 // event->buttons() 产生事件时,按键的状态
200 // event->button() 触发当前事件的按键
201 qDebug(logMouse) << "CFrmViewer::mouseReleaseEvent"
202 << event << event->button() << event->buttons() << pos;
203 emit sigMouseReleaseEvent(event, QPoint(pos.x(), pos.y()));
204 event->accept();
205}
206
207void CFrmViewer::mouseMoveEvent(QMouseEvent *event)
208{
209 QPointF pos =
210#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
211 event->position();
212#else
213 event->pos();
214#endif
215 if(TranslationMousePoint(pos, pos)) return;
216 // event->buttons() 产生事件时,按键的状态
217 // event->button() 触发当前事件的按键
218 qDebug(logMouse) << "CFrmViewer::mouseMoveEvent"
219 << event->button() << event->buttons() << pos;
220 emit sigMouseMoveEvent(event, QPoint(pos.x(), pos.y()));
221 emit sigMouseMoveEvent(event);
222 event->accept();
223}
224
225void CFrmViewer::wheelEvent(QWheelEvent *event)
226{
227 QPointF pos =
228#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
229 event->position();
230#else
231 event->pos();
232#endif
233 if(TranslationMousePoint(pos, pos)) return;
234 qDebug(logMouse) << "CFrmViewer::wheelEvent"
235 << event->buttons() << event->angleDelta() << pos;
236 emit sigWheelEvent(event, QPoint(pos.x(), pos.y()));
237 event->accept();
238}
239
240void CFrmViewer::keyPressEvent(QKeyEvent *event)
241{
242 qDebug(logKey) << "CFrmViewer::keyPressEvent" << event;
243 emit sigKeyPressEvent(event);
244 event->accept();
245}
246
247void CFrmViewer::keyReleaseEvent(QKeyEvent *event)
248{
249 qDebug(logKey) << "CFrmViewer::keyReleaseEvent" << event;
250 emit sigKeyReleaseEvent(event);
251 event->accept();
252}
253
254void CFrmViewer::slotSystemCombination()
255{
256 // Send ctl+alt+del
257 emit sigKeyPressEvent(new QKeyEvent(QKeyEvent::KeyPress, Qt::Key_Control, Qt::NoModifier));
258 emit sigKeyPressEvent(new QKeyEvent(QKeyEvent::KeyPress, Qt::Key_Alt, Qt::NoModifier));
259 emit sigKeyPressEvent(new QKeyEvent(QKeyEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier));
260 emit sigKeyPressEvent(new QKeyEvent(QKeyEvent::KeyPress, Qt::Key_Control, Qt::NoModifier));
261 emit sigKeyPressEvent(new QKeyEvent(QKeyEvent::KeyPress, Qt::Key_Alt, Qt::NoModifier));
262 emit sigKeyPressEvent(new QKeyEvent(QKeyEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier));
263}
264
265QSize CFrmViewer::GetDesktopSize()
266{
267 return m_DesktopSize;
268}
269
271{
272 return m_dbZoomFactor;
273}
274
275int CFrmViewer::slotSetZoomFactor(double newZoomFactor)
276{
277 if(newZoomFactor < 0) return -1;
278 if (qFuzzyCompare(m_dbZoomFactor, newZoomFactor))
279 return 0;
280 m_dbZoomFactor = newZoomFactor;
281 return 0;
282}
283
284int CFrmViewer::ReSize(int width, int height)
285{
286 int w = width * GetZoomFactor();
287 int h = height * GetZoomFactor();
288 resize(w, h);
289 return 0;
290}
291
292void CFrmViewer::slotSetAdaptWindows(ADAPT_WINDOWS aw)
293{
294 m_AdaptWindows = aw;
295 if(!m_Desktop.isNull())
296 {
297 switch (m_AdaptWindows) {
300 slotSetZoomFactor(1);
302 ReSize(m_DesktopSize.width(), m_DesktopSize.height());
303 break;
304 default:
305 break;
306 }
307 }
308 update();
309 //setFocus();
310}
311
312CFrmViewer::ADAPT_WINDOWS CFrmViewer::GetAdaptWindows()
313{
314 return m_AdaptWindows;
315}
316
317void CFrmViewer::slotSetDesktopSize(int width, int height)
318{
319 m_DesktopSize = QSize(width, height);
320 m_Desktop = QImage(width, height, QImage::Format_RGB32);
321
322 if(ADAPT_WINDOWS::Original == m_AdaptWindows
323 || ADAPT_WINDOWS::OriginalCenter == m_AdaptWindows
324 || ADAPT_WINDOWS::Zoom == m_AdaptWindows)
325 ReSize(width, height);
326
327 return;
328}
329
330void CFrmViewer::slotConnected()
331{
332 setEnabled(true);
333}
334
335void CFrmViewer::slotSetName(const QString& szName)
336{
337 this->setWindowTitle(szName);
338 emit sigServerName(szName);
339}
340
341void CFrmViewer::slotUpdateRect(const QImage& image)
342{
343 //qDebug(log) << "void CFrmViewer::slotUpdateRect(const QImage& image)" << image;
344 m_Desktop = image;
345
346 if(m_bRecordVideo)
347 emit sigRecordVideo(m_Desktop);
348
349 update();
350 return;
351}
352
353void CFrmViewer::slotUpdateRect(const QRect& r, const QImage& image)
354{
355#if DEBUG
356 if(r.width() != image.rect().width() || r.height() != image.rect().height())
357 {
358 qWarning(log) << "Image is error";
359 }
360#endif
361 //qDebug(log) << "void CFrmViewer::slotUpdateRect(const QRect& r, const QImage& image)" << r << image;
362 if(m_Desktop.isNull() || m_Desktop.rect() == r)
363 {
364 m_Desktop = image;
365 //qDebug(log) << "Update image size is same old image size";
366 }
367 else
368 {
369 QPainter painter(&m_Desktop);
370 painter.drawImage(r, image);
371 //qDebug(log) << "Update image size isn't same old image size" << r << image.rect() << image;
372 }
373
374 if(m_bRecordVideo)
375 emit sigRecordVideo(m_Desktop);
376
377 update();
378}
379
380void CFrmViewer::slotUpdateCursor(const QCursor& cursor)
381{
382 setCursor(cursor);
383}
384
386{
387 cursor().setPos(pos);
388}
389
390#if ! (defined(Q_OS_WIN) || defined(Q_OS_APPLE) || defined(Q_OS_ANDROID) || defined(__APPLE__))
391unsigned int getModifierMask(unsigned int keysym)
392{
393 XkbDescPtr xkb;
394 unsigned int mask, keycode;
395 XkbAction *act;
396
397 mask = 0;
398 Display *dpy = XOpenDisplay(0);
399 xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
400 if (xkb == nullptr)
401 return 0;
402
403 for (keycode = xkb->min_key_code; keycode <= xkb->max_key_code; keycode++) {
404 unsigned int state_out;
405 KeySym ks;
406
407 XkbTranslateKeyCode(xkb, keycode, 0, &state_out, &ks);
408 if (ks == NoSymbol)
409 continue;
410
411 if (ks == keysym)
412 break;
413 }
414
415 // KeySym not mapped?
416 if (keycode > xkb->max_key_code)
417 goto out;
418
419 act = XkbKeyAction(xkb, keycode, 0);
420 if (act == nullptr)
421 goto out;
422 if (act->type != XkbSA_LockMods)
423 goto out;
424
425 if (act->mods.flags & XkbSA_UseModMapMods)
426 mask = xkb->map->modmap[keycode];
427 else
428 mask = act->mods.mask;
429
430out:
431 XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
432
433 return mask;
434}
435#endif
436
437void CFrmViewer::slotUpdateLedState(unsigned int state)
438{
439 qDebug(log, "Got server LED state: 0x%08x", state);
440
441 if (!hasFocus())
442 return;
443
444 //TODO: test led
445
446#if defined(WIN32)
447 INPUT input[6];
448 UINT count;
449 UINT ret;
450
451 memset(input, 0, sizeof(input));
452 count = 0;
453
454 if (!!(state & CapsLock) != !!(GetKeyState(VK_CAPITAL) & 0x1)) {
455 input[count].type = input[count+1].type = INPUT_KEYBOARD;
456 input[count].ki.wVk = input[count+1].ki.wVk = VK_CAPITAL;
457 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
458 input[count].ki.dwFlags = 0;
459 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
460 count += 2;
461 }
462
463 if (!!(state & NumLock) != !!(GetKeyState(VK_NUMLOCK) & 0x1)) {
464 input[count].type = input[count+1].type = INPUT_KEYBOARD;
465 input[count].ki.wVk = input[count+1].ki.wVk = VK_NUMLOCK;
466 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
467 input[count].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
468 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
469 count += 2;
470 }
471
472 if (!!(state & ScrollLock) != !!(GetKeyState(VK_SCROLL) & 0x1)) {
473 input[count].type = input[count+1].type = INPUT_KEYBOARD;
474 input[count].ki.wVk = input[count+1].ki.wVk = VK_SCROLL;
475 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
476 input[count].ki.dwFlags = 0;
477 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
478 count += 2;
479 }
480
481 if (count == 0)
482 return;
483
484 ret = SendInput(count, input, sizeof(*input));
485 if (ret < count)
486 qCritical(log) << "Failed to update keyboard LED state:" << GetLastError();
487#elif defined(Q_OS_APPLE)
488
489 // No support for Scroll Lock //
490 ;
491
492#elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_APPLE)
493 unsigned int affect, values;
494 unsigned int mask;
495
496 Bool ret;
497
498 affect = values = 0;
499
500 affect |= LockMask;
501 if (state & CapsLock)
502 values |= LockMask;
503
504 mask = getModifierMask(XK_Num_Lock);
505 affect |= mask;
506 if (state & NumLock)
507 values |= mask;
508
509 mask = getModifierMask(XK_Scroll_Lock);
510 affect |= mask;
511 if (state & ScrollLock)
512 values |= mask;
513 Display *dpy = XOpenDisplay(0);
514 ret = XkbLockModifiers(dpy, XkbUseCoreKbd, affect, values);
515 if (!ret)
516 qCritical(log) << tr("Failed to update keyboard LED state");
517 XFlush(dpy);
518 XCloseDisplay(dpy);
519#endif
520}
521
522QImage CFrmViewer::GrabImage(int x, int y, int w, int h)
523{
524 int width = w, height = h;
525 if(-1 == w)
526 width = m_DesktopSize.width();
527 if(-1 == w)
528 height = m_DesktopSize.height();
529 return m_Desktop.copy(x, y, width, height);
530}
531
532void CFrmViewer::slotRecordVideo(bool bRecord)
533{
534 m_bRecordVideo = bRecord;
535}
用于显示从 CConnectDesktop 输出的图像,和向 CConnectDesktop 发送键盘、鼠标事件。
Definition FrmViewer.h:49
void slotSetDesktopSize(int width, int height)
Update desktop size
ADAPT_WINDOWS
窗口适配枚举常量
Definition FrmViewer.h:61
@ Original
原始桌面大小,桌面的左上点与窗口的左上点对齐
@ Zoom
缩放窗口大小等于桌面大小 * 系数
@ KeepAspectRationToWindow
保持长宽比缩放到窗口大小,窗口是固定的
@ OriginalCenter
原始桌面大小,桌面中心点与窗口中心点对齐
@ ZoomToWindow
桌面缩放到窗口大小,窗口是固定的
@ Disable
禁用适配窗口
void slotUpdateCursor(const QCursor &cursor)
Update cursor
void slotUpdateRect(const QRect &r, const QImage &image)
Update image
void slotSetName(const QString &szName)
Update desktop name
void slotUpdateCursorPosition(const QPoint &pos)
Update cursor position
double GetZoomFactor() const
调整缩放系数。 调整完成后需要调用 SetAdaptWindows(FrmViewer::Zoom) 缩放窗口大小。