File indexing completed on 2024-04-28 08:51:01

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 <cerrno>
0011 #include <netinet/in.h>
0012 #include <netinet/tcp.h>
0013 #include <sys/types.h>
0014 #include <sys/socket.h>
0015 #include <QMutexLocker>
0016 #include <QTimer>
0017 #include <QBitmap>
0018 #include <QPixmap>
0019 #include <QCursor>
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     VncClientThread *t = (VncClientThread *) rfbClientGetClientData(cl, nullptr);
0092     Q_ASSERT(t);
0093 
0094     // get cursor shape from remote cursor field
0095     // it's important to set stride for images, pictures in VNC are not always 32-bit aligned
0096     QImage cursorImg;
0097     switch (bpp) {
0098     case 4:
0099         cursorImg = QImage(cl->rcSource, width, height, bpp * width, QImage::Format_RGB32);
0100         break;
0101     case 2:
0102         cursorImg = QImage(cl->rcSource, width, height, bpp * width, QImage::Format_RGB16);
0103         break;
0104     case 1:
0105         cursorImg = QImage(cl->rcSource, width, height, bpp * width, QImage::Format_Indexed8);
0106         cursorImg.setColorTable(t->m_colorTable);
0107         break;
0108     default:
0109         qCWarning(KRDC) << "Unsupported bpp value for cursor shape:" << bpp;
0110         return;
0111     }
0112 
0113     // get alpha channel
0114     QImage alpha(cl->rcMask, width, height, 1 * width, QImage::Format_Indexed8);
0115     alpha.setColorTable({qRgb(255, 255, 255), qRgb(0, 0, 0)});
0116 
0117     // apply transparency mask
0118     QPixmap cursorPixmap(QPixmap::fromImage(cursorImg));
0119     cursorPixmap.setMask(QBitmap::fromImage(alpha));
0120 
0121     Q_EMIT t->gotCursor(QCursor{cursorPixmap, xhot, yhot});
0122 }
0123 
0124 void VncClientThread::setClientColorDepth(rfbClient* cl, VncClientThread::ColorDepth cd)
0125 {
0126     switch(cd) {
0127     case bpp8:
0128         if (m_colorTable.isEmpty()) {
0129             m_colorTable.resize(256);
0130             int r, g, b;
0131             for (int i = 0; i < 256; ++i) {
0132                 //pick out the red (3 bits), green (3 bits) and blue (2 bits) bits and make them maximum significant in 8bits
0133                 //this gives a colortable for 8bit true colors
0134                 r= (i & 0x07) << 5;
0135                 g= (i & 0x38) << 2;
0136                 b= i & 0xc0;
0137                 m_colorTable[i] = qRgb(r, g, b);
0138             }
0139         }
0140         cl->format.depth = 8;
0141         cl->format.bitsPerPixel = 8;
0142         cl->format.redShift = 0;
0143         cl->format.greenShift = 3;
0144         cl->format.blueShift = 6;
0145         cl->format.redMax = 7;
0146         cl->format.greenMax = 7;
0147         cl->format.blueMax = 3;
0148         break;
0149     case bpp16:
0150         cl->format.depth = 16;
0151         cl->format.bitsPerPixel = 16;
0152         cl->format.redShift = 11;
0153         cl->format.greenShift = 5;
0154         cl->format.blueShift = 0;
0155         cl->format.redMax = 0x1f;
0156         cl->format.greenMax = 0x3f;
0157         cl->format.blueMax = 0x1f;
0158         break;
0159     case bpp32:
0160     default:
0161         cl->format.depth = 24;
0162         cl->format.bitsPerPixel = 32;
0163         cl->format.redShift = 16;
0164         cl->format.greenShift = 8;
0165         cl->format.blueShift = 0;
0166         cl->format.redMax = 0xff;
0167         cl->format.greenMax = 0xff;
0168         cl->format.blueMax = 0xff;
0169     }
0170 }
0171 
0172 rfbBool VncClientThread::newclient()
0173 {
0174     //8bit color hack for Intel(r) AMT KVM "classic vnc" = vnc server built in in Intel Vpro chipsets.
0175     if (INTEL_AMT_KVM_STRING == QLatin1String(cl->desktopName)) {
0176         qCDebug(KRDC) << "Intel(R) AMT KVM: switching to 8 bit color depth (workaround, recent libvncserver needed)";
0177         setColorDepth(bpp8);
0178     }
0179     setClientColorDepth(cl, colorDepth());
0180 
0181     const int width = cl->width, height = cl->height, depth = cl->format.bitsPerPixel;
0182     const int size = width * height * (depth / 8);
0183     if (size <= 0) {
0184         return false;
0185     }
0186     if (frameBuffer)
0187         delete [] frameBuffer; // do not leak if we get a new framebuffer size
0188     frameBuffer = new uint8_t[size];
0189     cl->frameBuffer = frameBuffer;
0190     memset(cl->frameBuffer, '\0', size);
0191 
0192     switch (quality()) {
0193     case RemoteView::High:
0194         cl->appData.encodingsString = "copyrect zlib hextile raw";
0195         cl->appData.compressLevel = 0;
0196         cl->appData.qualityLevel = 9;
0197         break;
0198     case RemoteView::Medium:
0199         cl->appData.encodingsString = "copyrect tight zrle ultra zlib hextile corre rre raw";
0200         cl->appData.compressLevel = 5;
0201         cl->appData.qualityLevel = 7;
0202         break;
0203     case RemoteView::Low:
0204     case RemoteView::Unknown:
0205     default:
0206         // bpp8 and tight encoding is not supported in libvnc
0207         cl->appData.encodingsString = "copyrect zrle ultra zlib hextile corre rre raw";
0208         cl->appData.compressLevel = 9;
0209         cl->appData.qualityLevel = 1;
0210     }
0211 
0212     SetFormatAndEncodings(cl);
0213     qCDebug(KRDC) << "Client created";
0214     return true;
0215 }
0216 
0217 void VncClientThread::updatefbPartial(int x, int y, int w, int h)
0218 {
0219 //    qCDebug(KRDC) << "updated client: x: " << x << ", y: " << y << ", w: " << w << ", h: " << h;
0220 
0221     m_dirtyRect = m_dirtyRect.united(QRect(x, y, w, h));
0222 }
0223 
0224 void VncClientThread::updatefbFinished()
0225 {
0226     const int width = cl->width, height = cl->height;
0227     QImage img;
0228     switch(colorDepth()) {
0229     case bpp8:
0230         img = QImage(cl->frameBuffer, width, height, width, QImage::Format_Indexed8);
0231         img.setColorTable(m_colorTable);
0232         break;
0233     case bpp16:
0234         img = QImage(cl->frameBuffer, width, height, 2 * width, QImage::Format_RGB16);
0235         break;
0236     case bpp32:
0237         img = QImage(cl->frameBuffer, width, height, 4 * width, QImage::Format_RGB32);
0238         break;
0239     }
0240 
0241     if (img.isNull()) {
0242         qCDebug(KRDC) << "image not loaded";
0243     }
0244 
0245     if (m_stopped) {
0246         return; // sending data to a stopped thread is not a good idea
0247     }
0248 
0249     img.setDevicePixelRatio(m_devicePixelRatio);
0250     setImage(img);
0251 
0252     // Compute bounding rect.
0253     QRect updateRect = m_dirtyRect;
0254     m_dirtyRect = QRect();
0255 
0256 //    qCDebug(KRDC) << Q_FUNC_INFO << updateRect;
0257     emitUpdated(updateRect.x(), updateRect.y(), updateRect.width(), updateRect.height());
0258 }
0259 
0260 void VncClientThread::cuttext(const char *text, int textlen)
0261 {
0262     const QString cutText = QString::fromLatin1(text, textlen);
0263     qCDebug(KRDC) << cutText;
0264 
0265     if (!cutText.isEmpty()) {
0266         emitGotCut(cutText);
0267     }
0268 }
0269 
0270 char *VncClientThread::passwdHandler()
0271 {
0272     qCDebug(KRDC) << "password request";
0273 
0274     // Never request a password during a reconnect attempt.
0275     if (!m_keepalive.failed) {
0276         passwordRequest();
0277         m_passwordError = true;
0278     }
0279     return strdup(m_password.toUtf8().constData());
0280 }
0281 
0282 rfbCredential *VncClientThread::credentialHandler(int credentialType)
0283 {
0284     qCDebug(KRDC) << "credential request" << credentialType;
0285 
0286     rfbCredential *cred = nullptr;
0287 
0288     switch (credentialType) {
0289         case rfbCredentialTypeUser:
0290             passwordRequest(true);
0291             m_passwordError = true;
0292 
0293             cred = new rfbCredential;
0294             cred->userCredential.username = strdup(username().toUtf8().constData());
0295             cred->userCredential.password = strdup(password().toUtf8().constData());
0296             break;
0297         default:
0298             qCritical(KRDC) << "credential request failed, unsupported credentialType:" << credentialType;
0299             outputErrorMessage(i18n("VNC authentication type is not supported."));
0300             break;
0301     }
0302     return cred;
0303 }
0304 
0305 void VncClientThread::outputHandler(const char *format, va_list args)
0306 {
0307     auto message =  QString::vasprintf(format, args);
0308 
0309     message = message.trimmed();
0310 
0311     qCDebug(KRDC) << message;
0312 
0313     if ((message.contains(QLatin1String("Couldn't convert "))) ||
0314             (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     QMutexLocker locker(&mutex);
0432     m_showLocalCursor = show;
0433 
0434     if (!cl) {
0435         // no client yet, only store local value
0436         return;
0437     }
0438 
0439     // from server point of view, "remote" cursor is the one local to the client
0440     // so the meaning in AppData struct is inverted
0441     cl->appData.useRemoteCursor = show;
0442 
0443     // need to communicate this change to server or it won't stop painting cursor
0444     m_eventQueue.enqueue(new ReconfigureEvent);
0445 }
0446 
0447 void VncClientThread::setQuality(RemoteView::Quality quality)
0448 {
0449     m_quality = quality;
0450     //set color depth dependent on quality
0451     switch(quality) {
0452     case RemoteView::Low:
0453         setColorDepth(bpp8);
0454         break;
0455     case RemoteView::High:
0456         setColorDepth(bpp32);
0457         break;
0458     case RemoteView::Medium:
0459     default:
0460         setColorDepth(bpp16);
0461     }
0462 }
0463 
0464 void VncClientThread::setDevicePixelRatio(qreal dpr)
0465 {
0466     m_devicePixelRatio = dpr;
0467 }
0468 
0469 void VncClientThread::setColorDepth(ColorDepth colorDepth)
0470 {
0471     m_colorDepth = colorDepth;
0472 }
0473 
0474 RemoteView::Quality VncClientThread::quality() const
0475 {
0476     return m_quality;
0477 }
0478 
0479 VncClientThread::ColorDepth VncClientThread::colorDepth() const
0480 {
0481     return m_colorDepth;
0482 }
0483 
0484 void VncClientThread::setImage(const QImage &img)
0485 {
0486     QMutexLocker locker(&mutex);
0487     m_image = img;
0488 }
0489 
0490 const QImage VncClientThread::image(int x, int y, int w, int h)
0491 {
0492     QMutexLocker locker(&mutex);
0493 
0494     if (w == 0) // full image requested
0495         return m_image;
0496     else
0497         return m_image.copy(x, y, w, h);
0498 }
0499 
0500 void VncClientThread::emitUpdated(int x, int y, int w, int h)
0501 {
0502     Q_EMIT imageUpdated(x, y, w, h);
0503 }
0504 
0505 void VncClientThread::emitGotCut(const QString &text)
0506 {
0507     Q_EMIT gotCut(text);
0508 }
0509 
0510 void VncClientThread::stop()
0511 {
0512     QMutexLocker locker(&mutex);
0513     m_stopped = true;
0514 }
0515 
0516 void VncClientThread::run()
0517 {
0518     QMutexLocker locker(&mutex);
0519 
0520     while (!m_stopped) { // try to connect as long as the server allows
0521         locker.relock();
0522         m_passwordError = false;
0523         locker.unlock();
0524 
0525         if (clientCreate(false)) {
0526             // The initial connection attempt worked!
0527             break;
0528         }
0529 
0530         locker.relock();
0531         if (m_passwordError) {
0532             locker.unlock();
0533             // Try again.
0534             continue;
0535         }
0536 
0537         // The initial connection attempt failed, and not because of a
0538         // password problem. Bail out.
0539         m_stopped = true;
0540         locker.unlock();
0541     }
0542 
0543     locker.relock();
0544     qCDebug(KRDC) << "--------------------- Starting main VNC event loop ---------------------";
0545     while (!m_stopped) {
0546         locker.unlock();
0547         const int i = WaitForMessage(cl, 500);
0548         if (m_stopped || i < 0) {
0549             break;
0550         }
0551         if (i) {
0552             if (!HandleRFBServerMessage(cl)) {
0553                 if (m_keepalive.failed) {
0554                     do {
0555                         // Reconnect after a short delay. That way, if the
0556                         // attempt fails very quickly, we don't sit in a very
0557                         // tight loop.
0558                         clientDestroy();
0559                         msleep(1000);
0560                         clientStateChange(RemoteView::Connecting, i18n("Reconnecting."));
0561                     } while (!clientCreate(true));
0562                     continue;
0563                 }
0564                 qCritical(KRDC) << "HandleRFBServerMessage failed";
0565                 break;
0566             }
0567         }
0568 
0569         locker.relock();
0570         while (!m_eventQueue.isEmpty()) {
0571             ClientEvent* clientEvent = m_eventQueue.dequeue();
0572             locker.unlock();
0573             clientEvent->fire(cl);
0574             delete clientEvent;
0575             locker.relock();
0576         }
0577     }
0578 
0579     m_stopped = true;
0580 }
0581 
0582 /**
0583  * Factor out the initialisation of the VNC client library. Notice this has
0584  * both static parts as in @see rfbClientLog and @see rfbClientErr,
0585  * as well as instance local data @see rfbGetClient().
0586  *
0587  * On return from here, if cl is set, the connection will have been made else
0588  * cl will not be set.
0589  */
0590 bool VncClientThread::clientCreate(bool reinitialising)
0591 {
0592     rfbClientLog = outputHandlerStatic;
0593     rfbClientErr = outputHandlerStatic;
0594 
0595     //24bit color dept in 32 bits per pixel = default. Will change colordepth and bpp later if needed
0596     cl = rfbGetClient(8, 3, 4);
0597     setClientColorDepth(cl, this->colorDepth());
0598     cl->MallocFrameBuffer = newclientStatic;
0599     cl->canHandleNewFBSize = true;
0600     cl->GetPassword = passwdHandlerStatic;
0601     cl->GetCredential = credentialHandlerStatic;
0602     cl->GotFrameBufferUpdate = updatefbStaticPartial;
0603     cl->FinishedFrameBufferUpdate = updateFbStaticFinished;
0604     cl->GotXCutText = cuttextStatic;
0605     cl->GotCursorShape = cursorShapeHandlerStatic;
0606     rfbClientSetClientData(cl, nullptr, this);
0607 
0608     cl->appData.useRemoteCursor = m_showLocalCursor;
0609 
0610     cl->serverHost = strdup(m_host.toUtf8().constData());
0611 
0612     cl->serverPort = m_port;
0613 
0614     qCDebug(KRDC) << "--------------------- trying init ---------------------";
0615 
0616     if (!rfbInitClient(cl, nullptr, nullptr)) {
0617         if (!reinitialising) {
0618             // Don't whine on reconnection failure: presumably the network
0619             // is simply still down.
0620             qCCritical(KRDC) << "rfbInitClient failed";
0621         }
0622         cl = nullptr;
0623         return false;
0624     }
0625 
0626     if (reinitialising) {
0627         clientStateChange(RemoteView::Connected, i18n("Reconnected."));
0628     } else {
0629         clientStateChange(RemoteView::Connected, i18n("Connected."));
0630     }
0631     clientSetKeepalive();
0632     return true;
0633 }
0634 
0635 /**
0636  * Undo @see clientCreate().
0637  */
0638 void VncClientThread::clientDestroy()
0639 {
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"