1 /****************************************************************************
3 ** Copyright (C) 2007-2008 Urs Wolfer <uwolfer @ kde.org>
5 ** This file is part of KDE.
7 ** This program is free software; you can redistribute it and/or modify
8 ** it under the terms of the GNU General Public License as published by
9 ** the Free Software Foundation; either version 2 of the License, or
10 ** (at your option) any later version.
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ** GNU General Public License for more details.
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; see the file COPYING. If not, write to
19 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 ** Boston, MA 02110-1301, USA.
22 ****************************************************************************/
24 #include "vncclientthread.h"
26 #include <QMutexLocker>
29 //for detecting intel AMT KVM vnc server
30 static const QString INTEL_AMT_KVM_STRING= "Intel(r) AMT KVM";
31 static QString outputErrorMessageString;
33 QVector<QRgb> VncClientThread::m_colorTable;
35 void VncClientThread::setClientColorDepth(rfbClient* cl, VncClientThread::ColorDepth cd)
39 if (m_colorTable.isEmpty()) {
40 m_colorTable.resize(256);
42 for (int i = 0; i < 256; ++i) {
43 //pick out the red (3 bits), green (3 bits) and blue (2 bits) bits and make them maximum significant in 8bits
44 //this gives a colortable for 8bit true colors
48 m_colorTable[i] = qRgb(r, g, b);
52 cl->format.bitsPerPixel = 8;
53 cl->format.redShift = 0;
54 cl->format.greenShift = 3;
55 cl->format.blueShift = 6;
56 cl->format.redMax = 7;
57 cl->format.greenMax = 7;
58 cl->format.blueMax = 3;
61 cl->format.depth = 16;
62 cl->format.bitsPerPixel = 16;
63 cl->format.redShift = 11;
64 cl->format.greenShift = 5;
65 cl->format.blueShift = 0;
66 cl->format.redMax = 0x1f;
67 cl->format.greenMax = 0x3f;
68 cl->format.blueMax = 0x1f;
72 cl->format.depth = 24;
73 cl->format.bitsPerPixel = 32;
74 cl->format.redShift = 16;
75 cl->format.greenShift = 8;
76 cl->format.blueShift = 0;
77 cl->format.redMax = 0xff;
78 cl->format.greenMax = 0xff;
79 cl->format.blueMax = 0xff;
83 rfbBool VncClientThread::newclient(rfbClient *cl)
85 VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
88 //8bit color hack for Intel(r) AMT KVM "classic vnc" = vnc server built in in Intel Vpro chipsets.
89 if (INTEL_AMT_KVM_STRING == cl->desktopName) {
90 kDebug(5011) << "Intel(R) AMT KVM: switching to 8 bit color depth (workaround, recent libvncserver needed)";
91 t->setColorDepth(bpp8);
93 setClientColorDepth(cl, t->colorDepth());
95 const int width = cl->width, height = cl->height, depth = cl->format.bitsPerPixel;
96 const int size = width * height * (depth / 8);
98 delete [] t->frameBuffer; // do not leak if we get a new framebuffer size
99 t->frameBuffer = new uint8_t[size];
100 cl->frameBuffer = t->frameBuffer;
101 memset(cl->frameBuffer, '\0', size);
103 switch (t->quality()) {
104 case RemoteView::High:
105 cl->appData.encodingsString = "copyrect zlib hextile raw";
106 cl->appData.compressLevel = 0;
107 cl->appData.qualityLevel = 9;
109 case RemoteView::Medium:
110 cl->appData.encodingsString = "copyrect tight zrle ultra zlib hextile corre rre raw";
111 cl->appData.compressLevel = 5;
112 cl->appData.qualityLevel = 7;
114 case RemoteView::Low:
115 case RemoteView::Unknown:
117 cl->appData.encodingsString = "copyrect tight zrle ultra zlib hextile corre rre raw";
118 cl->appData.compressLevel = 9;
119 cl->appData.qualityLevel = 1;
122 SetFormatAndEncodings(cl);
123 kDebug(5011) << "Client created";
127 void VncClientThread::updatefb(rfbClient* cl, int x, int y, int w, int h)
129 // kDebug(5011) << "updated client: x: " << x << ", y: " << y << ", w: " << w << ", h: " << h;
130 VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
133 const int width = cl->width, height = cl->height;
135 switch(t->colorDepth()) {
137 img = QImage(cl->frameBuffer, width, height, QImage::Format_Indexed8);
138 img.setColorTable(m_colorTable);
141 img = QImage(cl->frameBuffer, width, height, QImage::Format_RGB16);
144 img = QImage(cl->frameBuffer, width, height, QImage::Format_RGB32);
149 kDebug(5011) << "image not loaded";
154 t->emitUpdated(x, y, w, h);
157 void VncClientThread::cuttext(rfbClient* cl, const char *text, int textlen)
159 const QString cutText = QString::fromUtf8(text, textlen);
160 kDebug(5011) << cutText;
162 if (!cutText.isEmpty()) {
163 VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
166 t->emitGotCut(cutText);
170 char *VncClientThread::passwdHandler(rfbClient *cl)
172 kDebug(5011) << "password request" << kBacktrace();
174 VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
177 t->passwordRequest();
178 t->m_passwordError = true;
180 return strdup(t->password().toLocal8Bit());
183 void VncClientThread::outputHandler(const char *format, ...)
186 va_start(args, format);
189 message.vsprintf(format, args);
193 message = message.trimmed();
195 kDebug(5011) << message;
197 if ((message.contains("Couldn't convert ")) ||
198 (message.contains("Unable to connect to VNC server")))
199 outputErrorMessageString = i18n("Server not found.");
201 if ((message.contains("VNC connection failed: Authentication failed, too many tries")) ||
202 (message.contains("VNC connection failed: Too many authentication failures")))
203 outputErrorMessageString = i18n("VNC authentication failed because of too many authentication tries.");
205 if (message.contains("VNC connection failed: Authentication failed"))
206 outputErrorMessageString = i18n("VNC authentication failed.");
208 if (message.contains("VNC server closed connection"))
209 outputErrorMessageString = i18n("VNC server closed connection.");
211 // internal messages, not displayed to user
212 if (message.contains("VNC server supports protocol version 3.889")) // see http://bugs.kde.org/162640
213 outputErrorMessageString = "INTERNAL:APPLE_VNC_COMPATIBILTY";
216 VncClientThread::VncClientThread(QObject *parent)
222 QMutexLocker locker(&mutex);
224 QTimer *outputErrorMessagesCheckTimer = new QTimer(this);
225 outputErrorMessagesCheckTimer->setInterval(500);
226 connect(outputErrorMessagesCheckTimer, SIGNAL(timeout()), this, SLOT(checkOutputErrorMessage()));
227 outputErrorMessagesCheckTimer->start();
230 VncClientThread::~VncClientThread()
235 const bool quitSuccess = wait(1000);
236 kDebug(5011) << "Attempting to stop in deconstructor, will crash if this fails:" << quitSuccess;
240 // Disconnect from vnc server & cleanup allocated resources
241 rfbClientCleanup(cl);
244 delete [] frameBuffer;
247 void VncClientThread::checkOutputErrorMessage()
249 if (!outputErrorMessageString.isEmpty()) {
250 kDebug(5011) << outputErrorMessageString;
251 QString errorMessage = outputErrorMessageString;
252 outputErrorMessageString.clear();
253 // show authentication failure error only after the 3rd unsuccessful try
254 if ((errorMessage != i18n("VNC authentication failed.")) || m_passwordError)
255 outputErrorMessage(errorMessage);
259 void VncClientThread::setHost(const QString &host)
261 QMutexLocker locker(&mutex);
265 void VncClientThread::setPort(int port)
267 QMutexLocker locker(&mutex);
271 void VncClientThread::setQuality(RemoteView::Quality quality)
274 //set color depth dependent on quality
276 case RemoteView::Low:
279 case RemoteView::High:
280 setColorDepth(bpp32);
282 case RemoteView::Medium:
284 setColorDepth(bpp16);
288 void VncClientThread::setColorDepth(ColorDepth colorDepth)
290 m_colorDepth= colorDepth;
293 RemoteView::Quality VncClientThread::quality() const
298 VncClientThread::ColorDepth VncClientThread::colorDepth() const
303 void VncClientThread::setImage(const QImage &img)
305 QMutexLocker locker(&mutex);
309 const QImage VncClientThread::image(int x, int y, int w, int h)
311 QMutexLocker locker(&mutex);
313 if (w == 0) // full image requested
316 return m_image.copy(x, y, w, h);
319 void VncClientThread::emitUpdated(int x, int y, int w, int h)
321 emit imageUpdated(x, y, w, h);
324 void VncClientThread::emitGotCut(const QString &text)
329 void VncClientThread::stop()
331 QMutexLocker locker(&mutex);
335 void VncClientThread::run()
337 QMutexLocker locker(&mutex);
339 while (!m_stopped) { // try to connect as long as the server allows
341 m_passwordError = false;
344 rfbClientLog = outputHandler;
345 rfbClientErr = outputHandler;
346 //24bit color dept in 32 bits per pixel = default. Will change colordepth and bpp later if needed
347 cl = rfbGetClient(8, 3, 4);
348 setClientColorDepth(cl, this->colorDepth());
349 cl->MallocFrameBuffer = newclient;
350 cl->canHandleNewFBSize = true;
351 cl->GetPassword = passwdHandler;
352 cl->GotFrameBufferUpdate = updatefb;
353 cl->GotXCutText = cuttext;
354 rfbClientSetClientData(cl, 0, this);
357 cl->serverHost = strdup(m_host.toUtf8().constData());
359 if (m_port < 0 || !m_port) // port is invalid or empty...
360 m_port = 5900; // fallback: try an often used VNC port
362 if (m_port >= 0 && m_port < 100) // the user most likely used the short form (e.g. :1)
364 cl->serverPort = m_port;
367 kDebug(5011) << "--------------------- trying init ---------------------";
369 if (rfbInitClient(cl, 0, 0))
382 kDebug(5011) << "--------------------- Starting main VNC event loop ---------------------";
385 const int i = WaitForMessage(cl, 500);
390 if (!HandleRFBServerMessage(cl)) {
396 while (!m_eventQueue.isEmpty()) {
397 ClientEvent* clientEvent = m_eventQueue.dequeue();
399 clientEvent->fire(cl);
408 ClientEvent::~ClientEvent()
412 void PointerClientEvent::fire(rfbClient* cl)
414 SendPointerEvent(cl, m_x, m_y, m_buttonMask);
417 void KeyClientEvent::fire(rfbClient* cl)
419 SendKeyEvent(cl, m_key, m_pressed);
422 void ClientCutEvent::fire(rfbClient* cl)
424 SendClientCutText(cl, text.toUtf8().data(), text.size());
427 void VncClientThread::mouseEvent(int x, int y, int buttonMask)
429 QMutexLocker lock(&mutex);
433 m_eventQueue.enqueue(new PointerClientEvent(x, y, buttonMask));
436 void VncClientThread::keyEvent(int key, bool pressed)
438 QMutexLocker lock(&mutex);
442 m_eventQueue.enqueue(new KeyClientEvent(key, pressed));
445 void VncClientThread::clientCut(const QString &text)
447 QMutexLocker lock(&mutex);
451 m_eventQueue.enqueue(new ClientCutEvent(text));
454 #include "moc_vncclientthread.cpp"