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 <QCoreApplication>
27 #include <QMutexLocker>
30 static QString outputErrorMessageString;
32 rfbBool VncClientThread::newclient(rfbClient *cl)
34 VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
37 const int width = cl->width, height = cl->height, depth = cl->format.bitsPerPixel;
38 const int size = width * height * (depth / 8);
40 delete [] t->frameBuffer; // do not leak if we get a new framebuffer size
41 t->frameBuffer = new uint8_t[size];
42 cl->frameBuffer = t->frameBuffer;
43 memset(cl->frameBuffer, '\0', size);
44 cl->format.bitsPerPixel = 32;
45 cl->format.redShift = 16;
46 cl->format.greenShift = 8;
47 cl->format.blueShift = 0;
48 cl->format.redMax = 0xff;
49 cl->format.greenMax = 0xff;
50 cl->format.blueMax = 0xff;
52 switch (t->quality()) {
53 case RemoteView::High:
54 cl->appData.useBGR233 = 0;
55 cl->appData.encodingsString = "copyrect hextile raw";
56 cl->appData.compressLevel = 0;
57 cl->appData.qualityLevel = 9;
59 case RemoteView::Medium:
60 cl->appData.useBGR233 = 0;
61 cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
62 cl->appData.compressLevel = 5;
63 cl->appData.qualityLevel = 7;
66 case RemoteView::Unknown:
68 cl->appData.useBGR233 = 1;
69 cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
70 cl->appData.compressLevel = 9;
71 cl->appData.qualityLevel = 1;
74 SetFormatAndEncodings(cl);
79 void VncClientThread::updatefb(rfbClient* cl, int x, int y, int w, int h)
81 //kDebug(5011) << "updated client: x: " << x << ", y: " << y << ", w: " << w << ", h: " << h;
83 const int width = cl->width, height = cl->height;
85 const QImage img(cl->frameBuffer, width, height, QImage::Format_RGB32);
88 kDebug(5011) << "image not loaded";
91 VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
96 t->emitUpdated(x, y, w, h);
99 void VncClientThread::cuttext(rfbClient* cl, const char *text, int textlen)
101 const QString cutText = QString::fromUtf8(text, textlen);
102 kDebug(5011) << cutText;
104 if (!cutText.isEmpty()) {
105 VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
108 t->emitGotCut(cutText);
112 char *VncClientThread::passwdHandler(rfbClient *cl)
114 kDebug(5011) << "password request" << kBacktrace();
116 VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
119 t->passwordRequest();
120 t->m_passwordError = true;
122 return strdup(t->password().toLocal8Bit());
125 void VncClientThread::outputHandler(const char *format, ...)
128 va_start(args, format);
131 message.vsprintf(format, args);
135 message = message.trimmed();
137 kDebug(5011) << message;
139 if ((message.contains("Couldn't convert ")) ||
140 (message.contains("Unable to connect to VNC server")))
141 outputErrorMessageString = i18n("Server not found.");
143 if ((message.contains("VNC connection failed: Authentication failed, too many tries")) ||
144 (message.contains("VNC connection failed: Too many authentication failures")))
145 outputErrorMessageString = i18n("VNC authentication failed because of too many authentication tries.");
147 if (message.contains("VNC connection failed: Authentication failed"))
148 outputErrorMessageString = i18n("VNC authentication failed.");
150 if (message.contains("VNC server closed connection"))
151 outputErrorMessageString = i18n("VNC server closed connection.");
153 // internal messages, not displayed to user
154 if (message.contains("VNC server supports protocol version 3.889")) // see http://bugs.kde.org/162640
155 outputErrorMessageString = "INTERNAL:APPLE_VNC_COMPATIBILTY";
158 VncClientThread::VncClientThread(QObject *parent)
162 QMutexLocker locker(&mutex);
165 QTimer *outputErrorMessagesCheckTimer = new QTimer(this);
166 outputErrorMessagesCheckTimer->setInterval(500);
167 connect(outputErrorMessagesCheckTimer, SIGNAL(timeout()), this, SLOT(checkOutputErrorMessage()));
168 outputErrorMessagesCheckTimer->start();
171 VncClientThread::~VncClientThread()
175 const bool quitSuccess = wait(1000);
177 kDebug(5011) << "~VncClientThread(): Quit VNC thread success:" << quitSuccess;
179 delete [] frameBuffer;
180 //cl is free()d when event loop exits.
182 kDebug(5011) << "inMessageHandler: " << inMessageHandler;
184 kDebug(5011) << "leaving ~VncClientThreod()";
187 void VncClientThread::checkOutputErrorMessage()
189 if (!outputErrorMessageString.isEmpty()) {
190 kDebug(5011) << outputErrorMessageString;
191 QString errorMessage = outputErrorMessageString;
192 outputErrorMessageString.clear();
193 // show authentication failure error only after the 3rd unsuccessful try
194 if ((errorMessage != i18n("VNC authentication failed.")) || m_passwordError)
195 outputErrorMessage(errorMessage);
199 void VncClientThread::setHost(const QString &host)
201 QMutexLocker locker(&mutex);
205 void VncClientThread::setPort(int port)
207 QMutexLocker locker(&mutex);
211 void VncClientThread::setQuality(RemoteView::Quality quality)
216 RemoteView::Quality VncClientThread::quality() const
221 void VncClientThread::setImage(const QImage &img)
223 QMutexLocker locker(&mutex);
227 const QImage VncClientThread::image(int x, int y, int w, int h)
229 QMutexLocker locker(&mutex);
231 if (w == 0) // full image requested
234 return m_image.copy(x, y, w, h);
237 void VncClientThread::emitUpdated(int x, int y, int w, int h)
239 emit imageUpdated(x, y, w, h);
242 void VncClientThread::emitGotCut(const QString &text)
247 void VncClientThread::stop()
249 QMutexLocker locker(&mutex);
250 //TODO: not locking the mutex leads to a crash, but at least it stops.
251 //TODO: i don't see how this makes a difference
252 kDebug(5011) << "stop(): mutex locked";
256 void VncClientThread::run()
258 QMutexLocker locker(&mutex);
261 int passwd_failures = 0;
262 while (!m_stopped) { // try to connect as long as the server allows
263 m_passwordError = false;
265 rfbClientLog = outputHandler;
266 rfbClientErr = outputHandler;
267 cl = rfbGetClient(8, 3, 4);
268 cl->MallocFrameBuffer = newclient;
269 cl->canHandleNewFBSize = true;
270 cl->GetPassword = passwdHandler;
271 cl->GotFrameBufferUpdate = updatefb;
272 cl->GotXCutText = cuttext;
273 rfbClientSetClientData(cl, 0, this);
275 cl->serverHost = strdup(m_host.toUtf8().constData());
277 if (m_port < 0 || !m_port) // port is invalid or empty...
278 m_port = 5900; // fallback: try an often used VNC port
280 if (m_port >= 0 && m_port < 100) // the user most likely used the short form (e.g. :1)
282 cl->serverPort = m_port;
284 kDebug(5011) << "--------------------- trying init ---------------------";
286 if (rfbInitClient(cl, 0, 0))
289 if (m_passwordError) {
291 if(passwd_failures > 2) {
293 clean = true; //rfbInitClient cleans up after itself upon failure
303 inMessageHandler = false;
305 // Main VNC event loop
307 const int i = WaitForMessage(cl, 500);
311 kDebug(5011) << "foo2";
315 inMessageHandler = true;
316 if (!HandleRFBServerMessage(cl))
318 inMessageHandler = false;
322 kDebug(5011) << "foo4";
328 while (!m_eventQueue.isEmpty()) {
329 ClientEvent* clientEvent = m_eventQueue.dequeue();
330 clientEvent->fire(cl);
336 kDebug(5011) << "exited main loop";
338 // Cleanup allocated resources
340 kDebug(5011) << "mutex relocked";
342 rfbClientCleanup(cl);
344 kDebug(5011) << "exiting run()";
347 ClientEvent::~ClientEvent()
351 void PointerClientEvent::fire(rfbClient* cl)
353 SendPointerEvent(cl, m_x, m_y, m_buttonMask);
356 void KeyClientEvent::fire(rfbClient* cl)
358 SendKeyEvent(cl, m_key, m_pressed);
361 void ClientCutEvent::fire(rfbClient* cl)
363 SendClientCutText(cl, text.toUtf8().data(), text.size());
366 void VncClientThread::mouseEvent(int x, int y, int buttonMask)
368 QMutexLocker lock(&mutex);
372 m_eventQueue.enqueue(new PointerClientEvent(x, y, buttonMask));
375 void VncClientThread::keyEvent(int key, bool pressed)
377 QMutexLocker lock(&mutex);
381 m_eventQueue.enqueue(new KeyClientEvent(key, pressed));
384 void VncClientThread::clientCut(const QString &text)
386 QMutexLocker lock(&mutex);
390 m_eventQueue.enqueue(new ClientCutEvent(text));
393 #include "moc_vncclientthread.cpp"