add
[presencevnc] / src / vncclientthread.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2007-2008 Urs Wolfer <uwolfer @ kde.org>
4 **
5 ** This file is part of KDE.
6 **
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.
11 **
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.
16 **
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.
21 **
22 ****************************************************************************/
23
24 #include "vncclientthread.h"
25
26 #include <QMutexLocker>
27 #include <QTimer>
28
29 static QString outputErrorMessageString;
30
31 #ifdef Q_WS_MAEMO_5
32 //N900 display has 16bit depth (R/G/B with 5/6/5 bpp)
33 const int MAX_COLOR_DEPTH = 16;
34 #else
35 const int MAX_COLOR_DEPTH = 32;
36 #endif
37
38
39 rfbBool VncClientThread::newclient(rfbClient *cl)
40 {
41     VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
42     Q_ASSERT(t);
43
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;
51         break;
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;
58         break;
59     case RemoteView::Low:
60     case RemoteView::Unknown:
61     default:
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;
66     }
67
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;
76     } else {
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;
84     }
85
86     if (t->frameBuffer)
87         delete [] t->frameBuffer; // do not leak if we get a new framebuffer size
88     const int size = cl->width * cl->height * (cl->format.bitsPerPixel / 8);
89     t->frameBuffer = new uint8_t[size];
90     cl->frameBuffer = t->frameBuffer;
91     memset(cl->frameBuffer, '\0', size);
92
93
94     SetFormatAndEncodings(cl);
95
96     return true;
97 }
98
99 void VncClientThread::updatefb(rfbClient* cl, int x, int y, int w, int h)
100 {
101     //kDebug(5011) << "updated client: x: " << x << ", y: " << y << ", w: " << w << ", h: " << h;
102
103     const QImage img(
104             cl->frameBuffer,
105             cl->width,
106             cl->height,
107             (cl->format.bitsPerPixel==16)?QImage::Format_RGB16:QImage::Format_RGB32
108     );
109
110     if (img.isNull()) {
111         kDebug(5011) << "image not loaded";
112     }
113
114     VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
115     Q_ASSERT(t);
116
117     t->setImage(img);
118
119     t->emitUpdated(x, y, w, h);
120 }
121
122 void VncClientThread::cuttext(rfbClient* cl, const char *text, int textlen)
123 {
124     const QString cutText = QString::fromUtf8(text, textlen);
125     kDebug(5011) << "cuttext: " << cutText;
126
127     if (!cutText.isEmpty()) {
128         VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
129         Q_ASSERT(t);
130
131         t->emitGotCut(cutText);
132     }
133 }
134
135 char *VncClientThread::passwdHandler(rfbClient *cl)
136 {
137     kDebug(5011) << "password request" << kBacktrace();
138
139     VncClientThread *t = (VncClientThread*)rfbClientGetClientData(cl, 0);
140     Q_ASSERT(t);
141
142     t->m_passwordError = true;
143     t->passwordRequest();
144
145     return strdup(t->password().toLocal8Bit());
146 }
147
148 void VncClientThread::setPassword(const QString &password)
149 {
150     if(password.isNull()) //cancelled, don't retry
151         m_passwordError = false;
152
153     m_password = password;
154 }
155
156 void VncClientThread::outputHandler(const char *format, ...)
157 {
158     va_list args;
159     va_start(args, format);
160
161     QString message;
162     message.vsprintf(format, args);
163
164     va_end(args);
165
166     message = message.trimmed();
167
168     kDebug(5011) << message;
169
170     if ((message.contains("Couldn't convert ")) ||
171             (message.contains("Unable to connect to VNC server")))
172         outputErrorMessageString = i18n("Server not found.");
173     else if ((message.contains("VNC connection failed: Authentication failed, too many tries")) ||
174             (message.contains("VNC connection failed: Too many authentication failures")))
175         outputErrorMessageString = i18n("VNC authentication failed because of too many authentication tries.");
176     else if (message.contains("VNC connection failed: Authentication failed"))
177         outputErrorMessageString = i18n("VNC authentication failed.");
178     else if (message.contains("VNC server closed connection"))
179         outputErrorMessageString = i18n("VNC server closed connection.");
180     else if (message.contains("VNC server supports protocol version 3.889")) // see http://bugs.kde.org/162640
181         outputErrorMessageString = "INTERNAL:APPLE_VNC_COMPATIBILTY"; // internal messages, not displayed to user
182 }
183
184 VncClientThread::VncClientThread(QObject *parent)
185     : QThread(parent)
186     , frameBuffer(0)
187 {
188     QMutexLocker locker(&mutex);
189     m_stopped = false;
190
191     QTimer *outputErrorMessagesCheckTimer = new QTimer(this);
192     outputErrorMessagesCheckTimer->setInterval(500);
193     connect(outputErrorMessagesCheckTimer, SIGNAL(timeout()), this, SLOT(checkOutputErrorMessage()));
194     outputErrorMessagesCheckTimer->start();
195 }
196
197 VncClientThread::~VncClientThread()
198 {
199     stop();
200
201     const bool quitSuccess = wait(4000);
202
203     if(!quitSuccess)
204         kDebug(5011) << "~VncClientThread(): Quit failed";
205
206     delete [] frameBuffer;
207     //cl is free()d when event loop exits.
208 }
209
210 void VncClientThread::checkOutputErrorMessage()
211 {
212     if (!outputErrorMessageString.isEmpty()) {
213         QString errorMessage = outputErrorMessageString;
214         outputErrorMessageString.clear();
215         // show authentication failure error only after the 3rd unsuccessful try
216         if ((errorMessage != i18n("VNC authentication failed.")) || m_passwordError)
217             emit outputErrorMessage(errorMessage);
218     }
219 }
220
221 void VncClientThread::setHost(const QString &host)
222 {
223     QMutexLocker locker(&mutex);
224     m_host = host;
225 }
226
227 void VncClientThread::setPort(int port)
228 {
229     QMutexLocker locker(&mutex);
230     m_port = port;
231 }
232
233 void VncClientThread::setQuality(RemoteView::Quality quality)
234 {
235     m_quality = quality;
236 }
237
238 RemoteView::Quality VncClientThread::quality() const
239 {
240     return m_quality;
241 }
242
243 void VncClientThread::setImage(const QImage &img)
244 {
245     QMutexLocker locker(&mutex);
246     m_image = img;
247 }
248
249 const QImage VncClientThread::image(int x, int y, int w, int h)
250 {
251     QMutexLocker locker(&mutex);
252
253     if (w == 0) // full image requested
254         return m_image;
255     else
256         return m_image.copy(x, y, w, h);
257 }
258
259 void VncClientThread::emitUpdated(int x, int y, int w, int h)
260 {
261     emit imageUpdated(x, y, w, h);
262 }
263
264 void VncClientThread::emitGotCut(const QString &text)
265 {
266     emit gotCut(text);
267 }
268
269 void VncClientThread::stop()
270 {
271     if(m_stopped)
272         return;
273
274     //also abort listening for connections, should be safe without locking
275     if(listen_port)
276         cl->listenSpecified = false;
277
278     QMutexLocker locker(&mutex);
279     m_stopped = true;
280 }
281
282 void VncClientThread::run()
283 {
284     QMutexLocker locker(&mutex);
285
286     int passwd_failures = 0;
287     while (!m_stopped) { // try to connect as long as the server allows
288         m_passwordError = false;
289         outputErrorMessageString.clear(); //don't deliver error messages of old instances...
290
291         rfbClientLog = outputHandler;
292         rfbClientErr = outputHandler;
293         cl = rfbGetClient(8, 3, 4); // bitsPerSample, samplesPerPixel, bytesPerPixel
294         cl->MallocFrameBuffer = newclient;
295         cl->canHandleNewFBSize = true;
296         cl->GetPassword = passwdHandler;
297         cl->GotFrameBufferUpdate = updatefb;
298         cl->GotXCutText = cuttext;
299         rfbClientSetClientData(cl, 0, this);
300
301         cl->serverHost = strdup(m_host.toUtf8().constData());
302
303         if (m_port < 0 || !m_port) // port is invalid or empty...
304             m_port = 5900; // fallback: try an often used VNC port
305
306         if (m_port >= 0 && m_port < 100) // the user most likely used the short form (e.g. :1)
307             m_port += 5900;
308         cl->serverPort = m_port;
309
310         cl->listenSpecified = rfbBool(listen_port > 0);
311         cl->listenPort = listen_port;
312
313         kDebug(5011) << "--------------------- trying init ---------------------";
314
315         if (rfbInitClient(cl, 0, 0))
316             break;
317
318         //init failed...
319         if (m_passwordError) {
320             passwd_failures++;
321             if(passwd_failures < 3)
322                 continue; //that's ok, try again
323         }
324
325         //stop connecting
326         m_stopped = true;
327         return; //no cleanup necessary, cl was free()d by rfbInitClient()
328     }
329
330     locker.unlock();
331
332     // Main VNC event loop
333     while (!m_stopped) {
334         const int i = WaitForMessage(cl, 500);
335         if(m_stopped or i < 0)
336             break;
337
338         if (i)
339             if (!HandleRFBServerMessage(cl))
340                 break;
341
342         locker.relock();
343
344         while (!m_eventQueue.isEmpty()) {
345             ClientEvent* clientEvent = m_eventQueue.dequeue();
346             clientEvent->fire(cl);
347             delete clientEvent;
348         }
349
350         locker.unlock();
351     }
352
353     // Cleanup allocated resources
354     locker.relock();
355     rfbClientCleanup(cl);
356     m_stopped = true;
357 }
358
359 ClientEvent::~ClientEvent()
360 {
361 }
362
363 void PointerClientEvent::fire(rfbClient* cl)
364 {
365     SendPointerEvent(cl, m_x, m_y, m_buttonMask);
366 }
367
368 void KeyClientEvent::fire(rfbClient* cl)
369 {
370     SendKeyEvent(cl, m_key, m_pressed);
371 }
372
373 void ClientCutEvent::fire(rfbClient* cl)
374 {
375     SendClientCutText(cl, text.toUtf8().data(), text.size());
376 }
377
378 void VncClientThread::mouseEvent(int x, int y, int buttonMask)
379 {
380     QMutexLocker lock(&mutex);
381     if (m_stopped)
382         return;
383
384     m_eventQueue.enqueue(new PointerClientEvent(x, y, buttonMask));
385 }
386
387 void VncClientThread::keyEvent(int key, bool pressed)
388 {
389     QMutexLocker lock(&mutex);
390     if (m_stopped)
391         return;
392
393     m_eventQueue.enqueue(new KeyClientEvent(key, pressed));
394 }
395
396 void VncClientThread::clientCut(const QString &text)
397 {
398     QMutexLocker lock(&mutex);
399     if (m_stopped)
400         return;
401
402     m_eventQueue.enqueue(new ClientCutEvent(text));
403 }