Rabbit Remote Control 0.0.35
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
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
254QSize CFrmViewer::GetDesktopSize()
255{
256 return m_DesktopSize;
257}
258
260{
261 return m_dbZoomFactor;
262}
263
264int CFrmViewer::slotSetZoomFactor(double newZoomFactor)
265{
266 if(newZoomFactor < 0) return -1;
267 if (qFuzzyCompare(m_dbZoomFactor, newZoomFactor))
268 return 0;
269 m_dbZoomFactor = newZoomFactor;
270 return 0;
271}
272
273int CFrmViewer::ReSize(int width, int height)
274{
275 int w = width * GetZoomFactor();
276 int h = height * GetZoomFactor();
277 resize(w, h);
278 return 0;
279}
280
281void CFrmViewer::slotSetAdaptWindows(ADAPT_WINDOWS aw)
282{
283 m_AdaptWindows = aw;
284 if(!m_Desktop.isNull())
285 {
286 switch (m_AdaptWindows) {
289 slotSetZoomFactor(1);
291 ReSize(m_DesktopSize.width(), m_DesktopSize.height());
292 break;
293 default:
294 break;
295 }
296 }
297 update();
298 //setFocus();
299}
300
301CFrmViewer::ADAPT_WINDOWS CFrmViewer::GetAdaptWindows()
302{
303 return m_AdaptWindows;
304}
305
306void CFrmViewer::slotSetDesktopSize(int width, int height)
307{
308 m_DesktopSize = QSize(width, height);
309 m_Desktop = QImage(width, height, QImage::Format_RGB32);
310
311 if(ADAPT_WINDOWS::Original == m_AdaptWindows
312 || ADAPT_WINDOWS::OriginalCenter == m_AdaptWindows
313 || ADAPT_WINDOWS::Zoom == m_AdaptWindows)
314 ReSize(width, height);
315
316 return;
317}
318
319void CFrmViewer::slotConnected()
320{
321 setEnabled(true);
322}
323
324void CFrmViewer::slotSetName(const QString& szName)
325{
326 this->setWindowTitle(szName);
327 emit sigServerName(szName);
328}
329
330void CFrmViewer::slotUpdateRect(const QImage& image)
331{
332 //qDebug(log) << "void CFrmViewer::slotUpdateRect(const QImage& image)" << image;
333 m_Desktop = image;
334
335 if(m_bRecordVideo)
336 emit sigRecordVideo(m_Desktop);
337
338 update();
339 return;
340}
341
342void CFrmViewer::slotUpdateRect(const QRect& r, const QImage& image)
343{
344#if DEBUG
345 if(r.width() != image.rect().width() || r.height() != image.rect().height())
346 {
347 qWarning(log) << "Image is error";
348 }
349#endif
350 //qDebug(log) << "void CFrmViewer::slotUpdateRect(const QRect& r, const QImage& image)" << r << image;
351 if(m_Desktop.isNull() || m_Desktop.rect() == r)
352 {
353 m_Desktop = image;
354 //qDebug(log) << "Update image size is same old image size";
355 }
356 else
357 {
358 QPainter painter(&m_Desktop);
359 painter.drawImage(r, image);
360 //qDebug(log) << "Update image size isn't same old image size" << r << image.rect() << image;
361 }
362
363 if(m_bRecordVideo)
364 emit sigRecordVideo(m_Desktop);
365
366 update();
367}
368
369void CFrmViewer::slotUpdateCursor(const QCursor& cursor)
370{
371 setCursor(cursor);
372}
373
375{
376 cursor().setPos(pos);
377}
378
379#if ! (defined(Q_OS_WIN) || defined(Q_OS_APPLE) || defined(Q_OS_ANDROID) || defined(Q_OS_APPLE) || defined(Q_OS_MACOS))
380unsigned int getModifierMask(unsigned int keysym)
381{
382 XkbDescPtr xkb;
383 unsigned int mask, keycode;
384 XkbAction *act;
385
386 mask = 0;
387 Display *dpy = XOpenDisplay(0);
388 xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
389 if (xkb == nullptr)
390 return 0;
391
392 for (keycode = xkb->min_key_code; keycode <= xkb->max_key_code; keycode++) {
393 unsigned int state_out;
394 KeySym ks;
395
396 XkbTranslateKeyCode(xkb, keycode, 0, &state_out, &ks);
397 if (ks == NoSymbol)
398 continue;
399
400 if (ks == keysym)
401 break;
402 }
403
404 // KeySym not mapped?
405 if (keycode > xkb->max_key_code)
406 goto out;
407
408 act = XkbKeyAction(xkb, keycode, 0);
409 if (act == nullptr)
410 goto out;
411 if (act->type != XkbSA_LockMods)
412 goto out;
413
414 if (act->mods.flags & XkbSA_UseModMapMods)
415 mask = xkb->map->modmap[keycode];
416 else
417 mask = act->mods.mask;
418
419out:
420 XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
421
422 return mask;
423}
424#endif
425
426void CFrmViewer::slotUpdateLedState(unsigned int state)
427{
428 qDebug(log, "Got server LED state: 0x%08x", state);
429
430 if (!hasFocus())
431 return;
432
433 //TODO: test led
434
435#if defined(WIN32)
436 INPUT input[6];
437 UINT count;
438 UINT ret;
439
440 memset(input, 0, sizeof(input));
441 count = 0;
442
443 if (!!(state & CapsLock) != !!(GetKeyState(VK_CAPITAL) & 0x1)) {
444 input[count].type = input[count+1].type = INPUT_KEYBOARD;
445 input[count].ki.wVk = input[count+1].ki.wVk = VK_CAPITAL;
446 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
447 input[count].ki.dwFlags = 0;
448 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
449 count += 2;
450 }
451
452 if (!!(state & NumLock) != !!(GetKeyState(VK_NUMLOCK) & 0x1)) {
453 input[count].type = input[count+1].type = INPUT_KEYBOARD;
454 input[count].ki.wVk = input[count+1].ki.wVk = VK_NUMLOCK;
455 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
456 input[count].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
457 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
458 count += 2;
459 }
460
461 if (!!(state & ScrollLock) != !!(GetKeyState(VK_SCROLL) & 0x1)) {
462 input[count].type = input[count+1].type = INPUT_KEYBOARD;
463 input[count].ki.wVk = input[count+1].ki.wVk = VK_SCROLL;
464 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
465 input[count].ki.dwFlags = 0;
466 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
467 count += 2;
468 }
469
470 if (count == 0)
471 return;
472
473 ret = SendInput(count, input, sizeof(*input));
474 if (ret < count)
475 qCritical(log) << "Failed to update keyboard LED state:" << GetLastError();
476#elif defined(Q_OS_APPLE)
477
478 // No support for Scroll Lock //
479 ;
480
481#elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_APPLE)
482 unsigned int affect, values;
483 unsigned int mask;
484
485 Bool ret;
486
487 affect = values = 0;
488
489 affect |= LockMask;
490 if (state & CapsLock)
491 values |= LockMask;
492
493 mask = getModifierMask(XK_Num_Lock);
494 affect |= mask;
495 if (state & NumLock)
496 values |= mask;
497
498 mask = getModifierMask(XK_Scroll_Lock);
499 affect |= mask;
500 if (state & ScrollLock)
501 values |= mask;
502 Display *dpy = XOpenDisplay(0);
503 ret = XkbLockModifiers(dpy, XkbUseCoreKbd, affect, values);
504 if (!ret)
505 qCritical(log) << tr("Failed to update keyboard LED state");
506 XFlush(dpy);
507 XCloseDisplay(dpy);
508#endif
509}
510
511QImage CFrmViewer::GrabImage(int x, int y, int w, int h)
512{
513 int width = w, height = h;
514 if(-1 == w)
515 width = m_DesktopSize.width();
516 if(-1 == w)
517 height = m_DesktopSize.height();
518 return m_Desktop.copy(x, y, width, height);
519}
520
521void CFrmViewer::slotRecordVideo(bool bRecord)
522{
523 m_bRecordVideo = bRecord;
524}
525
526void CFrmViewer::focusInEvent(QFocusEvent *event)
527{
528 qDebug(log) << Q_FUNC_INFO << event << this;
529 emit sigViewerFocusIn(this);
530}
531
532void CFrmViewer::focusOutEvent(QFocusEvent *event)
533{
534 qDebug(log) << Q_FUNC_INFO << event << this;
535}
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 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 slotSetName(const QString &szName)
Update desktop name.
void slotUpdateCursorPosition(const QPoint &pos)
Update cursor position.
double GetZoomFactor() const
Adjust the zoom factor.