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 static QString outputErrorMessageString;
32 //N900 display has 16bit depth (R/G/B with 5/6/5 bpp)
33 const int MAX_COLOR_DEPTH = 16;
35 const int MAX_COLOR_DEPTH = 32;
39 rfbBool VncClientThread::newclient(rfbClient *cl)
41 VncClientThread *t = static_cast<VncClientThread*>(rfbClientGetClientData(cl, 0));
44 switch (t->quality()) {
45 case RemoteView::High:
46 cl->format.bitsPerPixel = MAX_COLOR_DEPTH;
47 cl->appData.useBGR233 = 0;
48 cl->appData.encodingsString = "copyrect hextile raw";
49 cl->appData.compressLevel = 0;
50 cl->appData.qualityLevel = 9;
52 case RemoteView::Medium:
53 cl->format.bitsPerPixel = 16;
54 cl->appData.useBGR233 = 0;
55 cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
56 cl->appData.compressLevel = 5;
57 cl->appData.qualityLevel = 7;
60 case RemoteView::Unknown:
62 cl->format.bitsPerPixel = 16; //TODO: add support for 8bit (needs color map)
63 cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
64 cl->appData.compressLevel = 9;
65 cl->appData.qualityLevel = 1;
68 if(cl->format.bitsPerPixel == 16) {
69 cl->format.depth = 16; //number of useful bits in the pixel value
70 cl->format.redShift = 11;
71 cl->format.greenShift = 5;
72 cl->format.blueShift = 0;
73 cl->format.redMax = 0x1f;
74 cl->format.greenMax = 0x3f;
75 cl->format.blueMax = 0x1f;
77 cl->format.depth = 24; //number of useful bits in the pixel value
78 cl->format.redShift = 16;
79 cl->format.greenShift = 8;
80 cl->format.blueShift = 0;
81 cl->format.redMax = 0xff;
82 cl->format.greenMax = 0xff;
83 cl->format.blueMax = 0xff;
86 delete [] t->m_frameBuffer; // do not leak if we get a new framebuffer size
87 const int size = cl->width * cl->height * (cl->format.bitsPerPixel / 8);
88 t->m_frameBuffer = new uint8_t[size];
89 cl->frameBuffer = t->m_frameBuffer;
90 memset(cl->frameBuffer, '\0', size);
93 SetFormatAndEncodings(cl);
98 void VncClientThread::updatefb(rfbClient* cl, int x, int y, int w, int h)
100 //kDebug(5011) << "updated client: x: " << x << ", y: " << y << ", w: " << w << ", h: " << h;
106 (cl->format.bitsPerPixel==16)?QImage::Format_RGB16:QImage::Format_RGB32
110 kDebug(5011) << "image not loaded";
113 VncClientThread *t = static_cast<VncClientThread*>(rfbClientGetClientData(cl, 0));
118 t->emitUpdated(x, y, w, h);
121 void VncClientThread::cuttext(rfbClient* cl, const char *text, int textlen)
123 const QString cutText = QString::fromUtf8(text, textlen);
124 kDebug(5011) << "cuttext: " << cutText;
126 if (!cutText.isEmpty()) {
127 VncClientThread *t = static_cast<VncClientThread*>(rfbClientGetClientData(cl, 0));
130 t->emitGotCut(cutText);
134 char *VncClientThread::passwdHandler(rfbClient *cl)
136 kDebug(5011) << "password request" << kBacktrace();
138 VncClientThread *t = static_cast<VncClientThread*>(rfbClientGetClientData(cl, 0));
141 t->m_passwordError = true;
142 t->passwordRequest();
144 return strdup(t->password().toLocal8Bit());
147 void VncClientThread::setPassword(const QString &password)
149 if(password.isNull()) //cancelled, don't retry
150 m_passwordError = false;
152 m_password = password;
155 void VncClientThread::outputHandler(const char *format, ...)
158 va_start(args, format);
161 message.vsprintf(format, args);
165 message = message.trimmed();
167 kDebug(5011) << message;
169 if ((message.contains("Couldn't convert ")) ||
170 (message.contains("Unable to connect to VNC server")))
171 outputErrorMessageString = i18n("Server not found.");
172 else if ((message.contains("VNC connection failed: Authentication failed, too many tries")) ||
173 (message.contains("VNC connection failed: Too many authentication failures")))
174 outputErrorMessageString = i18n("VNC authentication failed because of too many authentication tries.");
175 else if (message.contains("VNC connection failed: Authentication failed"))
176 outputErrorMessageString = i18n("VNC authentication failed.");
177 else if (message.contains("VNC server closed connection"))
178 outputErrorMessageString = i18n("VNC server closed connection.");
179 else if (message.contains("VNC server supports protocol version 3.889")) // see http://bugs.kde.org/162640
180 outputErrorMessageString = "INTERNAL:APPLE_VNC_COMPATIBILTY"; // internal messages, not displayed to user
183 VncClientThread::VncClientThread(QObject *parent)
187 QMutexLocker locker(&m_mutex);
190 QTimer *outputErrorMessagesCheckTimer = new QTimer(this);
191 outputErrorMessagesCheckTimer->setInterval(500);
192 connect(outputErrorMessagesCheckTimer, SIGNAL(timeout()), this, SLOT(checkOutputErrorMessage()));
193 outputErrorMessagesCheckTimer->start();
196 VncClientThread::~VncClientThread()
200 const bool quitSuccess = wait(4000);
203 kDebug(5011) << "~VncClientThread(): Quit failed";
205 delete [] m_frameBuffer;
206 //m_cl is free()d when event loop exits.
209 void VncClientThread::checkOutputErrorMessage()
211 if (!outputErrorMessageString.isEmpty()) {
212 QString errorMessage = outputErrorMessageString;
213 outputErrorMessageString.clear();
214 // show authentication failure error only after the 3rd unsuccessful try
215 if ((errorMessage != i18n("VNC authentication failed.")) || m_passwordError)
216 emit outputErrorMessage(errorMessage);
220 void VncClientThread::setHost(const QString &host)
222 QMutexLocker locker(&m_mutex);
226 void VncClientThread::setPort(int port)
228 QMutexLocker locker(&m_mutex);
232 void VncClientThread::setQuality(RemoteView::Quality quality)
237 RemoteView::Quality VncClientThread::quality() const
242 void VncClientThread::setImage(const QImage &img)
244 QMutexLocker locker(&m_mutex);
248 const QImage VncClientThread::image(int x, int y, int w, int h)
250 QMutexLocker locker(&m_mutex);
252 if (w == 0) // full image requested
255 return m_image.copy(x, y, w, h);
258 void VncClientThread::emitUpdated(int x, int y, int w, int h)
260 emit imageUpdated(x, y, w, h);
263 void VncClientThread::emitGotCut(const QString &text)
268 void VncClientThread::stop()
273 //also abort listening for connections, should be safe without locking
275 m_cl->listenSpecified = false;
277 QMutexLocker locker(&m_mutex);
281 void VncClientThread::run()
283 QMutexLocker locker(&m_mutex);
285 int passwd_failures = 0;
286 while (!m_stopped) { // try to connect as long as the server allows
287 m_passwordError = false;
288 outputErrorMessageString.clear(); //don't deliver error messages of old instances...
290 rfbClientLog = outputHandler;
291 rfbClientErr = outputHandler;
292 m_cl = rfbGetClient(8, 3, 4); // bitsPerSample, samplesPerPixel, bytesPerPixel
293 m_cl->MallocFrameBuffer = newclient;
294 m_cl->canHandleNewFBSize = true;
295 m_cl->GetPassword = passwdHandler;
296 m_cl->GotFrameBufferUpdate = updatefb;
297 m_cl->GotXCutText = cuttext;
298 rfbClientSetClientData(m_cl, 0, this);
300 m_cl->serverHost = strdup(m_host.toUtf8().constData());
302 if (m_port < 0 || !m_port) // port is invalid or empty...
303 m_port = 5900; // fallback: try an often used VNC port
305 if (m_port >= 0 && m_port < 100) // the user most likely used the short form (e.g. :1)
307 m_cl->serverPort = m_port;
309 m_cl->listenSpecified = rfbBool(m_listen_port > 0);
310 m_cl->listenPort = m_listen_port;
312 kDebug(5011) << "--------------------- trying init ---------------------";
314 if (rfbInitClient(m_cl, 0, 0))
318 if (m_passwordError) {
320 if(passwd_failures < 3)
321 continue; //that's ok, try again
326 return; //no cleanup necessary, m_cl was free()d by rfbInitClient()
331 // Main VNC event loop
333 const int i = WaitForMessage(m_cl, 500);
334 if(m_stopped or i < 0)
338 if (!HandleRFBServerMessage(m_cl))
343 while (!m_eventQueue.isEmpty()) {
344 ClientEvent* clientEvent = m_eventQueue.dequeue();
345 clientEvent->fire(m_cl);
352 // Cleanup allocated resources
354 rfbClientCleanup(m_cl);
358 ClientEvent::~ClientEvent()
362 void PointerClientEvent::fire(rfbClient* cl)
364 SendPointerEvent(cl, m_x, m_y, m_buttonMask);
367 void KeyClientEvent::fire(rfbClient* cl)
369 SendKeyEvent(cl, m_key, m_pressed);
372 void ClientCutEvent::fire(rfbClient* cl)
374 SendClientCutText(cl, text.toUtf8().data(), text.size());
377 void VncClientThread::mouseEvent(int x, int y, int buttonMask)
379 QMutexLocker lock(&m_mutex);
383 m_eventQueue.enqueue(new PointerClientEvent(x, y, buttonMask));
386 void VncClientThread::keyEvent(int key, bool pressed)
388 QMutexLocker lock(&m_mutex);
392 m_eventQueue.enqueue(new KeyClientEvent(key, pressed));
395 void VncClientThread::clientCut(const QString &text)
397 QMutexLocker lock(&m_mutex);
401 m_eventQueue.enqueue(new ClientCutEvent(text));