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