4#include <QNetworkProxy>
6#include <QLoggingCategory>
9#include "ConvertKeyCode.h"
10#include "BackendLibVNCServer.h"
12static Q_LOGGING_CATEGORY(log,
"LibVNCServer.Backend")
13static Q_LOGGING_CATEGORY(logger,
"LibVNCServer.Backend.log")
14static Q_LOGGING_CATEGORY(logKey,
"LibVNCServer.Backend.Key")
15static Q_LOGGING_CATEGORY(logInputMethod,
"LibVNCServer.Backend.InputMethod")
16static Q_LOGGING_CATEGORY(logMouse,
"LibVNCServer.Backend.Mouse")
18const
char* gThis =
"This pointer";
19#define LOG_BUFFER_LENGTH 1024
20static void rfbQtClientLog(
const char *format, ...)
24 char buf[LOG_BUFFER_LENGTH];
26 if(!rfbEnableClientLogging)
29 va_start(args, format);
30 nRet = vsnprintf(buf, LOG_BUFFER_LENGTH, format, args);
34 qCritical(log) <<
"vsnprintf fail";
37 if(nRet >= LOG_BUFFER_LENGTH)
39 qCritical(log) <<
"vsprintf buf is short,"
40 << nRet <<
">" << LOG_BUFFER_LENGTH
41 <<
". Truncated it:" << nRet - LOG_BUFFER_LENGTH;
42 buf[LOG_BUFFER_LENGTH - 1] = 0;
44 qDebug(logger) << buf;
56 rfbClientLog = rfbQtClientLog;
58 if(!m_pParameter->GetLocalCursor())
60 emit sigUpdateCursor(QCursor(Qt::BlankCursor));
64CBackendLibVNCServer::~CBackendLibVNCServer()
66 qDebug(log) <<
"CConnectLibVNCServer::~CConnectLibVNCServer()";
77 qDebug(log) <<
"CConnectLibVNCServer::OnStart()";
78 if(m_pClient) Q_ASSERT(
false);
80 m_pClient = rfbGetClient(8, 3, 4);
84 szErr = tr(
"Protocol version error");
86 qCritical(log) <<
"rfbGetClient fail";
87 return OnStartReturnValue::Fail;
91 m_pClient->programName = strdup(qApp->applicationName().toStdString().c_str());
92 m_pClient->appData.shareDesktop = m_pParameter->GetShared();
93 m_pClient->appData.viewOnly = m_pParameter->GetOnlyView();
94 m_pClient->appData.useRemoteCursor = m_pParameter->GetLocalCursor();
133 m_pClient->appData.enableJPEG = m_pParameter->GetJpeg();
134 if(m_pClient->appData.enableJPEG)
135 m_pClient->appData.qualityLevel = m_pParameter->GetQualityLevel();
136 if(m_pParameter->GetEnableCompressLevel())
137 m_pClient->appData.compressLevel = m_pParameter->GetCompressLevel();
140 m_pClient->MallocFrameBuffer = cb_resize;
141 m_pClient->GotFrameBufferUpdate = cb_update;
142 m_pClient->HandleKeyboardLedState = cb_kbd_leds;
143 m_pClient->Bell = cb_bell;
144 m_pClient->HandleTextChat = cb_text_chat;
145 m_pClient->GotXCutText = cb_got_selection;
146 m_pClient->GetCredential = cb_get_credential;
147 m_pClient->GetPassword = cb_get_password;
148 m_pClient->HandleCursorPos = cb_cursor_pos;
149 m_pClient->GotCursorShape = cb_got_cursor_shape;
151 m_pClient->canHandleNewFBSize = TRUE;
152 rfbClientSetClientData(m_pClient, (
void*)gThis,
this);
155 switch(m_pParameter->m_Proxy.GetUsedType())
157 case CParameterProxy::TYPE::None:
160 if(m_pParameter->m_Net.GetHost().isEmpty())
163 szErr = tr(
"The server is empty, please input it");
164 qCritical(log) << szErr;
166 return OnStartReturnValue::Fail;
168 auto &net = m_pParameter->m_Net;
169 m_pClient->serverHost = strdup(net.GetHost().toStdString().c_str());
170 m_pClient->serverPort = net.GetPort();
172 if(!rfbInitClient(m_pClient,
nullptr,
nullptr))
177 szErr = tr(
"Fail: Connect to %1:%2").arg(m_pParameter->m_Net.GetHost(),
178 QString::number(m_pParameter->m_Net.GetPort()));
179 qCritical(log) << szErr;
181 return OnStartReturnValue::Fail;
184 QString szInfo = QString(
"Connected to ") + m_pClient->desktopName;
185 qInfo(log) << szInfo;
187 emit sigSetDesktopSize(m_pClient->width, m_pClient->height);
188 emit sigServerName(m_pClient->desktopName);
192 return OnStartReturnValue::UseOnProcess;
238 case CParameterProxy::TYPE::SSHTunnel:
244 &m_pParameter->m_Proxy.m_SSH, &m_pParameter->m_Net,
this);
247 return OnStartReturnValue::Fail;
248 bool check = connect(m_pThread, SIGNAL(sigServer(QString, quint16)),
251#if defined(HAVE_UNIX_DOMAIN_SOCKET)
252 check = connect(m_pThread, SIGNAL(sigServer(QString)),
256 check = connect(m_pThread, SIGNAL(
sigError(
int,QString)),
257 this, SIGNAL(
sigError(
int,QString)));
259 check = connect(m_pThread, SIGNAL(
sigStop()),
264 return OnStartReturnValue::NotUseOnProcess;
272 return OnStartReturnValue::UseOnProcess;
277 qDebug(log) <<
"CConnectLibVNCServer::OnStop()";
281 disconnect(m_pThread);
289 rfbClientCleanup(m_pClient);
320 struct timeval timeout = {0, DEFAULT_TIMEOUT};
323 if(RFB_INVALID_SOCKET != m_pClient->sock)
325 FD_SET(m_pClient->sock, &set);
327 rfbSocket eventFd = m_Event.GetFd();
328 if(RFB_INVALID_SOCKET != eventFd)
330 FD_SET(eventFd, &set);
332 rfbSocket fd = RFB_INVALID_SOCKET;
333 fd = std::max(eventFd, m_pClient->sock);
335 nRet = select(fd + 1, &set, NULL, NULL, &timeout);
338 qCritical(log) <<
"select fail:" << nRet <<
"errno" << errno << strerror(errno);
348 if(RFB_INVALID_SOCKET != eventFd && FD_ISSET(eventFd, &set))
354 if(RFB_INVALID_SOCKET != m_pClient->sock
355 && FD_ISSET(m_pClient->sock, &set)) {
357 if(!HandleRFBServerMessage(m_pClient))
359 qCritical(log) <<
"HandleRFBServerMessage fail";
369 return m_Event.WakeUp();
374 if(!m_pParameter || !m_pParameter->GetClipboard() || !m_pClient)
return;
375 QClipboard* pClipboard = QApplication::clipboard();
378 QString szText = pClipboard->text();
379 if(!szText.isEmpty() && m_pClient)
380 SendClientCutText(m_pClient,
381 (
char*)szText.toStdString().c_str(),
382 szText.toStdString().length());
386rfbBool CBackendLibVNCServer::cb_resize(rfbClient* client)
390 if(pThis->OnSize())
return FALSE;
394void CBackendLibVNCServer::cb_update(rfbClient *client,
int x,
int y,
int w,
int h)
399 CBackendLibVNCServer* pThis = (CBackendLibVNCServer*)rfbClientGetClientData(client, (
void*)gThis);
400 QRect rect(x, y, w, h);
401 QImage img = pThis->m_Image.copy(rect);
405void CBackendLibVNCServer::cb_got_selection(rfbClient *client,
const char *text,
int len)
408 CBackendLibVNCServer* pThis = (CBackendLibVNCServer*)rfbClientGetClientData(client, (
void*)gThis);
409 if(!pThis->m_pParameter->GetClipboard())
return;
410 QClipboard* pClipboard = QApplication::clipboard();
412 pClipboard->setText(text);
415void CBackendLibVNCServer::cb_kbd_leds(rfbClient *client,
int value,
int pad)
417 qDebug(log) <<
"CConnectLibVnc::cb_kbd_leds";
420void CBackendLibVNCServer::cb_bell(
struct _rfbClient *client)
425void CBackendLibVNCServer::cb_text_chat(rfbClient *client,
int value,
char *text)
427 qDebug(log) <<
"CConnectLibVnc::cb_text_chat";
430rfbCredential* CBackendLibVNCServer::cb_get_credential(rfbClient *cl,
int credentialType)
432 qDebug(log) <<
"CConnectLibVnc::cb_get_credential";
433 CBackendLibVNCServer* pThis = (CBackendLibVNCServer*)rfbClientGetClientData(cl, (
void*)gThis);
434 rfbCredential *c = (rfbCredential*)malloc(
sizeof(rfbCredential));
435 c->userCredential.username = (
char*)malloc(RFB_BUF_SIZE);
436 memset(c->userCredential.username, 0, RFB_BUF_SIZE);
437 c->userCredential.password = (
char*)malloc(RFB_BUF_SIZE);
438 memset(c->userCredential.password, 0, RFB_BUF_SIZE);
440 if(credentialType != rfbCredentialTypeUser) {
441 qCritical(log) <<
"something else than username and password required for authentication";
445 qDebug(log) <<
"Username and password required for authentication!";
447 memcpy(c->userCredential.username,
448 pThis->m_pParameter->m_Net.
m_User.GetUser().toStdString().c_str(),
449 pThis->m_pParameter->m_Net.
m_User.GetUser().toStdString().length());
450 memcpy(c->userCredential.password,
451 pThis->m_pParameter->m_Net.
m_User.GetPassword().toStdString().c_str(),
452 pThis->m_pParameter->m_Net.
m_User.GetPassword().toStdString().length());
457char* CBackendLibVNCServer::cb_get_password(rfbClient *client)
459 CBackendLibVNCServer* pThis = (CBackendLibVNCServer*)rfbClientGetClientData(client, (
void*)gThis);
460 QString szPassword = pThis->m_pParameter->m_Net.
m_User.GetPassword();
461 if(szPassword.isEmpty())
463 int nRet = QDialog::Rejected;
464 auto *net = &pThis->m_pParameter->m_Net;
466 if(QDialog::Accepted == nRet)
468 szPassword = net->m_User.GetPassword();
471 if(!szPassword.isEmpty())
472 pThis->m_SecurityLevel |= CSecurityLevel::Level::Authentication;
473 return strdup(szPassword.toStdString().c_str());
476int CBackendLibVNCServer::OnSize()
479 int nWidth = m_pClient->width;
480 int nHeight = m_pClient->height;
482 m_Image = QImage(nWidth, nHeight, QImage::Format_RGB32);
484 m_pClient->frameBuffer = m_Image.bits();
486 m_pClient->format.bitsPerPixel = 32;
487 m_pClient->format.depth = m_Image.depth();
488 m_pClient->format.redShift = 16;
489 m_pClient->format.greenShift = 8;
490 m_pClient->format.blueShift = 0;
491 m_pClient->format.redMax = 255;
492 m_pClient->format.greenMax = 255;
493 m_pClient->format.blueMax = 255;
494 SetFormatAndEncodings(m_pClient);
496 emit sigSetDesktopSize(m_pClient->width, m_pClient->height);
500rfbBool CBackendLibVNCServer::cb_cursor_pos(rfbClient *client,
int x,
int y)
508void CBackendLibVNCServer::cb_got_cursor_shape(rfbClient *client,
510 int width,
int height,
516 if(!client->rcSource)
518 qCritical(log) <<
"client->rcSource is null";
521 CBackendLibVNCServer* pThis = (CBackendLibVNCServer*)rfbClientGetClientData(client, (
void*)gThis);
522 if ((width == 0) || (height == 0)) {
523 QImage cursor(1, 1, QImage::Format_ARGB32);
524 uchar* buffer = cursor.bits();
525 memset(buffer, 0, 4);
526 emit pThis->sigUpdateCursor(QCursor(QPixmap::fromImage(cursor), xhot, yhot));
528 switch (bytesPerPixel) {
531 QImage cursor(width, height, QImage::Format_ARGB32);
532 uchar *buffer = cursor.bits();
533 memcpy(buffer, client->rcSource, width * height * 4);
534 uchar* pMask = client->rcMask;
537 uchar* pDest = buffer;
538 for(
int y = 0; y < height; y++)
540 for(
int w = 0; w < width; w++)
546 QColor c = cursor.pixel(w, y);
548 cursor.setPixelColor(w,y,c);
551 *((uint32_t*)pDest) = 0;
557 emit pThis->sigUpdateCursor(QCursor(QPixmap::fromImage(cursor), xhot, yhot));
566void CBackendLibVNCServer::mousePressEvent(QMouseEvent *event)
568 if(!m_pClient)
return;
569 if(m_pParameter && m_pParameter->GetOnlyView())
return;
571 unsigned char mask = 0;
572 if(event->button() & Qt::MouseButton::LeftButton)
574 if(event->button() & Qt::MouseButton::MiddleButton)
576 if(event->button() & Qt::MouseButton::RightButton)
578 if(event->button() & Qt::MouseButton::BackButton)
580 QPoint pos =
event->pos();
584 SendPointerEvent(m_pClient, pos.x(), pos.y(), mask);
587void CBackendLibVNCServer::mouseReleaseEvent(QMouseEvent *event)
589 if(!m_pClient)
return;
590 if(m_pParameter && m_pParameter->GetOnlyView())
return;
592 QPoint pos =
event->pos();
594 SendPointerEvent(m_pClient, pos.x(), pos.y(), mask);
597void CBackendLibVNCServer::mouseMoveEvent(QMouseEvent *event)
599 if(!m_pClient)
return;
600 if(m_pParameter && m_pParameter->GetOnlyView())
return;
602 if(event->buttons() & Qt::MouseButton::LeftButton)
604 if(event->buttons() & Qt::MouseButton::MiddleButton)
606 if(event->buttons() & Qt::MouseButton::RightButton)
608 if(event->buttons() & Qt::MouseButton::BackButton)
610 QPoint pos =
event->pos();
612 SendPointerEvent(m_pClient, pos.x(), pos.y(), mask);
616void CBackendLibVNCServer::wheelEvent(QWheelEvent *event)
618 if(!m_pClient)
return;
619 if(m_pParameter && m_pParameter->GetOnlyView())
return;
621 if(event->buttons() & Qt::MouseButton::LeftButton)
623 if(event->buttons() & Qt::MouseButton::MiddleButton)
625 if(event->buttons() & Qt::MouseButton::RightButton)
627 if(event->buttons() & Qt::MouseButton::BackButton)
630 QPoint d =
event->angleDelta();
641#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
642 pos =
event->position();
649 SendPointerEvent(m_pClient, pos.x(), pos.y(), mask);
652void CBackendLibVNCServer::keyPressEvent(QKeyEvent *event)
654 if(!m_pClient)
return;
655 if(m_pParameter && m_pParameter->GetOnlyView())
return;
656 bool modifier =
false;
657 if (event->modifiers() & Qt::ShiftModifier)
661 SendKeyEvent(m_pClient, k, TRUE);
664void CBackendLibVNCServer::keyReleaseEvent(QKeyEvent *event)
666 if(!m_pClient)
return;
667 if(m_pParameter && m_pParameter->GetOnlyView())
return;
668 bool modifier =
false;
669 if (event->modifiers() & Qt::ShiftModifier)
673 SendKeyEvent(m_pClient, k, FALSE);
676void CBackendLibVNCServer::InputMethodEvent(QInputMethodEvent *event)
679 if(!m_pClient)
return;
680 if(m_pParameter && m_pParameter->GetOnlyView())
return;
682 QString szText =
event->commitString();
683 if (szText.isEmpty())
688 QVector<uint32_t> ucs4 = szText.toUcs4();
689 foreach (uint32_t cp, ucs4) {
690 quint32 keysym =
static_cast<quint32
>(cp);
691 SendKeyEvent(m_pClient, keysym, TRUE);
692 SendKeyEvent(m_pClient, keysym, FALSE);
700 auto &net = m_pParameter->m_Proxy.m_SSH.m_Net;
703 m_pClient->serverHost = strdup(szHost.toStdString().c_str());
704 m_pClient->serverPort = nPort;
705 szErr = tr(
"Will connect to local socket server: %1:%2")
706 .arg(m_pClient->serverHost,
707 QString::number(m_pClient->serverPort));
708 qDebug(log) << szErr;
709 if(!rfbInitClient(m_pClient,
nullptr,
nullptr))
714 szErr = tr(
"Fail: Connect to %1:%2 <- %3:%4 <- %5:%6")
715 .arg(m_pParameter->m_Net.GetHost(),
716 QString::number(m_pParameter->m_Net.GetPort()),
717 net.GetHost(), QString::number(net.GetPort()),
718 szHost, QString::number(nPort)
720 qCritical(log) << szErr;
725 QString szInfo = QString(
"Connected to %1 <- %2:%3 <- %4:%5")
726 .arg(m_pClient->desktopName,
727 net.GetHost(), QString::number(net.GetPort()),
728 szHost, QString::number(nPort));
729 qInfo(log) << szInfo;
731 emit sigSetDesktopSize(m_pClient->width, m_pClient->height);
732 emit sigServerName(m_pClient->desktopName);
738#if defined(HAVE_UNIX_DOMAIN_SOCKET)
744 m_pClient->serverHost = strdup(szFile.toStdString().c_str());
745 szErr = tr(
"Will connect to local socket server: ") + szFile;
746 qDebug(log) << szErr;
747 if(!rfbInitClient(m_pClient,
nullptr,
nullptr))
752 szErr = tr(
"Fail: Connect to %1:%2 with %3")
753 .arg(m_pParameter->m_Net.GetHost(),
754 QString::number(m_pParameter->m_Net.GetPort()),
756 qCritical(log) << szErr;
761 QString szInfo = QString(
"Connected to ") + m_pClient->desktopName +
" with " + szFile;
762 qInfo(log) << szInfo;
765 emit sigSetDesktopSize(m_pClient->width, m_pClient->height);
766 emit sigServerName(m_pClient->desktopName);
void sigUpdateRect(const QRect &r, const QImage &image)
通知视图,图像更新
virtual OnStartReturnValue OnStart() override
void slotConnectProxyServer(QString szHost, quint16 nPort)
[connect local socket server]
virtual int WakeUp() override
唤醒后台线程。
virtual int OnStop() override
清理
virtual void slotClipBoardChanged() override
当剪切板发生改变时调用
virtual int OnProcess() override
插件连接的具体操作处理。因为此插件是非Qt事件,所以在此函数中等待。
void sigInformation(const QString &szInfo)
从后台线程中触发在主线程中显示信息,不阻塞后台线程
void sigStop()
需要通知用户停止时触发。仅由插件触发。 当从插件中需要停止时触发。例如:对端断开连接、重置连接或者连接出错。
void sigShowMessageBox(const QString &szTitle, const QString &szMessage, const QMessageBox::Icon &icon=QMessageBox::Information)
从后台线程中触发在主线程中显示消息对话框(QMessageBox),不阻塞后台线程
void sigError(const int nError, const QString &szError=QString())
当有错误产生时触发
void sigRunning()
当插件开始成功后触发。仅由插件触发
void sigFinished()
停止成功信号。仅由插件触发
virtual void slotTimeOut()
一个非 Qt 事件处理,它调用 OnProcess(),并根据其返回值开始新的定时器。 如果是不是一个非 Qt 事件循环(就是普通的循环处理), 可以重载它,或者 OnStart() 返回值 OnSta...
void sigSecurityLevel(CSecurityLevel::Levels level)
当安全级别改变时触发
void sigBlockShowWidget(const QString &className, int &nRet, void *pContext)
阻塞后台线程,并在前台线程中显示窗口。
static quint32 QtToRfbKey(int inkey, bool modifier)
CConvertKeyCode::QtToRfbKey
CParameterUser m_User
[Instance user]
实现通过本地 SOCKET 与 SSH 隧道转发数据。适用于库没有实现传输层接口,只有 socket 的情况。