Rabbit Remote Control 0.0.37
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")
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 connecter is not connected, don't accept keyboard and mouse event
47 // When the CConnecter::sigConnected() set true. accept keyboard and mouse event
48 // \see CConnecter::sigConnected()
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::TranslationMousePoint(QPointF inPos, QPointF &outPos)
142{
143 qDebug(logMouse) << "TranslationPoint 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 break;
173 }
174
175 return 0;
176}
177
178void CFrmViewer::mousePressEvent(QMouseEvent *event)
179{
180 QPointF pos =
181#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
182 event->position();
183#else
184 event->pos();
185#endif
186
187 if(TranslationMousePoint(pos, pos)) return;
188 // event->buttons() 产生事件时,按键的状态
189 // event->button() 触发当前事件的按键
190 qDebug(logMouse) << "CFrmViewer::mousePressEvent"
191 << event << event->button() << event->buttons() << pos;
192 emit sigMousePressEvent(event, QPoint(pos.x(), pos.y()));
193 event->accept();
194
195 if(testAttribute(Qt::WA_InputMethodEnabled)
196 && event->button() == Qt::LeftButton) {
197 qDebug(logInputMethod) << Q_FUNC_INFO << "Update micro focus";
198 updateMicroFocus();
199 }
200}
201
202void CFrmViewer::mouseReleaseEvent(QMouseEvent *event)
203{
204 QPointF pos =
205#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
206 event->position();
207#else
208 event->pos();
209#endif
210 if(TranslationMousePoint(pos, pos)) return;
211 // event->buttons() 产生事件时,按键的状态
212 // event->button() 触发当前事件的按键
213 qDebug(logMouse) << "CFrmViewer::mouseReleaseEvent"
214 << event << event->button() << event->buttons() << pos;
215 emit sigMouseReleaseEvent(event, QPoint(pos.x(), pos.y()));
216 event->accept();
217}
218
219void CFrmViewer::mouseMoveEvent(QMouseEvent *event)
220{
221 QPointF pos =
222#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
223 event->position();
224#else
225 event->pos();
226#endif
227 if(TranslationMousePoint(pos, pos)) return;
228 // event->buttons() 产生事件时,按键的状态
229 // event->button() 触发当前事件的按键
230 qDebug(logMouse) << "CFrmViewer::mouseMoveEvent"
231 << event->button() << event->buttons() << pos;
232 emit sigMouseMoveEvent(event, QPoint(pos.x(), pos.y()));
233 emit sigMouseMoveEvent(event);
234 event->accept();
235}
236
237void CFrmViewer::wheelEvent(QWheelEvent *event)
238{
239 QPointF pos =
240#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
241 event->position();
242#else
243 event->pos();
244#endif
245 if(TranslationMousePoint(pos, pos)) return;
246 qDebug(logMouse) << "CFrmViewer::wheelEvent"
247 << event->buttons() << event->angleDelta() << pos;
248 emit sigWheelEvent(event, QPoint(pos.x(), pos.y()));
249 event->accept();
250}
251
252void CFrmViewer::keyPressEvent(QKeyEvent *event)
253{
254 qDebug(logKey) << "CFrmViewer::keyPressEvent" << event;
255 emit sigKeyPressEvent(event);
256 event->accept();
257}
258
259void CFrmViewer::keyReleaseEvent(QKeyEvent *event)
260{
261 qDebug(logKey) << "CFrmViewer::keyReleaseEvent" << event;
262 emit sigKeyReleaseEvent(event);
263 event->accept();
264}
265
266void CFrmViewer::inputMethodEvent(QInputMethodEvent *event)
267{
268 qDebug(logInputMethod) << Q_FUNC_INFO << event;
269 emit sigInputMethodEvent(event);
270 event->accept();
271 /*
272 if(event->commitString().isEmpty()) return;
273 QPoint p(cursor().pos().x() + fontMetrics().horizontalAdvance(event->commitString()), cursor().pos().y());
274 qDebug(logKey) << cursor().pos() << fontMetrics().horizontalAdvance(event->commitString());
275 slotUpdateCursorPosition(p);//*/
276}
277
278QVariant CFrmViewer::inputMethodQuery(Qt::InputMethodQuery query) const
279{
280 qDebug(logInputMethod) << Q_FUNC_INFO << query;
281 switch ( query )
282 {
283 case Qt::ImCursorRectangle: {
284 QPoint p = mapFromGlobal(cursor().pos());
285 QRect r(p.x(), p.y() , 1, fontMetrics().height());
286 return r;
287 }
288 default:
289 break;
290 }
291 QVariant r = QWidget::inputMethodQuery(query);
292 qDebug(logKey) << Q_FUNC_INFO << query << r;
293 return r;
294}
295
296QSize CFrmViewer::GetDesktopSize()
297{
298 return m_DesktopSize;
299}
300
302{
303 return m_dbZoomFactor;
304}
305
306int CFrmViewer::slotSetZoomFactor(double newZoomFactor)
307{
308 if(newZoomFactor < 0) return -1;
309 if (qFuzzyCompare(m_dbZoomFactor, newZoomFactor))
310 return 0;
311 m_dbZoomFactor = newZoomFactor;
312 return 0;
313}
314
315int CFrmViewer::ReSize(int width, int height)
316{
317 int w = width * GetZoomFactor();
318 int h = height * GetZoomFactor();
319 resize(w, h);
320 return 0;
321}
322
323void CFrmViewer::slotSetAdaptWindows(ADAPT_WINDOWS aw)
324{
325 m_AdaptWindows = aw;
326 if(!m_Desktop.isNull())
327 {
328 switch (m_AdaptWindows) {
331 slotSetZoomFactor(1);
333 ReSize(m_DesktopSize.width(), m_DesktopSize.height());
334 break;
335 default:
336 break;
337 }
338 }
339 update();
340 //setFocus();
341}
342
343CFrmViewer::ADAPT_WINDOWS CFrmViewer::GetAdaptWindows()
344{
345 return m_AdaptWindows;
346}
347
348void CFrmViewer::slotSetDesktopSize(int width, int height)
349{
350 m_DesktopSize = QSize(width, height);
351 m_Desktop = QImage(width, height, QImage::Format_RGB32);
352
353 if(ADAPT_WINDOWS::Original == m_AdaptWindows
354 || ADAPT_WINDOWS::OriginalCenter == m_AdaptWindows
355 || ADAPT_WINDOWS::Zoom == m_AdaptWindows)
356 ReSize(width, height);
357
358 return;
359}
360
362{
363 setEnabled(true);
364}
365
367{
368 qDebug(logInputMethod) << Q_FUNC_INFO << bEnable;
369 setAttribute(Qt::WA_InputMethodEnabled, bEnable);
370}
371
372void CFrmViewer::slotSetName(const QString& szName)
373{
374 this->setWindowTitle(szName);
375 emit sigServerName(szName);
376}
377
378void CFrmViewer::slotUpdateRect(const QImage& image)
379{
380 //qDebug(log) << "void CFrmViewer::slotUpdateRect(const QImage& image)" << image;
381 m_Desktop = image;
382
383 if(m_bRecordVideo)
384 emit sigRecordVideo(m_Desktop);
385
386 update();
387 return;
388}
389
390void CFrmViewer::slotUpdateRect(const QRect& r, const QImage& image)
391{
392#if DEBUG
393 if(r.width() != image.rect().width() || r.height() != image.rect().height())
394 {
395 qWarning(log) << "Image is error";
396 }
397#endif
398 //qDebug(log) << "void CFrmViewer::slotUpdateRect(const QRect& r, const QImage& image)" << r << image;
399 if(m_Desktop.isNull() || m_Desktop.rect() == r)
400 {
401 m_Desktop = image;
402 //qDebug(log) << "Update image size is same old image size";
403 } else {
404 QPainter painter(&m_Desktop);
405 painter.drawImage(r, image);
406 //qDebug(log) << "Update image size isn't same old image size" << r << image.rect() << image;
407 }
408
409 if(m_bRecordVideo)
410 emit sigRecordVideo(m_Desktop);
411
412 update();
413}
414
415void CFrmViewer::slotUpdateCursor(const QCursor& cursor)
416{
417 //qDebug(log) << Q_FUNC_INFO << cursor;
418 setCursor(cursor);
419 if(testAttribute(Qt::WA_InputMethodEnabled)) {
420 qDebug(logInputMethod) << Q_FUNC_INFO << "Update micro focus";
421 updateMicroFocus();
422 }
423}
424
426{
427 //qDebug(log) << Q_FUNC_INFO << pos;
428 cursor().setPos(pos);
429 if(testAttribute(Qt::WA_InputMethodEnabled)) {
430 qDebug(logInputMethod) << Q_FUNC_INFO << "Update micro focus";
431 updateMicroFocus();
432 }
433}
434
435#if ! (defined(Q_OS_WIN) || defined(Q_OS_APPLE) || defined(Q_OS_ANDROID) || defined(Q_OS_APPLE) || defined(Q_OS_MACOS))
436unsigned int getModifierMask(unsigned int keysym)
437{
438 XkbDescPtr xkb;
439 unsigned int mask, keycode;
440 XkbAction *act;
441
442 mask = 0;
443 Display *dpy = XOpenDisplay(0);
444 xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
445 if (xkb == nullptr)
446 return 0;
447
448 for (keycode = xkb->min_key_code; keycode <= xkb->max_key_code; keycode++) {
449 unsigned int state_out;
450 KeySym ks;
451
452 XkbTranslateKeyCode(xkb, keycode, 0, &state_out, &ks);
453 if (ks == NoSymbol)
454 continue;
455
456 if (ks == keysym)
457 break;
458 }
459
460 // KeySym not mapped?
461 if (keycode > xkb->max_key_code)
462 goto out;
463
464 act = XkbKeyAction(xkb, keycode, 0);
465 if (act == nullptr)
466 goto out;
467 if (act->type != XkbSA_LockMods)
468 goto out;
469
470 if (act->mods.flags & XkbSA_UseModMapMods)
471 mask = xkb->map->modmap[keycode];
472 else
473 mask = act->mods.mask;
474
475out:
476 XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
477
478 return mask;
479}
480#endif
481
482void CFrmViewer::slotUpdateLedState(unsigned int state)
483{
484 qDebug(log, "Got server LED state: 0x%08x", state);
485
486 if (!hasFocus())
487 return;
488
489 //TODO: test led
490
491#if defined(WIN32)
492 INPUT input[6];
493 UINT count;
494 UINT ret;
495
496 memset(input, 0, sizeof(input));
497 count = 0;
498
499 if (!!(state & CapsLock) != !!(GetKeyState(VK_CAPITAL) & 0x1)) {
500 input[count].type = input[count+1].type = INPUT_KEYBOARD;
501 input[count].ki.wVk = input[count+1].ki.wVk = VK_CAPITAL;
502 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
503 input[count].ki.dwFlags = 0;
504 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
505 count += 2;
506 }
507
508 if (!!(state & NumLock) != !!(GetKeyState(VK_NUMLOCK) & 0x1)) {
509 input[count].type = input[count+1].type = INPUT_KEYBOARD;
510 input[count].ki.wVk = input[count+1].ki.wVk = VK_NUMLOCK;
511 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
512 input[count].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
513 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
514 count += 2;
515 }
516
517 if (!!(state & ScrollLock) != !!(GetKeyState(VK_SCROLL) & 0x1)) {
518 input[count].type = input[count+1].type = INPUT_KEYBOARD;
519 input[count].ki.wVk = input[count+1].ki.wVk = VK_SCROLL;
520 //input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
521 input[count].ki.dwFlags = 0;
522 input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
523 count += 2;
524 }
525
526 if (count == 0)
527 return;
528
529 ret = SendInput(count, input, sizeof(*input));
530 if (ret < count)
531 qCritical(log) << "Failed to update keyboard LED state:" << GetLastError();
532#elif defined(Q_OS_APPLE)
533
534 // No support for Scroll Lock //
535 ;
536
537#elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_APPLE)
538 unsigned int affect, values;
539 unsigned int mask;
540
541 Bool ret;
542
543 affect = values = 0;
544
545 affect |= LockMask;
546 if (state & CapsLock)
547 values |= LockMask;
548
549 mask = getModifierMask(XK_Num_Lock);
550 affect |= mask;
551 if (state & NumLock)
552 values |= mask;
553
554 mask = getModifierMask(XK_Scroll_Lock);
555 affect |= mask;
556 if (state & ScrollLock)
557 values |= mask;
558 Display *dpy = XOpenDisplay(0);
559 ret = XkbLockModifiers(dpy, XkbUseCoreKbd, affect, values);
560 if (!ret)
561 qCritical(log) << tr("Failed to update keyboard LED state");
562 XFlush(dpy);
563 XCloseDisplay(dpy);
564#endif
565}
566
567QImage CFrmViewer::GrabImage(int x, int y, int w, int h)
568{
569 int width = w, height = h;
570 if(-1 == w)
571 width = m_DesktopSize.width();
572 if(-1 == w)
573 height = m_DesktopSize.height();
574 return m_Desktop.copy(x, y, width, height);
575}
576
577void CFrmViewer::slotRecordVideo(bool bRecord, qreal nRate)
578{
579 m_bRecordVideo = bRecord;
580 if(0 == nRate) {
581 qWarning(log) << "The video frame rate is 0. it is not record static remote desktop."
582 << "If need record static remote desktop,"
583 << "please 'Record->Video->Encoding mode' don't select 'Constant quality' in settings dialog,"
584 << "then set 'Record->Video->Video frame'";
585 return;
586 }
587
588 m_bRecordVideo = false;
589 if(bRecord) {
590 qreal ms = 1000 / nRate;
591 //qDebug(log) << "Rate:" << nRate << ms << "ms";
592 m_TimerRecordVideo.start(ms);
593 }
594 else
595 m_TimerRecordVideo.stop();
596}
597
598void CFrmViewer::slotRecordVideo()
599{
600 emit sigRecordVideo(m_Desktop);
601}
602
603void CFrmViewer::focusInEvent(QFocusEvent *event)
604{
605 qDebug(log) << Q_FUNC_INFO << event << this;
606 emit sigViewerFocusIn(this);
607}
608
609void CFrmViewer::focusOutEvent(QFocusEvent *event)
610{
611 qDebug(log) << Q_FUNC_INFO << event << this;
612}
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 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
Adjust the zoom factor.