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"