File indexing completed on 2024-04-21 04:58:36
0001 /* 0002 SPDX-FileCopyrightText: 2007-2013 Urs Wolfer <uwolfer@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "vncclientthread.h" 0008 #include "krdc_debug.h" 0009 0010 #include <QBitmap> 0011 #include <QCursor> 0012 #include <QMutexLocker> 0013 #include <QPixmap> 0014 #include <QTimer> 0015 #include <cerrno> 0016 #include <netinet/in.h> 0017 #include <netinet/tcp.h> 0018 #include <sys/socket.h> 0019 #include <sys/types.h> 0020 0021 // for detecting intel AMT KVM vnc server 0022 static const QString INTEL_AMT_KVM_STRING = QLatin1String("Intel(r) AMT KVM"); 0023 0024 // Dispatch from this static callback context to the member context. 0025 rfbBool VncClientThread::newclientStatic(rfbClient *cl) 0026 { 0027 VncClientThread *t = (VncClientThread *)rfbClientGetClientData(cl, nullptr); 0028 Q_ASSERT(t); 0029 0030 return t->newclient(); 0031 } 0032 0033 // Dispatch from this static callback context to the member context. 0034 void VncClientThread::updatefbStaticPartial(rfbClient *cl, int x, int y, int w, int h) 0035 { 0036 VncClientThread *t = (VncClientThread *)rfbClientGetClientData(cl, nullptr); 0037 Q_ASSERT(t); 0038 0039 return t->updatefbPartial(x, y, w, h); 0040 } 0041 0042 // Dispatch from this static callback context to the member context. 0043 void VncClientThread::updateFbStaticFinished(rfbClient *cl) 0044 { 0045 VncClientThread *t = (VncClientThread *)rfbClientGetClientData(cl, nullptr); 0046 Q_ASSERT(t); 0047 0048 return t->updatefbFinished(); 0049 } 0050 0051 // Dispatch from this static callback context to the member context. 0052 void VncClientThread::cuttextStatic(rfbClient *cl, const char *text, int textlen) 0053 { 0054 VncClientThread *t = (VncClientThread *)rfbClientGetClientData(cl, nullptr); 0055 Q_ASSERT(t); 0056 0057 t->cuttext(text, textlen); 0058 } 0059 0060 // Dispatch from this static callback context to the member context. 0061 char *VncClientThread::passwdHandlerStatic(rfbClient *cl) 0062 { 0063 VncClientThread *t = (VncClientThread *)rfbClientGetClientData(cl, nullptr); 0064 Q_ASSERT(t); 0065 0066 return t->passwdHandler(); 0067 } 0068 0069 // Dispatch from this static callback context to the member context. 0070 rfbCredential *VncClientThread::credentialHandlerStatic(rfbClient *cl, int credentialType) 0071 { 0072 VncClientThread *t = (VncClientThread *)rfbClientGetClientData(cl, nullptr); 0073 Q_ASSERT(t); 0074 0075 return t->credentialHandler(credentialType); 0076 } 0077 0078 // Dispatch from this static callback context to the member context. 0079 void VncClientThread::outputHandlerStatic(const char *format, ...) 0080 { 0081 VncClientThread *t = qobject_cast<VncClientThread *>(QThread::currentThread()); 0082 Q_ASSERT(t); 0083 0084 va_list args; 0085 va_start(args, format); 0086 t->outputHandler(format, args); 0087 va_end(args); 0088 } 0089 0090 void VncClientThread::cursorShapeHandlerStatic(rfbClient *cl, int xhot, int yhot, int width, int height, int bpp) 0091 { 0092 VncClientThread *t = (VncClientThread *)rfbClientGetClientData(cl, nullptr); 0093 Q_ASSERT(t); 0094 0095 // get cursor shape from remote cursor field 0096 // it's important to set stride for images, pictures in VNC are not always 32-bit aligned 0097 QImage cursorImg; 0098 switch (bpp) { 0099 case 4: 0100 cursorImg = QImage(cl->rcSource, width, height, bpp * width, QImage::Format_RGB32); 0101 break; 0102 case 2: 0103 cursorImg = QImage(cl->rcSource, width, height, bpp * width, QImage::Format_RGB16); 0104 break; 0105 case 1: 0106 cursorImg = QImage(cl->rcSource, width, height, bpp * width, QImage::Format_Indexed8); 0107 cursorImg.setColorTable(t->m_colorTable); 0108 break; 0109 default: 0110 qCWarning(KRDC) << "Unsupported bpp value for cursor shape:" << bpp; 0111 return; 0112 } 0113 0114 // get alpha channel 0115 QImage alpha(cl->rcMask, width, height, 1 * width, QImage::Format_Indexed8); 0116 alpha.setColorTable({qRgb(255, 255, 255), qRgb(0, 0, 0)}); 0117 0118 // apply transparency mask 0119 QPixmap cursorPixmap(QPixmap::fromImage(cursorImg)); 0120 cursorPixmap.setMask(QBitmap::fromImage(alpha)); 0121 0122 Q_EMIT t->gotCursor(QCursor{cursorPixmap, xhot, yhot}); 0123 } 0124 0125 void VncClientThread::setClientColorDepth(rfbClient *cl, VncClientThread::ColorDepth cd) 0126 { 0127 switch (cd) { 0128 case bpp8: 0129 if (m_colorTable.isEmpty()) { 0130 m_colorTable.resize(256); 0131 int r, g, b; 0132 for (int i = 0; i < 256; ++i) { 0133 // pick out the red (3 bits), green (3 bits) and blue (2 bits) bits and make them maximum significant in 8bits 0134 // this gives a colortable for 8bit true colors 0135 r = (i & 0x07) << 5; 0136 g = (i & 0x38) << 2; 0137 b = i & 0xc0; 0138 m_colorTable[i] = qRgb(r, g, b); 0139 } 0140 } 0141 cl->format.depth = 8; 0142 cl->format.bitsPerPixel = 8; 0143 cl->format.redShift = 0; 0144 cl->format.greenShift = 3; 0145 cl->format.blueShift = 6; 0146 cl->format.redMax = 7; 0147 cl->format.greenMax = 7; 0148 cl->format.blueMax = 3; 0149 break; 0150 case bpp16: 0151 cl->format.depth = 16; 0152 cl->format.bitsPerPixel = 16; 0153 cl->format.redShift = 11; 0154 cl->format.greenShift = 5; 0155 cl->format.blueShift = 0; 0156 cl->format.redMax = 0x1f; 0157 cl->format.greenMax = 0x3f; 0158 cl->format.blueMax = 0x1f; 0159 break; 0160 case bpp32: 0161 default: 0162 cl->format.depth = 24; 0163 cl->format.bitsPerPixel = 32; 0164 cl->format.redShift = 16; 0165 cl->format.greenShift = 8; 0166 cl->format.blueShift = 0; 0167 cl->format.redMax = 0xff; 0168 cl->format.greenMax = 0xff; 0169 cl->format.blueMax = 0xff; 0170 } 0171 } 0172 0173 rfbBool VncClientThread::newclient() 0174 { 0175 // 8bit color hack for Intel(r) AMT KVM "classic vnc" = vnc server built in in Intel Vpro chipsets. 0176 if (INTEL_AMT_KVM_STRING == QLatin1String(cl->desktopName)) { 0177 qCDebug(KRDC) << "Intel(R) AMT KVM: switching to 8 bit color depth (workaround, recent libvncserver needed)"; 0178 setColorDepth(bpp8); 0179 } 0180 setClientColorDepth(cl, colorDepth()); 0181 0182 const int width = cl->width, height = cl->height, depth = cl->format.bitsPerPixel; 0183 const int size = width * height * (depth / 8); 0184 if (size <= 0) { 0185 return false; 0186 } 0187 if (frameBuffer) 0188 delete[] frameBuffer; // do not leak if we get a new framebuffer size 0189 frameBuffer = new uint8_t[size]; 0190 cl->frameBuffer = frameBuffer; 0191 memset(cl->frameBuffer, '\0', size); 0192 0193 switch (quality()) { 0194 case RemoteView::High: 0195 cl->appData.encodingsString = "copyrect zlib hextile raw"; 0196 cl->appData.compressLevel = 0; 0197 cl->appData.qualityLevel = 9; 0198 break; 0199 case RemoteView::Medium: 0200 cl->appData.encodingsString = "copyrect tight zrle ultra zlib hextile corre rre raw"; 0201 cl->appData.compressLevel = 5; 0202 cl->appData.qualityLevel = 7; 0203 break; 0204 case RemoteView::Low: 0205 case RemoteView::Unknown: 0206 default: 0207 // bpp8 and tight encoding is not supported in libvnc 0208 cl->appData.encodingsString = "copyrect zrle ultra zlib hextile corre rre raw"; 0209 cl->appData.compressLevel = 9; 0210 cl->appData.qualityLevel = 1; 0211 } 0212 0213 SetFormatAndEncodings(cl); 0214 qCDebug(KRDC) << "Client created"; 0215 return true; 0216 } 0217 0218 void VncClientThread::updatefbPartial(int x, int y, int w, int h) 0219 { 0220 // qCDebug(KRDC) << "updated client: x: " << x << ", y: " << y << ", w: " << w << ", h: " << h; 0221 0222 m_dirtyRect = m_dirtyRect.united(QRect(x, y, w, h)); 0223 } 0224 0225 void VncClientThread::updatefbFinished() 0226 { 0227 const int width = cl->width, height = cl->height; 0228 QImage img; 0229 switch (colorDepth()) { 0230 case bpp8: 0231 img = QImage(cl->frameBuffer, width, height, width, QImage::Format_Indexed8); 0232 img.setColorTable(m_colorTable); 0233 break; 0234 case bpp16: 0235 img = QImage(cl->frameBuffer, width, height, 2 * width, QImage::Format_RGB16); 0236 break; 0237 case bpp32: 0238 img = QImage(cl->frameBuffer, width, height, 4 * width, QImage::Format_RGB32); 0239 break; 0240 } 0241 0242 if (img.isNull()) { 0243 qCDebug(KRDC) << "image not loaded"; 0244 } 0245 0246 if (m_stopped) { 0247 return; // sending data to a stopped thread is not a good idea 0248 } 0249 0250 img.setDevicePixelRatio(m_devicePixelRatio); 0251 setImage(img); 0252 0253 // Compute bounding rect. 0254 QRect updateRect = m_dirtyRect; 0255 m_dirtyRect = QRect(); 0256 0257 // qCDebug(KRDC) << Q_FUNC_INFO << updateRect; 0258 emitUpdated(updateRect.x(), updateRect.y(), updateRect.width(), updateRect.height()); 0259 } 0260 0261 void VncClientThread::cuttext(const char *text, int textlen) 0262 { 0263 const QString cutText = QString::fromLatin1(text, textlen); 0264 qCDebug(KRDC) << cutText; 0265 0266 if (!cutText.isEmpty()) { 0267 emitGotCut(cutText); 0268 } 0269 } 0270 0271 char *VncClientThread::passwdHandler() 0272 { 0273 qCDebug(KRDC) << "password request"; 0274 0275 // Never request a password during a reconnect attempt. 0276 if (!m_keepalive.failed) { 0277 passwordRequest(); 0278 m_passwordError = true; 0279 } 0280 return strdup(m_password.toUtf8().constData()); 0281 } 0282 0283 rfbCredential *VncClientThread::credentialHandler(int credentialType) 0284 { 0285 qCDebug(KRDC) << "credential request" << credentialType; 0286 0287 rfbCredential *cred = nullptr; 0288 0289 switch (credentialType) { 0290 case rfbCredentialTypeUser: 0291 passwordRequest(true); 0292 m_passwordError = true; 0293 0294 cred = new rfbCredential; 0295 cred->userCredential.username = strdup(username().toUtf8().constData()); 0296 cred->userCredential.password = strdup(password().toUtf8().constData()); 0297 break; 0298 default: 0299 qCritical(KRDC) << "credential request failed, unsupported credentialType:" << credentialType; 0300 outputErrorMessage(i18n("VNC authentication type is not supported.")); 0301 break; 0302 } 0303 return cred; 0304 } 0305 0306 void VncClientThread::outputHandler(const char *format, va_list args) 0307 { 0308 auto message = QString::vasprintf(format, args); 0309 0310 message = message.trimmed(); 0311 0312 qCDebug(KRDC) << message; 0313 0314 if ((message.contains(QLatin1String("Couldn't convert "))) || (message.contains(QLatin1String("Unable to connect to VNC server")))) { 0315 // Don't show a dialog if a reconnection is needed. Never contemplate 0316 // reconnection if we don't have a password. 0317 QString tmp = i18n("Server not found."); 0318 if (m_keepalive.set && !m_password.isNull()) { 0319 m_keepalive.failed = true; 0320 if (m_previousDetails != tmp) { 0321 m_previousDetails = tmp; 0322 clientStateChange(RemoteView::Disconnected, tmp); 0323 } 0324 } else { 0325 outputErrorMessageString = tmp; 0326 } 0327 } 0328 0329 // Process general authentication failures before more specific authentication 0330 // failures. All authentication failures cancel any auto-reconnection that 0331 // may be in progress. 0332 if (message.contains(QLatin1String("VNC connection failed: Authentication failed"))) { 0333 m_keepalive.failed = false; 0334 outputErrorMessageString = i18n("VNC authentication failed."); 0335 } 0336 if ((message.contains(QLatin1String("VNC connection failed: Authentication failed, too many tries"))) 0337 || (message.contains(QLatin1String("VNC connection failed: Too many authentication failures")))) { 0338 m_keepalive.failed = false; 0339 outputErrorMessageString = i18n("VNC authentication failed because of too many authentication tries."); 0340 } 0341 0342 if (message.contains(QLatin1String("VNC server closed connection"))) 0343 outputErrorMessageString = i18n("VNC server closed connection."); 0344 0345 // If we are not going to attempt a reconnection, at least tell the user 0346 // the connection went away. 0347 if (message.contains(QLatin1String("read ("))) { 0348 // Don't show a dialog if a reconnection is needed. Never contemplate 0349 // reconnection if we don't have a password. 0350 #ifdef QTONLY 0351 QString tmp = i18n("Disconnected: %1.", message.toStdString().c_str()); 0352 #else 0353 QString tmp = i18n("Disconnected: %1.", message); 0354 #endif 0355 if (m_keepalive.set && !m_password.isNull()) { 0356 m_keepalive.failed = true; 0357 clientStateChange(RemoteView::Disconnected, tmp); 0358 } else { 0359 outputErrorMessageString = tmp; 0360 } 0361 } 0362 0363 // internal messages, not displayed to user 0364 if (message.contains(QLatin1String("VNC server supports protocol version 3.889"))) // see https://bugs.kde.org/162640 0365 outputErrorMessageString = QLatin1String("INTERNAL:APPLE_VNC_COMPATIBILTY"); 0366 } 0367 0368 VncClientThread::VncClientThread(QObject *parent) 0369 : QThread(parent) 0370 , frameBuffer(nullptr) 0371 , cl(nullptr) 0372 , m_devicePixelRatio(1.0) 0373 , m_stopped(false) 0374 { 0375 // We choose a small value for interval...after all if the connection is 0376 // supposed to sustain a VNC session, a reasonably frequent ping should 0377 // be perfectly supportable. 0378 m_keepalive.intervalSeconds = 1; 0379 m_keepalive.failedProbes = 3; 0380 m_keepalive.set = false; 0381 m_keepalive.failed = false; 0382 m_previousDetails = QString(); 0383 outputErrorMessageString.clear(); // don't deliver error messages of old instances... 0384 QMutexLocker locker(&mutex); 0385 0386 QTimer *outputErrorMessagesCheckTimer = new QTimer(this); 0387 outputErrorMessagesCheckTimer->setInterval(500); 0388 connect(outputErrorMessagesCheckTimer, SIGNAL(timeout()), this, SLOT(checkOutputErrorMessage())); 0389 outputErrorMessagesCheckTimer->start(); 0390 } 0391 0392 VncClientThread::~VncClientThread() 0393 { 0394 if (isRunning()) { 0395 stop(); 0396 terminate(); 0397 const bool quitSuccess = wait(1000); 0398 qCDebug(KRDC) << "Attempting to stop in deconstructor, will crash if this fails:" << quitSuccess; 0399 } 0400 0401 clientDestroy(); 0402 0403 delete[] frameBuffer; 0404 } 0405 0406 void VncClientThread::checkOutputErrorMessage() 0407 { 0408 if (!outputErrorMessageString.isEmpty()) { 0409 qCDebug(KRDC) << outputErrorMessageString; 0410 QString errorMessage = outputErrorMessageString; 0411 outputErrorMessageString.clear(); 0412 // show authentication failure error only after the 3rd unsuccessful try 0413 if ((errorMessage != i18n("VNC authentication failed.")) || m_passwordError) 0414 outputErrorMessage(errorMessage); 0415 } 0416 } 0417 0418 void VncClientThread::setHost(const QString &host) 0419 { 0420 QMutexLocker locker(&mutex); 0421 m_host = host; 0422 } 0423 0424 void VncClientThread::setPort(int port) 0425 { 0426 QMutexLocker locker(&mutex); 0427 m_port = port; 0428 } 0429 0430 void VncClientThread::setShowLocalCursor(bool show) 0431 { 0432 QMutexLocker locker(&mutex); 0433 m_showLocalCursor = show; 0434 0435 if (!cl) { 0436 // no client yet, only store local value 0437 return; 0438 } 0439 0440 // from server point of view, "remote" cursor is the one local to the client 0441 // so the meaning in AppData struct is inverted 0442 cl->appData.useRemoteCursor = show; 0443 0444 // need to communicate this change to server or it won't stop painting cursor 0445 m_eventQueue.enqueue(new ReconfigureEvent); 0446 } 0447 0448 void VncClientThread::setQuality(RemoteView::Quality quality) 0449 { 0450 m_quality = quality; 0451 // set color depth dependent on quality 0452 switch (quality) { 0453 case RemoteView::Low: 0454 setColorDepth(bpp8); 0455 break; 0456 case RemoteView::High: 0457 setColorDepth(bpp32); 0458 break; 0459 case RemoteView::Medium: 0460 default: 0461 setColorDepth(bpp16); 0462 } 0463 } 0464 0465 void VncClientThread::setDevicePixelRatio(qreal dpr) 0466 { 0467 m_devicePixelRatio = dpr; 0468 } 0469 0470 void VncClientThread::setColorDepth(ColorDepth colorDepth) 0471 { 0472 m_colorDepth = colorDepth; 0473 } 0474 0475 RemoteView::Quality VncClientThread::quality() const 0476 { 0477 return m_quality; 0478 } 0479 0480 VncClientThread::ColorDepth VncClientThread::colorDepth() const 0481 { 0482 return m_colorDepth; 0483 } 0484 0485 void VncClientThread::setImage(const QImage &img) 0486 { 0487 QMutexLocker locker(&mutex); 0488 m_image = img; 0489 } 0490 0491 const QImage VncClientThread::image(int x, int y, int w, int h) 0492 { 0493 QMutexLocker locker(&mutex); 0494 0495 if (w == 0) // full image requested 0496 return m_image; 0497 else 0498 return m_image.copy(x, y, w, h); 0499 } 0500 0501 void VncClientThread::emitUpdated(int x, int y, int w, int h) 0502 { 0503 Q_EMIT imageUpdated(x, y, w, h); 0504 } 0505 0506 void VncClientThread::emitGotCut(const QString &text) 0507 { 0508 Q_EMIT gotCut(text); 0509 } 0510 0511 void VncClientThread::stop() 0512 { 0513 QMutexLocker locker(&mutex); 0514 m_stopped = true; 0515 } 0516 0517 void VncClientThread::run() 0518 { 0519 QMutexLocker locker(&mutex); 0520 0521 while (!m_stopped) { // try to connect as long as the server allows 0522 locker.relock(); 0523 m_passwordError = false; 0524 locker.unlock(); 0525 0526 if (clientCreate(false)) { 0527 // The initial connection attempt worked! 0528 break; 0529 } 0530 0531 locker.relock(); 0532 if (m_passwordError) { 0533 locker.unlock(); 0534 // Try again. 0535 continue; 0536 } 0537 0538 // The initial connection attempt failed, and not because of a 0539 // password problem. Bail out. 0540 m_stopped = true; 0541 locker.unlock(); 0542 } 0543 0544 locker.relock(); 0545 qCDebug(KRDC) << "--------------------- Starting main VNC event loop ---------------------"; 0546 while (!m_stopped) { 0547 locker.unlock(); 0548 const int i = WaitForMessage(cl, 500); 0549 if (m_stopped || i < 0) { 0550 break; 0551 } 0552 if (i) { 0553 if (!HandleRFBServerMessage(cl)) { 0554 if (m_keepalive.failed) { 0555 do { 0556 // Reconnect after a short delay. That way, if the 0557 // attempt fails very quickly, we don't sit in a very 0558 // tight loop. 0559 clientDestroy(); 0560 msleep(1000); 0561 clientStateChange(RemoteView::Connecting, i18n("Reconnecting.")); 0562 } while (!clientCreate(true)); 0563 continue; 0564 } 0565 qCritical(KRDC) << "HandleRFBServerMessage failed"; 0566 break; 0567 } 0568 } 0569 0570 locker.relock(); 0571 while (!m_eventQueue.isEmpty()) { 0572 ClientEvent *clientEvent = m_eventQueue.dequeue(); 0573 locker.unlock(); 0574 clientEvent->fire(cl); 0575 delete clientEvent; 0576 locker.relock(); 0577 } 0578 } 0579 0580 m_stopped = true; 0581 } 0582 0583 /** 0584 * Factor out the initialisation of the VNC client library. Notice this has 0585 * both static parts as in @see rfbClientLog and @see rfbClientErr, 0586 * as well as instance local data @see rfbGetClient(). 0587 * 0588 * On return from here, if cl is set, the connection will have been made else 0589 * cl will not be set. 0590 */ 0591 bool VncClientThread::clientCreate(bool reinitialising) 0592 { 0593 rfbClientLog = outputHandlerStatic; 0594 rfbClientErr = outputHandlerStatic; 0595 0596 // 24bit color dept in 32 bits per pixel = default. Will change colordepth and bpp later if needed 0597 cl = rfbGetClient(8, 3, 4); 0598 setClientColorDepth(cl, this->colorDepth()); 0599 cl->MallocFrameBuffer = newclientStatic; 0600 cl->canHandleNewFBSize = true; 0601 cl->GetPassword = passwdHandlerStatic; 0602 cl->GetCredential = credentialHandlerStatic; 0603 cl->GotFrameBufferUpdate = updatefbStaticPartial; 0604 cl->FinishedFrameBufferUpdate = updateFbStaticFinished; 0605 cl->GotXCutText = cuttextStatic; 0606 cl->GotCursorShape = cursorShapeHandlerStatic; 0607 rfbClientSetClientData(cl, nullptr, this); 0608 0609 cl->appData.useRemoteCursor = m_showLocalCursor; 0610 0611 cl->serverHost = strdup(m_host.toUtf8().constData()); 0612 0613 cl->serverPort = m_port; 0614 0615 qCDebug(KRDC) << "--------------------- trying init ---------------------"; 0616 0617 if (!rfbInitClient(cl, nullptr, nullptr)) { 0618 if (!reinitialising) { 0619 // Don't whine on reconnection failure: presumably the network 0620 // is simply still down. 0621 qCCritical(KRDC) << "rfbInitClient failed"; 0622 } 0623 cl = nullptr; 0624 return false; 0625 } 0626 0627 if (reinitialising) { 0628 clientStateChange(RemoteView::Connected, i18n("Reconnected.")); 0629 } else { 0630 clientStateChange(RemoteView::Connected, i18n("Connected.")); 0631 } 0632 clientSetKeepalive(); 0633 return true; 0634 } 0635 0636 /** 0637 * Undo @see clientCreate(). 0638 */ 0639 void VncClientThread::clientDestroy() 0640 { 0641 if (cl) { 0642 // Disconnect from vnc server & cleanup allocated resources 0643 rfbClientCleanup(cl); 0644 cl = nullptr; 0645 } 0646 } 0647 0648 /** 0649 * The VNC client library does not make use of keepalives. We go behind its 0650 * back to set it up. 0651 */ 0652 void VncClientThread::clientSetKeepalive() 0653 { 0654 // If keepalive is disabled, do nothing. 0655 m_keepalive.set = false; 0656 m_keepalive.failed = false; 0657 if (!m_keepalive.intervalSeconds) { 0658 return; 0659 } 0660 int optval; 0661 socklen_t optlen = sizeof(optval); 0662 0663 // Try to set the option active 0664 optval = 1; 0665 if (setsockopt(cl->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) { 0666 qCritical(KRDC) << "setsockopt(SO_KEEPALIVE)" << strerror(errno); 0667 return; 0668 } 0669 0670 optval = m_keepalive.intervalSeconds; 0671 if (setsockopt(cl->sock, IPPROTO_TCP, TCP_KEEPIDLE, &optval, optlen) < 0) { 0672 qCritical(KRDC) << "setsockopt(TCP_KEEPIDLE)" << strerror(errno); 0673 return; 0674 } 0675 0676 optval = m_keepalive.intervalSeconds; 0677 if (setsockopt(cl->sock, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen) < 0) { 0678 qCritical(KRDC) << "setsockopt(TCP_KEEPINTVL)" << strerror(errno); 0679 return; 0680 } 0681 0682 optval = m_keepalive.failedProbes; 0683 if (setsockopt(cl->sock, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen) < 0) { 0684 qCritical(KRDC) << "setsockopt(TCP_KEEPCNT)" << strerror(errno); 0685 return; 0686 } 0687 m_keepalive.set = true; 0688 qCDebug(KRDC) << "TCP keepalive set"; 0689 } 0690 0691 /** 0692 * The VNC client state changed. 0693 */ 0694 void VncClientThread::clientStateChange(RemoteView::RemoteStatus status, const QString &details) 0695 { 0696 qCDebug(KRDC) << status << details << m_host << ":" << m_port; 0697 Q_EMIT clientStateChanged(status, details); 0698 } 0699 0700 ClientEvent::~ClientEvent() 0701 { 0702 } 0703 0704 void ReconfigureEvent::fire(rfbClient *cl) 0705 { 0706 SetFormatAndEncodings(cl); 0707 } 0708 0709 void PointerClientEvent::fire(rfbClient *cl) 0710 { 0711 SendPointerEvent(cl, m_x, m_y, m_buttonMask); 0712 } 0713 0714 void KeyClientEvent::fire(rfbClient *cl) 0715 { 0716 SendKeyEvent(cl, m_key, m_pressed); 0717 } 0718 0719 void ClientCutEvent::fire(rfbClient *cl) 0720 { 0721 QByteArray toLatin1Converted = text.toLatin1(); 0722 SendClientCutText(cl, toLatin1Converted.data(), toLatin1Converted.length()); 0723 } 0724 0725 void VncClientThread::mouseEvent(int x, int y, int buttonMask) 0726 { 0727 QMutexLocker lock(&mutex); 0728 if (m_stopped) 0729 return; 0730 0731 m_eventQueue.enqueue(new PointerClientEvent(x, y, buttonMask)); 0732 } 0733 0734 void VncClientThread::keyEvent(int key, bool pressed) 0735 { 0736 QMutexLocker lock(&mutex); 0737 if (m_stopped) 0738 return; 0739 0740 m_eventQueue.enqueue(new KeyClientEvent(key, pressed)); 0741 } 0742 0743 void VncClientThread::clientCut(const QString &text) 0744 { 0745 QMutexLocker lock(&mutex); 0746 if (m_stopped) 0747 return; 0748 0749 m_eventQueue.enqueue(new ClientCutEvent(text)); 0750 } 0751 0752 #include "moc_vncclientthread.cpp"