Rabbit Remote Control 0.0.36
Loading...
Searching...
No Matches
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")
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 bool check = connect(&m_TimerRecordVideo, SIGNAL(timeout()),
50 this, SLOT(slotRecordVideo()));
51 Q_ASSERT(check);
52}
53
54CFrmViewer::~CFrmViewer()
55{
56 qDebug(log) << Q_FUNC_INFO;
57}
58
59QRectF CFrmViewer::GetAspectRationRect()
60{
61 QRectF dstRect = rect();
62 qreal newW = dstRect.width();
63 qreal newH = dstRect.height();
64 qreal newT = 0;
65 qreal newL = 0;
66
67 qreal rateW = static_cast<qreal>(rect().width())
68 / static_cast<qreal>(m_DesktopSize.width());
69 qreal rateH = static_cast<qreal>(rect().height())
70 / static_cast<qreal>(m_DesktopSize.height());
71 if(rateW < rateH)
72 {
73 newW = m_DesktopSize.width() * rateW;
74 newH = m_DesktopSize.height() * rateW;
75 newT = (static_cast<qreal>(rect().height()) - newH)
76 / static_cast<qreal>(2);
77 } else if(rateW > rateH) {
78 newW = m_DesktopSize.width() * rateH;
79 newH = m_DesktopSize.height() * rateH;
80 newL = (static_cast<qreal>(rect().width()) - newW)
81 / static_cast<qreal>(2);
82 }
83 dstRect = QRectF(newL, newT, newW, newH);
84 return dstRect;
85}
86
87void CFrmViewer::paintDesktop()
88{
89 QRectF dstRect = rect();
90
91 switch (m_AdaptWindows) {
93 case ADAPT_WINDOWS::Auto:
96 break;
98 dstRect.setLeft((rect().width() - m_DesktopSize.width()) >> 1);
99 dstRect.setTop((rect().height() - m_DesktopSize.height()) >> 1);
100 dstRect.setWidth(m_DesktopSize.width());
101 dstRect.setHeight(m_DesktopSize.height());
102 break;
104 {
105 dstRect = GetAspectRationRect();
106 break;
107 }
108 default:
109 break;
110 }
111
112 if(m_Desktop.isNull()) return;
113
114 QPainter painter(this);
115 // Clear background
116 if(ADAPT_WINDOWS::KeepAspectRationToWindow == m_AdaptWindows)
117 {
118#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
119 painter.fillRect(rect(), QBrush(palette().color(QPalette::Window)));
120#else
121 painter.fillRect(rect(), QBrush(palette().color(QPalette::Background)));
122#endif
123 } //*/
124
125 // 设置平滑模式
126 painter.setRenderHint(QPainter::SmoothPixmapTransform);
127 painter.drawImage(dstRect, m_Desktop);
128
129}
130
131void CFrmViewer::paintEvent(QPaintEvent *event)
132{
133 //qqDebug(log) << "CFrmViewer::paintEvent";
134 Q_UNUSED(event)
135
136 paintDesktop();
137}
138
139int CFrmViewer::TranslationMousePoint(QPointF inPos, QPointF &outPos)
140{
141 qDebug(logMouse) << "TranslationPoint x:" << inPos.x() << ";y:" << inPos.y();
142
143 switch (m_AdaptWindows) {
144 case ADAPT_WINDOWS::Auto:
147 outPos = inPos;
148 break;
150 outPos.setX(inPos.x() / GetZoomFactor());
151 outPos.setY(inPos.y() / GetZoomFactor());
152 break;
154 outPos.setX(m_DesktopSize.width() * inPos.x() / width());
155 outPos.setY(m_DesktopSize.height() * inPos.y() / height());
156 break;
158 {
159 QRectF r = GetAspectRationRect();
160 if(inPos.x() < r.left()
161 || inPos.x() > r.right()
162 || inPos.y() < r.top()
163 || inPos.y() > r.bottom())
164 return -1;
165 outPos.setX(m_DesktopSize.width() * (inPos.x() - r.left()) / r.width());
166 outPos.setY(m_DesktopSize.height() * (inPos.y() - r.top()) / r.height());
167 break;
168 }
169 default:
170 break;
171 }
172
173 return 0;
174}
175
176void CFrmViewer::mousePressEvent(QMouseEvent *event)
177{
178 QPointF pos =
179#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
180 event->position();
181#else
182 event->pos();
183#endif
184
185 if(TranslationMousePoint(pos, pos)) return;
186 // event->buttons() 产生事件时,按键的状态
187 // event->button() 触发当前事件的按键
188 qDebug(logMouse) << "CFrmViewer::mousePressEvent"
189 << event << event->button() << event->buttons() << pos;
190 emit sigMousePressEvent(event, QPoint(pos.x(), pos.y()));
191 event->accept();
192}
193
194void CFrmViewer::mouseReleaseEvent(QMouseEvent *event)
195{
196 QPointF pos =
197#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
198 event->position();
199#else
200 event->pos();
201#endif
202 if(TranslationMousePoint(pos, pos)) return;
203 // event->buttons() 产生事件时,按键的状态
204 // event->button() 触发当前事件的按键
205 qDebug(logMouse) << "CFrmViewer::mouseReleaseEvent"
206 << event << event->button() << event->buttons() << pos;
207 emit sigMouseReleaseEvent(event, QPoint(pos.x(), pos.y()));
208 event->accept();
209}
210
211void CFrmViewer::mouseMoveEvent(QMouseEvent *event)
212{
213 QPointF pos =
214#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
215 event->position();
216#else
217 event->pos();
218#endif
219 if(TranslationMousePoint(pos, pos)) return;
220 // event->buttons() 产生事件时,按键的状态
221 // event->button() 触发当前事件的按键
222 qDebug(logMouse) << "CFrmViewer::mouseMoveEvent"
223 << event->button() << event->buttons() << pos;
224 emit sigMouseMoveEvent(event, QPoint(pos.x(), pos.y()));
225 emit sigMouseMoveEvent(event);
226 event->accept();
227}
228
229void CFrmViewer::wheelEvent(QWheelEvent *event)
230{
231 QPointF pos =
232#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
233 event->position();
234#else
235 event->pos();
236#endif
237 if(TranslationMousePoint(pos, pos)) return;
238 qDebug(logMouse) << "CFrmViewer::wheelEvent"
239 << event->buttons() << event->angleDelta() << pos;
240 emit sigWheelEvent(event, QPoint(pos.x(), pos.y()));
241 event->accept();
242}
243
244void CFrmViewer::keyPressEvent(QKeyEvent *event)
245{
246 qDebug(logKey) << "CFrmViewer::keyPressEvent" << event;
247 emit sigKeyPressEvent(event);
248 event->accept();
249}
250
251void CFrmViewer::keyReleaseEvent(QKeyEvent *event)
252{
253 qDebug(logKey) << "CFrmViewer::keyReleaseEvent" << event;
254 emit sigKeyReleaseEvent(event);
255 event->accept();
256}
257
258QSize CFrmViewer::GetDesktopSize()
259{
260 return m_DesktopSize;
261}
262
264{
265 return m_dbZoomFactor;
266}
267
268int CFrmViewer::slotSetZoomFactor(double newZoomFactor)
269{
270 if(newZoomFactor < 0) return -1;
271 if (qFuzzyCompare(m_dbZoomFactor, newZoomFactor))
272 return 0;
273 m_dbZoomFactor = newZoomFactor;
274 return 0;
275}
276
277int CFrmViewer::ReSize(int width, int height)
278{
279 int w = width * GetZoomFactor();
280 int h = height * GetZoomFactor();
281 resize(w, h);
282 return 0;
283}
284
285void CFrmViewer::slotSetAdaptWindows(ADAPT_WINDOWS aw)
286{
287 m_AdaptWindows = aw;
288 if(!m_Desktop.isNull())
289 {
290 switch (m_AdaptWindows) {
293 slotSetZoomFactor(1);
295 ReSize(m_DesktopSize.width(), m_DesktopSize.height());
296 break;
297 default:
298 break;
299 }
300 }
301 update();
302 //setFocus();
303}
304
305CFrmViewer::ADAPT_WINDOWS CFrmViewer::GetAdaptWindows()
306{
307 return m_AdaptWindows;
308}
309
310void CFrmViewer::slotSetDesktopSize(int width, int height)
311{
312 m_DesktopSize = QSize(width, height);
313 m_Desktop = QImage(width, height, QImage::Format_RGB32);
314
315 if(ADAPT_WINDOWS::Original == m_AdaptWindows
316 || ADAPT_WINDOWS::OriginalCenter == m_AdaptWindows
317 || ADAPT_WINDOWS::Zoom == m_AdaptWindows)
318 ReSize(width, height);
319
320 return;
321}
322
324{
325 setEnabled(true);
326}
327
328void CFrmViewer::slotSetName(const QString& szName)
329{
330 this->setWindowTitle(szName);
331 emit sigServerName(szName);
332}
333
334void CFrmViewer::slotUpdateRect(const QImage& image)
335{
336 //qDebug(log) << "void CFrmViewer::slotUpdateRect(const QImage& image)" << image;
337 m_Desktop = image;
338
339 if(m_bRecordVideo)
340 emit sigRecordVideo(m_Desktop);
341
342 update();
343 return;
344}
345
346void CFrmViewer::slotUpdateRect(const QRect& r, const QImage& image)
347{
348#if DEBUG
349 if(r.width() != image.rect().width() || r.height() != image.rect().height())
350 {
351 qWarning(log) << "Image is error";
352 }
353#endif
354 //qDebug(log) << "void CFrmViewer::slotUpdateRect(const QRect& r, const QImage& image)" << r << image;
355 if(m_Desktop.isNull() || m_Desktop.rect() == r)
356 {
357 m_Desktop = image;
358 //qDebug(log) << "Update image size is same old image size";
359 } else {
360 QPainter painter(&m_Desktop);
361 painter.drawImage(r, image);
362 //qDebug(log) << "Update image size isn't same old image size" << r << image.rect() << image;
363 }
364
365 if(m_bRecordVideo)
366 emit sigRecordVideo(m_Desktop);
367
368 update();
369}
370
371void CFrmViewer::slotUpdateCursor(const QCursor& cursor)
372{
373 setCursor(cursor);
374}
375
377{
378 cursor().setPos(pos);
379}
380
381#if ! (defined(Q_OS_WIN) || defined(Q_OS_APPLE) || defined(Q_OS_ANDROID) || defined(Q_OS_APPLE) || defined(Q_OS_MACOS))
382unsigned int getModifierMask(unsigned int keysym)
383{
384 XkbDescPtr xkb;
385 unsigned int mask, keycode;
386 XkbAction *act;
387
388 mask = 0;
389 Display *dpy = XOpenDisplay(0);
390 xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
391 if (xkb == nullptr)
392 return 0;
393
394 for (keycode = xkb->min_key_code; keycode <= xkb->max_key_code; keycode++) {
395 unsigned int state_out;
396 KeySym ks;
397
398 XkbTranslateKeyCode(xkb, keycode, 0, &state_out, &ks);
399 if (ks == NoSymbol)
400 continue;
401
402 if (ks == keysym)
403 break;
404 }
405
406 // KeySym not mapped?
407 if (keycode > xkb->max_key_code)
408 goto out;
409
410 act = XkbKeyAction(xkb, keycode, 0);
411 if (act == nullptr)
412 goto out;
413 if (act->type != XkbSA_LockMods)
414 goto out;
415
416 if (act->mods.flags & XkbSA_UseModMapMods)
417 mask = xkb->map->modmap[keycode];
418 else
419 mask = act->mods.mask;
420
421out:
422 XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
423
424 return mask;
425}
426#endif
427
428void CFrmViewer::slotUpdateLedState(unsigned int state)
429{
430 qDebug(log, "Got server LED state: 0x%08x", state);
431
432 if (!hasFocus())
433 return;
434
435 //TODO: test led
436
437#if defined(WIN32)
438 INPUT input[6];
439 UINT count;
440 UINT ret;
441
442 memset(input, 0, sizeof(input));
443 count = 0;
444
445 if (!!(state & CapsLock) != !!(GetKeyState(VK_CAPITAL) & 0x1)) {
446 input[count].type = input[count+1].type = INPUT_KEYBOARD;
447 input[count].ki.wVk = input[count+1].ki.wVk = VK_CAPITAL;
448 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
449 input[count].ki.dwFlags = 0;
450 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
451 count += 2;
452 }
453
454 if (!!(state & NumLock) != !!(GetKeyState(VK_NUMLOCK) & 0x1)) {
455 input[count].type = input[count+1].type = INPUT_KEYBOARD;
456 input[count].ki.wVk = input[count+1].ki.wVk = VK_NUMLOCK;
457 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
458 input[count].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
459 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
460 count += 2;
461 }
462
463 if (!!(state & ScrollLock) != !!(GetKeyState(VK_SCROLL) & 0x1)) {
464 input[count].type = input[count+1].type = INPUT_KEYBOARD;
465 input[count].ki.wVk = input[count+1].ki.wVk = VK_SCROLL;
466 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
467 input[count].ki.dwFlags = 0;
468 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
469 count += 2;
470 }
471
472 if (count == 0)
473 return;
474
475 ret = SendInput(count, input, sizeof(*input));
476 if (ret < count)
477 qCritical(log) << "Failed to update keyboard LED state:" << GetLastError();
478#elif defined(Q_OS_APPLE)
479
480 // No support for Scroll Lock //
481 ;
482
483#elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_APPLE)
484 unsigned int affect, values;
485 unsigned int mask;
486
487 Bool ret;
488
489 affect = values = 0;
490
491 affect |= LockMask;
492 if (state & CapsLock)
493 values |= LockMask;
494
495 mask = getModifierMask(XK_Num_Lock);
496 affect |= mask;
497 if (state & NumLock)
498 values |= mask;
499
500 mask = getModifierMask(XK_Scroll_Lock);
501 affect |= mask;
502 if (state & ScrollLock)
503 values |= mask;
504 Display *dpy = XOpenDisplay(0);
505 ret = XkbLockModifiers(dpy, XkbUseCoreKbd, affect, values);
506 if (!ret)
507 qCritical(log) << tr("Failed to update keyboard LED state");
508 XFlush(dpy);
509 XCloseDisplay(dpy);
510#endif
511}
512
513QImage CFrmViewer::GrabImage(int x, int y, int w, int h)
514{
515 int width = w, height = h;
516 if(-1 == w)
517 width = m_DesktopSize.width();
518 if(-1 == w)
519 height = m_DesktopSize.height();
520 return m_Desktop.copy(x, y, width, height);
521}
522
523void CFrmViewer::slotRecordVideo(bool bRecord, qreal nRate)
524{
525 m_bRecordVideo = bRecord;
526 if(0 == nRate) {
527 return;
528 }
529
530 m_bRecordVideo = false;
531 if(bRecord)
532 m_TimerRecordVideo.start(1000 / nRate);
533 else
534 m_TimerRecordVideo.stop();
535}
536
537void CFrmViewer::slotRecordVideo()
538{
539 emit sigRecordVideo(m_Desktop);
540}
541
542void CFrmViewer::focusInEvent(QFocusEvent *event)
543{
544 qDebug(log) << Q_FUNC_INFO << event << this;
545 emit sigViewerFocusIn(this);
546}
547
548void CFrmViewer::focusOutEvent(QFocusEvent *event)
549{
550 qDebug(log) << Q_FUNC_INFO << event << this;
551}
A widget which displays output image from a CConnectDesktop and sends input keypresses and mouse acti...
Definition FrmViewer.h:48
void slotSetDesktopSize(int width, int height)
Update desktop size.
ADAPT_WINDOWS
The ADAPT_WINDOWS enum.
Definition FrmViewer.h:60
@ Original
Original desktop size, the left-top of the desktop is aligned with the left-top of the window.
@ Zoom
zoom windows = desktop size * factor
@ KeepAspectRationToWindow
Keep desktop aspectration adapt to windows.
@ OriginalCenter
Original desktop size, the center of the desktop is aligned with the center of the window.
@ ZoomToWindow
Desktop adapt to windows.
@ Disable
Disable adapt windows.
void sigViewerFocusIn(QWidget *pView)
The view is focus.
void slotUpdateCursor(const QCursor &cursor)
Update cursor.
void slotUpdateRect(const QRect &r, const QImage &image)
Update image.
void slotConnected()
Enable mouse and keyboard.
void slotSetName(const QString &szName)
Update desktop name.
void slotUpdateCursorPosition(const QPoint &pos)
Update cursor position.
double GetZoomFactor() const
Adjust the zoom factor.