Rabbit Remote Control 0.1.0-bate16
Loading...
Searching...
No Matches
BackendVnc.cpp
1// Author: Kang Lin <kl222@126.com>
2
3#include "BackendVnc.h"
4#include "OperateDesktop.h"
5
6#ifndef WIN32
7#include <string.h>
8#include "network/UnixSocket.h"
9#endif
10
11#include <rfb/CMsgWriter.h>
12#include <rfb/Hostname.h>
13#include "rfb/LogWriter.h"
14#include <rfb/util.h>
15#include <rfb/clipboardTypes.h>
16#include <rfb/LogWriter.h>
17#include <rfb/Logger_stdio.h>
18#include <rfb/fenceTypes.h>
19
20#include "FramePixelBuffer.h"
21
22#include <QDir>
23#include <QMimeData>
24#include <QClipboard>
25#include <QApplication>
26#include <QLoggingCategory>
27#include <QKeyEvent>
28#include <QMouseEvent>
29#include <QNetworkProxy>
30#include <QMutex>
31#include <QInputDialog>
32
33#include "rfb/Security.h"
34#include "rfb/Exception.h"
35#ifdef HAVE_GNUTLS
36#include "rfb/CSecurityTLS.h"
37#endif
38
39#ifdef HAVE_ICE
40 #include "Ice.h"
41 #include "ChannelIce.h"
42#endif
43
44#ifdef HAVE_LIBSSH
45 #include "ChannelSSHTunnel.h"
46#endif
47
48#include "ConvertKeyCode.h"
49
50static Q_LOGGING_CATEGORY(log, "VNC.Backend")
51static Q_LOGGING_CATEGORY(logVNC, "VNC.Log")
52static Q_LOGGING_CATEGORY(logKey, "VNC.Backend.Key")
53static Q_LOGGING_CATEGORY(logInputMethod, "VNC.Backend.InputMethod")
54static Q_LOGGING_CATEGORY(logMouse, "VNC.Backend.Mouse")
55
56class VncLogger: public rfb::Logger
57{
58public:
59 VncLogger(const char *name) : rfb::Logger(name)
60 {}
61 void write(int level, const char *logname, const char *text) override
62 {
63 if (level >= rfb::LogWriter::LEVEL_DEBUG) {
64 qDebug(logVNC) << logname << text;
65 } else if (level >= rfb::LogWriter::LEVEL_INFO) {
66 qInfo(logVNC) << logname << text;
67 } else if (level >= rfb::LogWriter::LEVEL_STATUS) {
68 qWarning(logVNC) << logname << text;
69 } else {
70 qCritical(logVNC) << logname << text;
71 }
72 }
73};
74
75static VncLogger g_logger("Rabbit");
76
77// 8 colours (1 bit per component)
78static const rfb::PixelFormat verylowColourPF(8, 3,false, true, 1, 1, 1, 2, 1, 0);
79// 64 colours (2 bits per component)
80static const rfb::PixelFormat lowColourPF(8, 6, false, true, 3, 3, 3, 4, 2, 0);
81// 256 colours (2-3 bits per component)
82static const rfb::PixelFormat mediumColourPF(8, 8, false, true, 7, 7, 3, 5, 2, 0);
83
84// RGB (4 bits per component)
85static const rfb::PixelFormat fullColourPF(32, 24, false, true, 255, 255, 255, 16, 8, 0);
86
87// Time new bandwidth estimates are weighted against (in ms)
88static const unsigned bpsEstimateWindow = 1000;
89
90CBackendVnc::CBackendVnc(COperateDesktop *pOperate)
91 : CBackendDesktop(pOperate)
92 , m_pOperate(pOperate)
93 , m_pPara(nullptr)
94 , m_SecurityLevel(CSecurityLevel::Level::Risk)
95{
96 static bool initlog = false;
97 if(!initlog)
98 {
99 g_logger.registerLogger();
100 rfb::LogWriter::setLogParams("*:Rabbit:100");
101 /*rfb::initStdIOLoggers();
102 rfb::LogWriter::setLogParams("*:stderr:100");
103 QString szFile = RabbitCommon::CDir::Instance()->GetDirLog()
104 + QDir::separator()
105 + "TigerVnc.log";
106 rfb::initFileLogger(szFile.toStdString().c_str());
107 rfb::LogWriter::setLogParams("*:file:100");
108 */
109 initlog = true;
110 }
111 m_pPara = dynamic_cast<CParameterVnc*>(pOperate->GetParameter());
112 Q_ASSERT(m_pPara);
113
114 if(!m_pPara->GetLocalCursor())
115 {
116 emit sigUpdateCursor(QCursor(Qt::BlankCursor));
117 }
118}
119
120CBackendVnc::~CBackendVnc()
121{
122 qDebug(log) << "CConnectVnc::~CConnectVnc()";
123}
124
125int CBackendVnc::SetPara()
126{
127 QDir d(os::getvncconfigdir());
128 if(!d.exists())
129 d.mkpath(os::getvncconfigdir());
130 QDir dstat(os::getvncstatedir());
131 if(!dstat.exists())
132 dstat.mkpath(os::getvncstatedir());
133 QDir dvnc(os::getvncdatadir());
134 if(!dvnc.exists())
135 dvnc.mkpath(os::getvncdatadir());
136
137 setServerName(m_pPara->m_Net.GetHost().toStdString().c_str());
138 setShared(m_pPara->GetShared());
139 supportsLocalCursor = m_pPara->GetLocalCursor();
140 supportsCursorPosition = m_pPara->GetCursorPosition();
141 supportsDesktopResize = m_pPara->GetSupportsDesktopResize();
142 supportsLEDState = m_pPara->GetLedState();
143
144 std::list<uint32_t> sec;
145 switch(m_pPara->m_Net.m_User.GetUsedType()) {
146 case CParameterUser::TYPE::None:
147 sec.push_back(rfb::secTypeNone);
148 break;
149 case CParameterUser::TYPE::OnlyPassword:
150 sec.push_back(rfb::secTypeVncAuth);
151 break;
152 case CParameterUser::TYPE::UserPassword:
153 sec.push_back(rfb::secTypePlain);
154 break;
156 sec.push_back(rfb::secTypeTLSVnc);
157 break;
158 case CParameterUser::TYPE::OnlyPasswordX509:
159 sec.push_back(rfb::secTypeX509Vnc);
160 break;
161 case CParameterUser::TYPE::UserPasswordX509None:
162 sec.push_back(rfb::secTypeTLSPlain);
163 break;
164 case CParameterUser::TYPE::UserPasswordX509:
165 sec.push_back(rfb::secTypeX509Plain);
166 break;
167 //TODO: add authentication
168 }
169 //*
170 qDebug(log) << "secTypes:" << security.GetEnabledSecTypes() << security.ToString()
171 << "new:" << sec; // << "Type:" << m_pPara->m_Net.m_User.GetType();//*/
172 security.SetSecTypes(sec);
173
174 // Set Preferred Encoding
175 setPreferredEncoding(m_pPara->GetPreferredEncoding());
176 setCompressLevel(m_pPara->GetCompressLevel());
177 setQualityLevel(m_pPara->GetQualityLevel());
178
179 // Set server pixmap format
180 updatePixelFormat();
181 return 0;
182}
183
184/*
185 * \return
186 * \li OnStartReturnValue::Fail: error
187 * \li OnStartReturnValue::UseOnProcess: Use OnProcess (non-Qt event loop)
188 * \li OnStartReturnValue::NotUseOnProcess: Don't use OnProcess (qt event loop)
189 */
190CBackendVnc::OnStartReturnValue CBackendVnc::OnStart()
191{
192 qDebug(log) << "CConnectVnc::OnStart()";
193 int nRet = 0;
194
195 if(m_pPara->GetIce())
196 nRet = IceInit();
197 else {
198#ifdef HAVE_LIBSSH
199 if(m_pPara->m_Proxy.GetUsedType() == CParameterProxy::TYPE::SSHTunnel) {
200 nRet = SSHInit();
201 if(nRet) return OnStartReturnValue::Fail; // error
202 return OnStartReturnValue::UseOnProcess;
203 }
204#endif
205 nRet = SocketInit();
206 }
207 if(nRet) return OnStartReturnValue::Fail; // error
208 // Don't use OnProcess (qt event loop)
209 return OnStartReturnValue::NotUseOnProcess;
210}
211
212int CBackendVnc::IceInit()
213{
214#ifdef HAVE_ICE
215 CIceSignal* pSignal = CICE::Instance()->GetSignal().data();
216 if(!pSignal || !pSignal->IsConnected())
217 {
218 qCritical(log) << "The g_pSignal is null. or signal don't connect server";
219 return -1;
220 }
221
222 qInfo(log, "Connected to signal server:%s:%d; user:%s",
223 CICE::Instance()->GetParameter()->getSignalServer().toStdString().c_str(),
224 CICE::Instance()->GetParameter()->getSignalPort(),
225 CICE::Instance()->GetParameter()->getSignalUser().toStdString().c_str());
226 auto channel = QSharedPointer<CChannelIce>(new CChannelIce(pSignal));
227 if(!channel)
228 {
229 qCritical(log) << "new CDataChannelIce fail";
230 return -2;
231 }
232 m_DataChannel = channel;
233 SetChannelConnect(channel);
234
235 rtc::Configuration config;
236 rtc::IceServer stun(CICE::Instance()->GetParameter()->getStunServer().toStdString().c_str(),
237 CICE::Instance()->GetParameter()->getStunPort());
238 rtc::IceServer turn(CICE::Instance()->GetParameter()->getTurnServer().toStdString().c_str(),
239 CICE::Instance()->GetParameter()->getTurnPort(),
240 CICE::Instance()->GetParameter()->getTurnUser().toStdString().c_str(),
241 CICE::Instance()->GetParameter()->getTurnPassword().toStdString().c_str());
242 config.iceServers.push_back(stun);
243 config.iceServers.push_back(turn);
244 channel->SetConfigure(config);
245
246 bool bRet = channel->open(CICE::Instance()->GetParameter()->getSignalUser(),
247 m_pPara->GetPeerUser(), true);
248 if(!bRet)
249 {
250 emit sigError(-1, "Open channel fail");
251 }
252
253#endif
254 return 0;
255}
256
257int CBackendVnc::SocketInit()
258{
259 int nRet = 0;
260 try{
261 QTcpSocket* pSock = new QTcpSocket(this);
262 if(!pSock)
263 return -1;
264 m_DataChannel = QSharedPointer<CChannel>(new CChannel(pSock));
265 if(!m_DataChannel) {
266 qCritical(log) << "New CChannel fail";
267 return -1;
268 }
269
270 SetChannelConnect(m_DataChannel);
271
272 if(!m_DataChannel->open(QIODevice::ReadWrite))
273 {
274 qCritical(log) << "Open channel fail";
275 return -1;
276 }
277
278 QNetworkProxy::ProxyType type = QNetworkProxy::NoProxy;
279 // Set sock
280 switch(m_pPara->m_Proxy.GetUsedType())
281 {
282 case CParameterProxy::TYPE::SockesV5:
283 type = QNetworkProxy::Socks5Proxy;
284 break;
285 case CParameterProxy::TYPE::System:
286 type = QNetworkProxy::DefaultProxy;
287 break;
288 case CParameterProxy::TYPE::Http:
289 type = QNetworkProxy::HttpProxy;
290 break;
291 case CParameterProxy::TYPE::None:
292 default:
293 break;
294 }
295
296 switch(type) {
297 case QNetworkProxy::DefaultProxy:
298 {
299 pSock->setProxy(QNetworkProxy::applicationProxy());
300 break;
301 }
302 case QNetworkProxy::HttpProxy:
303 case QNetworkProxy::Socks5Proxy:
304 {
305 QNetworkProxy proxy;
306 auto net = &m_pPara->m_Proxy.m_SockesV5;
307 if(QNetworkProxy::HttpProxy == type) {
308 net = &m_pPara->m_Proxy.m_Http;
309 }
310 if(net->GetHost().isEmpty())
311 {
312 QString szErr;
313 szErr = tr("The proxy server is empty, please input it");
314 qCritical(log) << szErr;
315 emit sigShowMessageBox(tr("Error"), szErr, QMessageBox::Critical);
316 return -1;
317 }
318 proxy.setHostName(net->GetHost());
319 proxy.setPort(net->GetPort());
320 auto &user = net->m_User;
321 if(((user.GetUsedType() == CParameterUser::TYPE::UserPassword)
322 && (user.GetPassword().isEmpty() || user.GetUser().isEmpty()))
323 || ((user.GetUsedType() == CParameterUser::TYPE::PublicKey)
324 && user.GetPassphrase().isEmpty())) {
325 int nRet = QDialog::Rejected;
326 emit sigBlockShowWidget("CDlgUserPassword", nRet, net);
327 if(QDialog::Accepted != nRet)
328 {
329 return -1;
330 }
331 }
332 proxy.setUser(user.GetUser());
333 proxy.setPassword(user.GetPassword());
334 proxy.setType(type);
335 pSock->setProxy(proxy);
336 break;
337 }
338 default:
339 break;
340 }
341
342 if(m_pPara->m_Net.GetHost().isEmpty())
343 {
344 QString szErr;
345 szErr = tr("The server is empty, please input it");
346 qCritical(log) << szErr;
347 emit sigShowMessageBox(tr("Error"), szErr, QMessageBox::Critical);
348 return -1;
349 }
350 pSock->connectToHost(m_pPara->m_Net.GetHost(), m_pPara->m_Net.GetPort());
351
352 QString serverHost;
353 serverHost = pSock->peerName();
354 qDebug(log) << "Server Host:" << serverHost;
355 setServerName(serverHost.toStdString().c_str());
356
357 return nRet;
358 } catch (rdr::Exception& e) {
359 qCritical(log) << "SocketInit exception:" << e.str();
360 emit sigError(-1, e.str());
361 nRet = -1;
362 }
363 return nRet;
364}
365
366int CBackendVnc::SSHInit()
367{
368#ifdef HAVE_LIBSSH
369 auto channel = QSharedPointer<CChannelSSHTunnel>(
370 new CChannelSSHTunnel(&m_pPara->m_Proxy.m_SSH, &m_pPara->m_Net, this));
371 if(!channel) {
372 qCritical(log) << "New CChannelSSHTunnel fail";
373 return -1;
374 }
375 m_DataChannel = channel;
376 SetChannelConnect(m_DataChannel);
377 if(!channel->open(QIODevice::ReadWrite))
378 {
379 QString szErr;
380 szErr = tr("Failed to open SSH tunnel:");
381 szErr += "(" + m_pPara->m_Proxy.m_SSH.m_Net.GetHost();
382 szErr += ":";
383 szErr += QString::number(m_pPara->m_Proxy.m_SSH.m_Net.GetPort());
384 szErr += " - ";
385 szErr += m_pPara->m_Net.GetHost();
386 szErr += ":";
387 szErr += QString::number(m_pPara->m_Net.GetPort()) + ")";
388 QString szMsg = szErr + "\n" + channel->errorString();
389 emit sigShowMessageBox(tr("Error"), szMsg, QMessageBox::Critical);
390 return -2;
391 }
392 m_SecurityLevel = CSecurityLevel::Level::Proxy;
393#endif
394 return 0;
395}
396
397int CBackendVnc::SetChannelConnect(QSharedPointer<CChannel> channel)
398{
399 bool check = false;
400 check = connect(channel.data(), SIGNAL(sigConnected()),
401 this, SLOT(slotConnected()));
402 Q_ASSERT(check);
403 check = connect(channel.data(), SIGNAL(sigDisconnected()),
404 this, SLOT(slotDisConnected()));
405 Q_ASSERT(check);
406 check = connect(channel.data(), SIGNAL(readyRead()),
407 this, SLOT(slotReadyRead()));
408 Q_ASSERT(check);
409 check = connect(channel.data(), SIGNAL(sigError(int, const QString&)),
410 this, SLOT(slotChannelError(int, const QString&)));
411 Q_ASSERT(check);
412 if(m_pOperate && m_pOperate->GetStats()) {
413 check = connect(channel.data(), SIGNAL(sigReceive(quint64)),
414 m_pOperate->GetStats(), SLOT(AddReceives(quint64)));
415 Q_ASSERT(check);
416 check = connect(channel.data(), SIGNAL(sigSend(quint64)),
417 m_pOperate->GetStats(), SLOT(AddSends(quint64)));
418 Q_ASSERT(check);
419 }
420 return 0;
421}
422
424{
425 qDebug(log) << "CConnectVnc::OnStop()";
426 close();
427 setStreams(nullptr, nullptr);
428 if(m_DataChannel) {
429 m_DataChannel->close();
430 }
431 emit sigFinished();
432 return 0;
433}
434
449{
450 int nRet = 0;
451#ifdef HAVE_LIBSSH
452 if(m_pPara->m_Proxy.GetUsedType() == CParameterProxy::TYPE::SSHTunnel) {
453 CChannelSSHTunnel* channel = (CChannelSSHTunnel*)m_DataChannel.data();
454 if(channel)
455 nRet = channel->Process();
456 }
457#endif
458
459 return nRet;
460}
461
463{
464#ifdef HAVE_LIBSSH
465 if(m_pPara->m_Proxy.GetUsedType() == CParameterProxy::TYPE::SSHTunnel) {
466 CChannelSSHTunnel* channel = (CChannelSSHTunnel*)m_DataChannel.data();
467 if(channel)
468 channel->WakeUp();
469 }
470#endif
471 return 0;
472}
473
474void CBackendVnc::slotConnected()
475{
476 qDebug(log) << Q_FUNC_INFO;
477 if(m_pPara->GetIce())
478 qInfo(log) << "Connected to peer " + m_pPara->GetPeerUser();
479 else {
480 auto &net = m_pPara->m_Net;
481 QString szInfo = "Connected to "
482 + net.GetHost() + ":" + QString::number(net.GetPort());
483#ifdef HAVE_LIBSSH
484 if(CParameterProxy::TYPE::SSHTunnel == m_pPara->m_Proxy.GetUsedType())
485 {
486 auto &sshNet = m_pPara->m_Proxy.m_SSH.m_Net;
487 szInfo += " with ssh turnnel: " + sshNet.GetHost()
488 + ":" + QString::number(sshNet.GetPort());
489 }
490#endif
491 qInfo(log) << szInfo;
492 }
493 int nRet = SetPara();
494 if(nRet)
495 {
496 emit sigStop();
497 return;
498 }
499 m_InStream = QSharedPointer<rdr::InStream>(new CInStreamChannel(m_DataChannel.data()));
500 m_OutStream = QSharedPointer<rdr::OutStream>(new COutStreamChannel(m_DataChannel.data()));
501 if(!(m_InStream && m_OutStream))
502 {
503 qCritical(log) << "m_InStream or m_OutStream is null";
504 emit sigStop();
505 return;
506 }
507 setStreams(m_InStream.data(), m_OutStream.data());
508 initialiseProtocol();
509}
510
511void CBackendVnc::slotDisConnected()
512{
513 QString szInfo;
514 szInfo = "CConnectVnc::slotDisConnected() from "
515 + m_pPara->m_Net.GetHost() + ":"
516 + QString::number(m_pPara->m_Net.GetPort());
517#ifdef HAVE_LIBSSH
518 if(CParameterProxy::TYPE::SSHTunnel == m_pPara->m_Proxy.GetUsedType())
519 {
520 auto &net = m_pPara->m_Proxy.m_SSH.m_Net;
521 szInfo += " with ssh turnnel: " + net.GetHost()
522 + ":" + QString::number(net.GetPort());
523 }
524#endif
525 qInfo(log) << szInfo;
526}
527
528void CBackendVnc::slotReadyRead()
529{
530 //qDebug(log) << "CConnectVnc::slotReadyRead";
531 QString szErr("processMsg exception: ");
532 int nRet = -1;
533 try {
534 getOutStream()->flush();
535
536 getOutStream()->cork(true);
537 while(processMsg())
538 ;
539 getOutStream()->cork(false);
540 return;
541 } catch (rfb::AuthFailureException& e) {
542 szErr = tr("Logon to ");
543 szErr += m_pPara->m_Net.GetHost();
544 szErr += ":";
545 szErr += QString::number(m_pPara->m_Net.GetPort());
546 szErr += tr(" fail.");
547 QString szMsg = szErr + "\n" + tr("Please check that the username and password are correct.") + "\n";
548 emit sigShowMessageBox(tr("Error"), szMsg, QMessageBox::Critical);
549
550 szErr += " [";
551 szErr += e.str();
552 szErr += "]";
553 } catch (rfb::ConnFailedException& e) {
554 QString szErr;
555 szErr = tr("Connect to ");
556 szErr += m_pPara->m_Net.GetHost();
557 szErr += ":";
558 szErr += QString::number(m_pPara->m_Net.GetPort());
559 szErr += tr(" fail.");
560 szErr += " [";
561 szErr += e.str();
562 szErr += "]";
563 emit sigShowMessageBox(tr("Error"), szErr, QMessageBox::Critical);
564 } catch (rdr::EndOfStream& e) {
565 szErr += e.str();
566 } catch(rdr::GAIException &e) {
567 nRet = e.err;
568 szErr += "[" + QString::number(e.err) + "] " + e.str();
569 } catch(rdr::SystemException &e) {
570 nRet = e.err;
571 szErr += "[" + QString::number(e.err) + "] " + e.str();
572 } catch (rdr::Exception& e) {
573 szErr += e.str();
574 } catch (std::exception &e) {
575 szErr += e.what();
576 } catch(...) {
577 szErr += "unknown exception";
578 }
579 qCritical(log) << szErr;
580 emit sigError(nRet, szErr);
581 emit sigStop();
582}
583
584void CBackendVnc::slotChannelError(int nErr, const QString& szErr)
585{
586 qDebug(log) << "Channel error:" << nErr << "-" << szErr;
587 emit sigError(nErr, szErr);
588 emit sigStop();
589}
590
591// initDone() is called when the serverInit message has been received. At
592// this point we create the desktop window and display it. We also tell the
593// server the pixel format and encodings to use and request the first update.
594void CBackendVnc::initDone()
595{
596 Q_ASSERT(m_pPara);
597 qDebug(log, "initDone: %s", ConnectInformation().toStdString().c_str());
598
599 emit sigSetDesktopSize(server.width(), server.height());
600 QString szName = QString::fromUtf8(server.name());
601 emit sigServerName(szName);
602
603 //Set viewer frame buffer
604 setFramebuffer(new CFramePixelBuffer(server.width(), server.height()));
605
606 // Force a switch to the format and encoding we'd like
607 // Set Preferred Encoding
608 setPreferredEncoding(m_pPara->GetPreferredEncoding());
609 updatePixelFormat();
610
611 emit sigSecurityLevel(m_SecurityLevel);
612 emit sigRunning();
613}
614
615void CBackendVnc::resizeFramebuffer()
616{
617 rfb::ModifiablePixelBuffer* buffer = getFramebuffer();
618
619 qDebug(log) << "CConnectVnc::resizeFramebuffer: new:"
620 << server.width() << server.height()
621 << "old:" << buffer->width() << buffer->height();
622
623 if(server.width() == buffer->width() && server.height() == buffer->height())
624 return;
625
626 //Set viewer frame buffer
627 setFramebuffer(new CFramePixelBuffer(server.width(), server.height()));
628 emit sigSetDesktopSize(server.width(), server.height());
629}
630
631void CBackendVnc::setColourMapEntries(int firstColour, int nColours, uint16_t *rgbs)
632{
633 qCritical(log) << "Invalid SetColourMapEntries from server!";
634}
635
636void CBackendVnc::bell()
637{
638 qApp->beep();
639}
640
641void CBackendVnc::setLEDState(unsigned int state)
642{
643 qDebug(log) << "CConnectVnc::setLEDState" << state;
644 CConnection::setLEDState(state);
645 emit sigUpdateLedState(state);
646}
647
648void CBackendVnc::setCursor(int width, int height, const rfb::Point &hotspot, const uint8_t *data)
649{
650 //qDebug(log) << "CConnectVnc::setCursor x:" << hotspot.x << ";y:" << hotspot.y;
651 if ((width == 0) || (height == 0)) {
652 QImage cursor(1, 1, QImage::Format_ARGB32);
653 uint8_t *buffer = cursor.bits();
654 memset(buffer, 0, 4);
655 emit sigUpdateCursor(QCursor(QPixmap::fromImage(cursor), hotspot.x, hotspot.y));
656 } else {
657 QImage cursor(width, height, QImage::Format_ARGB32);
658 uint8_t *buffer = cursor.bits();
659 memcpy(buffer, data, width * height * 4);
660 emit sigUpdateCursor(QCursor(QPixmap::fromImage(cursor), hotspot.x, hotspot.y));
661 }
662}
663
664void CBackendVnc::setCursorPos(const rfb::Point &pos)
665{
666 //qDebug(log) << "CConnectVnc::setCursorPos x:" << pos.x << ";y:" << pos.y;
667 emit sigUpdateCursorPosition(QPoint(pos.x, pos.y));
668}
669
670void CBackendVnc::fence(uint32_t flags, unsigned int len, const uint8_t data[])
671{
672 //qDebug(log, "CConnectVnc::fence:flags:0x%X; len:%d", flags, len);
673 CMsgHandler::fence(flags, len, data);
674 if (!(flags & rfb::fenceFlagRequest))
675 return;
676
677 // We handle everything synchronously so we trivially honor these modes
678 flags = flags & (rfb::fenceFlagBlockBefore | rfb::fenceFlagBlockAfter);
679 writer()->writeFence(flags, len, data);
680 return;
681}
682
683void CBackendVnc::getUserPasswd(bool secure, std::string *user, std::string *password)
684{
685 if(password)
686 {
687 auto &user = m_pPara->m_Net.m_User;
688 *password = user.GetPassword().toStdString();
689 if(user.GetPassword().isEmpty())
690 {
691 int nRet = QDialog::Rejected;
692 auto *net = &m_pPara->m_Net;
693 emit sigBlockShowWidget("CDlgUserPassword", nRet, net);
694 if(QDialog::Accepted == nRet)
695 {
696 *password = user.GetPassword().toStdString();
697 }
698 }
699 if(!password->empty())
700 m_SecurityLevel |= CSecurityLevel::Level::Authentication;
701 }
702}
703
704int CBackendVnc::getX509File(std::string *ca, std::string *crl)
705{
706 auto &user = m_pPara->m_Net.m_User;
707 if(ca)
708 *ca = user.GetCAFile().toStdString().c_str();
709 if(crl)
710 *crl = user.GetCRLFile().toStdString().c_str();
711 return 0;
712}
713
714bool CBackendVnc::showMsgBox(rfb::MsgBoxFlags flags, const char *title, const char *text)
715{
716 qDebug(log) << title << text;
717 QMessageBox::StandardButton nRet = QMessageBox::No;
718 QMessageBox::StandardButtons fgBtn = QMessageBox::No;
719 bool bCheckBox = 0;
720 if((int)flags & (int)rfb::MsgBoxFlags::M_OK)
721 fgBtn |= QMessageBox::Ok;
722
723 if((int)flags & (int)rfb::MsgBoxFlags::M_OKCANCEL)
724 fgBtn |= QMessageBox::Ok | QMessageBox::Cancel;
725 if((int)flags & (int)rfb::MsgBoxFlags::M_YESNO)
726 fgBtn |= QMessageBox::Yes | QMessageBox::No;
727
728 emit sigBlockShowMessageBox(QString(title), QString(text),
729 fgBtn, nRet, bCheckBox);
730 if(QMessageBox::Ok == nRet
731 || QMessageBox::Yes == nRet)
732 return true;
733 return false;
734}
735
736// setName() is called when the desktop name changes
737void CBackendVnc::setName(const char *name)
738{
739 qDebug(log) << "CConnectVnc::setName:" << name;
740 CConnection::setName(name);
741 QString szName = QString::fromUtf8(server.name());
742 emit sigServerName(szName);
743}
744
745// framebufferUpdateStart() is called at the beginning of an update.
746// Here we try to send out a new framebuffer update request so that the
747// next update can be sent out in parallel with us decoding the current
748// one.
749void CBackendVnc::framebufferUpdateStart()
750{
751 //qDebug(log) << "CConnectVnc::framebufferUpdateStart()";
752 CConnection::framebufferUpdateStart();
753}
754
755// framebufferUpdateEnd() is called at the end of an update.
756// For each rectangle, the FdInStream will have timed the speed
757// of the connection, allowing us to select format and encoding
758// appropriately, and then request another incremental update.
759void CBackendVnc::framebufferUpdateEnd()
760{
761 //qDebug(log) << "CConnectVnc::framebufferUpdateEnd()";
762 rfb::CConnection::framebufferUpdateEnd();
763
764 if(m_pPara /*&& m_pPara->GetBufferEndRefresh()*/)
765 {
766 const QImage& img = dynamic_cast<CFramePixelBuffer*>(getFramebuffer())->getImage();
767 emit sigUpdateRect(img);
768 }
769}
770
771// requestNewUpdate() requests an update from the server, having set the
772// format and encoding appropriately.
773void CBackendVnc::updatePixelFormat()
774{
775 Q_ASSERT(m_pPara);
776
777 if(!m_pPara) return;
778 rfb::PixelFormat pf;
779
780 switch (m_pPara->GetColorLevel()) {
781 case CParameterVnc::Full:
782 pf = fullColourPF;
783 break;
784 case CParameterVnc::Medium:
785 pf = mediumColourPF;
786 break;
787 case CParameterVnc::Low:
788 pf = lowColourPF;
789 break;
790 case CParameterVnc::VeryLow:
791 pf = verylowColourPF;
792 break;
793 }
794
795 char str[256];
796 pf.print(str, 256);
797 qInfo(log) << "Update pixel format:" << str;
798 setPF(pf);
799}
800
801void CBackendVnc::mousePressEvent(QMouseEvent *event)
802{
803 if(!writer()) return;
804 if(m_pPara && m_pPara->GetOnlyView()) return;
805
806 uint8_t mask = 0;
807 if(event->button() & Qt::MouseButton::LeftButton)
808 mask |= 0x1;
809 if(event->button() & Qt::MouseButton::MiddleButton)
810 mask |= 0x2;
811 if(event->button() & Qt::MouseButton::RightButton)
812 mask |= 0x4;
813 if(event->button() & Qt::MouseButton::BackButton)
814 mask |= 0x80;
815
816 QPoint pos = event->pos();
817 rfb::Point p(pos.x(), pos.y());
818
819 //qDebug(logMouse) << Q_FUNC_INFO << event->buttons() << event->button() << pos << mask;
820
821 try{
822 writer()->writePointerEvent(p, mask);
823 } catch (rdr::Exception& e) {
824 emit sigError(-1, e.str());
825 }
826}
827
828void CBackendVnc::mouseReleaseEvent(QMouseEvent *event)
829{
830 if(!writer()) return;
831 if(m_pPara && m_pPara->GetOnlyView()) return;
832 uint8_t mask = 0;
833 QPoint pos = event->pos();
834 rfb::Point p(pos.x(), pos.y());
835 //qDebug(logMouse) << Q_FUNC_INFO << event->buttons() << event->button() << pos << mask;
836 try{
837 writer()->writePointerEvent(p, mask);
838 } catch (rdr::Exception& e) {
839 emit sigError(-1, e.str());
840 }
841}
842
843void CBackendVnc::mouseMoveEvent(QMouseEvent *event)
844{
845 if(!writer()) return;
846 if(m_pPara && m_pPara->GetOnlyView()) return;
847
848 QPoint pos = event->pos();
849 rfb::Point p(pos.x(), pos.y());
850 uint8_t mask = 0;
851 if(event->buttons() & Qt::MouseButton::LeftButton)
852 mask |= 0x1;
853 if(event->buttons() & Qt::MouseButton::MiddleButton)
854 mask |= 0x2;
855 if(event->buttons() & Qt::MouseButton::RightButton)
856 mask |= 0x4;
857 if(event->buttons() & Qt::MouseButton::BackButton)
858 mask |= 0x80;
859
860 //qDebug(logMouse) << Q_FUNC_INFO << event->buttons() << event->button() << pos << mask;
861
862 try{
863 writer()->writePointerEvent(p, mask);
864 } catch (rdr::Exception& e) {
865 emit sigError(-1, e.str());
866 }
867}
868
869// https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#pointerevent
870void CBackendVnc::wheelEvent(QWheelEvent *event)
871{
872 if(!writer()) return;
873 if(m_pPara && m_pPara->GetOnlyView()) return;
874 uint8_t mask = 0;
875 if(event->buttons() & Qt::MouseButton::LeftButton)
876 mask |= 0x1;
877 if(event->buttons() & Qt::MouseButton::MiddleButton)
878 mask |= 0x2;
879 if(event->buttons() & Qt::MouseButton::RightButton)
880 mask |= 0x4;
881 if(event->buttons() & Qt::MouseButton::BackButton)
882 mask |= 0x80;
883
884 QPoint d = event->angleDelta();
885 if(d.y() > 0)
886 mask |= 0x8;
887 if(d.y() < 0)
888 mask |= 0x10;
889 if(d.x() < 0)
890 mask |= 0x20;
891 if(d.x() > 0)
892 mask |= 0x40;
893
894 QPointF pos;
895#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
896 pos = event->position();
897#else
898 pos = event->pos();
899#endif
900
901 rfb::Point p(pos.x(), pos.y());
902 /*
903 qDebug(logMouse) << Q_FUNC_INFO << event->buttons()
904#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
905 << event->button()
906#endif
907 << pos << mask; //*/
908
909 try{
910 writer()->writePointerEvent(p, mask);
911 } catch (rdr::Exception& e) {
912 emit sigError(-1, e.str());
913 }
914}
915
916void CBackendVnc::keyPressEvent(QKeyEvent *event)
917{
918 if(!writer()) return;
919 if(m_pPara && m_pPara->GetOnlyView()) return;
920 bool modifier = false;
921 if (event->modifiers() & Qt::ShiftModifier)
922 modifier = true;
923
924 uint32_t k = CConvertKeyCode::QtToRfbKey(event->key(), modifier);
925 //qDebug(logKey) << Q_FUNC_INFO << event << k << modifier;
926
927 try{
928 writer()->writeKeyEvent(k, 0, true);
929 } catch (rdr::Exception& e) {
930 emit sigError(-1, e.str());
931 }
932}
933
934void CBackendVnc::keyReleaseEvent(QKeyEvent *event)
935{
936 if(m_pPara && m_pPara->GetOnlyView()) return;
937 if(!writer()) return;
938 bool modifier = false;
939 if (event->modifiers() & Qt::ShiftModifier)
940 modifier = true;
941
942 uint32_t k = CConvertKeyCode::QtToRfbKey(event->key(), modifier);
943 //qDebug(logKey) << Q_FUNC_INFO << event << k << modifier;
944
945 try{
946 writer()->writeKeyEvent(k, 0, false);
947 } catch (rdr::Exception& e) {
948 emit sigError(-1, e.str());
949 }
950}
951
952void CBackendVnc::InputMethodEvent(QInputMethodEvent *event)
953{
954 //qDebug(logInputMethod) << Q_FUNC_INFO << event;
955 if (m_pPara && m_pPara->GetOnlyView()) return;
956 if (!writer()) return;
957
958 QString szText = event->commitString();
959 if (szText.isEmpty())
960 return;
961
962 //qDebug(logInputMethod) << Q_FUNC_INFO << szText;
963
964 QVector<uint32_t> ucs4 = szText.toUcs4();
965 foreach (uint32_t cp, ucs4) {
966 quint32 keysym = static_cast<quint32>(cp); // 直接用 Unicode code point 作为 keysym
967 try {
968 writer()->writeKeyEvent(keysym, 0, true);
969 writer()->writeKeyEvent(keysym, 0, false);
970 } catch (rdr::Exception &e) {
971 emit sigError(-1, e.str());
972 }
973 }
974}
975
976QString CBackendVnc::ConnectInformation()
977{
978 const int len = 128;
979 char buf[len];
980 QString szInfo;
981 szInfo = QString("Desktop name: ") + server.name() + "\n";
982 szInfo += QString("Size: %1 x %2").arg(server.width()).arg(server.height()) + "\n";
983
984 // TRANSLATORS: Will be filled in with a string describing the
985 // protocol pixel format in a fairly language neutral way
986 server.pf().print(buf, len);
987 szInfo += QString("Pixel format: ") + buf + "\n";
988 szInfo += QString("Requested encoding: ") + rfb::encodingName(getPreferredEncoding()) + "\n";
989 szInfo += QString("Protocol version: %1.%2").arg(server.majorVersion).arg(server.minorVersion) + "\n";
990 szInfo += QString("Security method: ") + rfb::secTypeName(csecurity->getType()) + "\n";
991 szInfo += QString("Support local cursor: %1").arg(supportsLocalCursor) + "\n";
992 szInfo += QString("Support cursor position: %1").arg(supportsCursorPosition) + "\n";
993 szInfo += QString("Support desktop resize: %1").arg(supportsDesktopResize) + "\n";
994 szInfo += QString("Support LED state: %1").arg(supportsLEDState) + "\n";
995 szInfo += QString("Led state: %1").arg(server.ledState()) + "\n";
996 szInfo += QString("Supports QEMU KeyEvent: %1").arg(server.supportsQEMUKeyEvent) + "\n";
997 szInfo += QString("Supports Set Desktop Size: %1").arg(server.supportsSetDesktopSize) + "\n";
998 szInfo += QString("Support fance: %1").arg(server.supportsFence) + "\n";
999 szInfo += QString("Support continue updates: %1").arg(server.supportsContinuousUpdates) + "\n";
1000
1001 return szInfo;
1002}
1003
1005{
1006 //qDebug(log) << "CConnectVnc::slotClipBoardChanged()";
1007 if(!m_pPara->GetClipboard() || !getOutStream() || !writer()) return;
1008 QClipboard* pClip = QApplication::clipboard();
1009 if(pClip->ownsClipboard()) return;
1010 announceClipboard(true);
1011}
1012
1013void CBackendVnc::handleClipboardRequest()
1014{
1015 //qDebug(log) << "CConnectVnc::handleClipboardRequest";
1016 if(!m_pPara->GetClipboard() || !getOutStream() || !writer()) return;
1017 const QClipboard *clipboard = QApplication::clipboard();
1018 const QMimeData *mimeData = clipboard->mimeData();
1019
1020 /*if (mimeData->hasImage()) {
1021 // setPixmap(qvariant_cast<QPixmap>(mimeData->imageData()));
1022 } else */if (mimeData->hasText()) {
1023 QString szText = mimeData->text();
1024 qDebug(log)
1025 << "CConnectVnc::handleClipboardRequest:szText:" << szText;
1026 try{
1027 sendClipboardData(szText.toStdString().c_str());
1028 } catch (rdr::Exception& e) {
1029 qCritical(log) << "sendClipboardData exception" << e.str();
1030 }
1031 } /*else if (mimeData->hasHtml()) {
1032 QString szHtml = mimeData->html();
1033 qDebug(log)
1034 << "CConnectVnc::handleClipboardRequest:html:" << szHtml;
1035 try{
1036 sendClipboardData(mimeData->html().toStdString().c_str());
1037 } catch (rdr::Exception& e) {
1038 qCritical(log) << "sendClipboardData exception" << e.str();
1039 }
1040 }*/ else {
1041 qCritical(log) << "Cannot display data";
1042 }
1043}
1044
1045void CBackendVnc::handleClipboardAnnounce(bool available)
1046{
1047 //qDebug(log) << "CConnectVnc::handleClipboardAnnounce";
1048 if(!m_pPara->GetClipboard() || !getOutStream() || !writer()) return;
1049 if(available)
1050 this->requestClipboard();
1051}
1052
1053void CBackendVnc::handleClipboardData(const char *data)
1054{
1055 //qDebug(log) << "CConnectVnc::handleClipboardData";
1056 if(!m_pPara->GetClipboard() || !getOutStream() || !writer()) return;
1057
1058 QMimeData* pData = new QMimeData();
1059 pData->setText(QString::fromUtf8(data));
1060 emit sigSetClipboard(pData);
1061}
1062
1063void CBackendVnc::authSuccess()
1064{
1065 qDebug(log) << "CConnectVnc::authSuccess";
1066}
Remote desktop interface.
void sigUpdateRect(const QRect &r, const QImage &image)
Notify the CFrmView update image.
virtual void slotClipBoardChanged()=0
Be called when the clip board change.
virtual OnStartReturnValue OnStart() override
Initialization.
virtual int OnStop() override
Clean.
virtual int WakeUp() override
Wake up backend thread(background thread).
virtual int OnProcess() override
Specific operation processing of plug-in connection.
void sigStop()
Notify the user to stop.
void sigShowMessageBox(const QString &szTitle, const QString &szMessage, const QMessageBox::Icon &icon=QMessageBox::Information)
Trigger the display of a message dialog (QMessageBox) in the main thread from a background thread wit...
void sigError(const int nError, const QString &szError=QString())
Triggered when an error is generated.
void sigBlockShowMessageBox(const QString &szTitle, const QString &szMessage, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton &nRet, bool &checkBox, QString checkBoxContext=QString())
Block background threads and display message dialogs in foreground threads (QMessageBox).
void sigRunning()
Emitted when the plugin is successfully started.
void sigFinished()
Successful stopped signal.
void sigSecurityLevel(CSecurityLevel::Levels level)
Triggered when the security level changes.
void sigBlockShowWidget(const QString &className, int &nRet, void *pContext)
Blocks the background thread and displays the window in the foreground thread.
ssh tunnel class
CChannel(QTcpSocket *pSocket, QObject *parent=nullptr)
CChannel.
Definition Channel.cpp:19
static quint32 QtToRfbKey(int inkey, bool modifier)
CConvertKeyCode::QtToRfbKey.
static CICE * Instance()
Single instance.
Definition Ice.cpp:55
The ICE signal interface class.
Definition IceSignal.h:26
Remote desktop operate interface.
virtual CParameterDesktop * GetParameter() const
Get parameter.
@ OnlyPasswordX509None
Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile.
Security level.
Definition Stats.h:97