Make coding style more consistent
[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 = static_cast<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     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);
91
92
93     SetFormatAndEncodings(cl);
94
95     return true;
96 }
97
98 void VncClientThread::updatefb(rfbClient* cl, int x, int y, int w, int h)
99 {
100     //kDebug(5011) << "updated client: x: " << x << ", y: " << y << ", w: " << w << ", h: " << h;
101
102     const QImage img(
103             cl->frameBuffer,
104             cl->width,
105             cl->height,
106             (cl->format.bitsPerPixel==16)?QImage::Format_RGB16:QImage::Format_RGB32
107     );
108
109     if (img.isNull()) {
110         kDebug(5011) << "image not loaded";
111     }
112
113     VncClientThread *t = static_cast<VncClientThread*>(rfbClientGetClientData(cl, 0));
114     Q_ASSERT(t);
115
116     t->setImage(img);
117
118     t->emitUpdated(x, y, w, h);
119 }
120
121 void VncClientThread::cuttext(rfbClient* cl, const char *text, int textlen)
122 {
123     const QString cutText = QString::fromUtf8(text, textlen);
124     kDebug(5011) << "cuttext: " << cutText;
125
126     if (!cutText.isEmpty()) {
127         VncClientThread *t = static_cast<VncClientThread*>(rfbClientGetClientData(cl, 0));
128         Q_ASSERT(t);
129
130         t->emitGotCut(cutText);
131     }
132 }
133
134 char *VncClientThread::passwdHandler(rfbClient *cl)
135 {
136     kDebug(5011) << "password request" << kBacktrace();
137
138     VncClientThread *t = static_cast<VncClientThread*>(rfbClientGetClientData(cl, 0));
139     Q_ASSERT(t);
140
141     t->m_passwordError = true;
142     t->passwordRequest();
143
144     return strdup(t->password().toLocal8Bit());
145 }
146
147 void VncClientThread::setPassword(const QString &password)
148 {
149     if(password.isNull()) //cancelled, don't retry
150         m_passwordError = false;
151
152     m_password = password;
153 }
154
155 void VncClientThread::outputHandler(const char *format, ...)
156 {
157     va_list args;
158     va_start(args, format);
159
160     QString message;
161     message.vsprintf(format, args);
162
163     va_end(args);
164
165     message = message.trimmed();
166
167     kDebug(5011) << message;
168
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
181 }
182
183 VncClientThread::VncClientThread(QObject *parent)
184     : QThread(parent)
185     , m_frameBuffer(0)
186 {
187     QMutexLocker locker(&m_mutex);
188     m_stopped = false;
189
190     QTimer *outputErrorMessagesCheckTimer = new QTimer(this);
191     outputErrorMessagesCheckTimer->setInterval(500);
192     connect(outputErrorMessagesCheckTimer, SIGNAL(timeout()), this, SLOT(checkOutputErrorMessage()));
193     outputErrorMessagesCheckTimer->start();
194 }
195
196 VncClientThread::~VncClientThread()
197 {
198     stop();
199
200     const bool quitSuccess = wait(4000);
201
202     if(!quitSuccess)
203         kDebug(5011) << "~VncClientThread(): Quit failed";
204
205     delete [] m_frameBuffer;
206     //m_cl is free()d when event loop exits.
207 }
208
209 void VncClientThread::checkOutputErrorMessage()
210 {
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);
217     }
218 }
219
220 void VncClientThread::setHost(const QString &host)
221 {
222     QMutexLocker locker(&m_mutex);
223     m_host = host;
224 }
225
226 void VncClientThread::setPort(int port)
227 {
228     QMutexLocker locker(&m_mutex);
229     m_port = port;
230 }
231
232 void VncClientThread::setQuality(RemoteView::Quality quality)
233 {
234     m_quality = quality;
235 }
236
237 RemoteView::Quality VncClientThread::quality() const
238 {
239     return m_quality;
240 }
241
242 void VncClientThread::setImage(const QImage &img)
243 {
244     QMutexLocker locker(&m_mutex);
245     m_image = img;
246 }
247
248 const QImage VncClientThread::image(int x, int y, int w, int h)
249 {
250     QMutexLocker locker(&m_mutex);
251
252     if (w == 0) // full image requested
253         return m_image;
254     else
255         return m_image.copy(x, y, w, h);
256 }
257
258 void VncClientThread::emitUpdated(int x, int y, int w, int h)
259 {
260     emit imageUpdated(x, y, w, h);
261 }
262
263 void VncClientThread::emitGotCut(const QString &text)
264 {
265     emit gotCut(text);
266 }
267
268 void VncClientThread::stop()
269 {
270     if(m_stopped)
271         return;
272
273     //also abort listening for connections, should be safe without locking
274     if(m_listen_port)
275         m_cl->listenSpecified = false;
276
277     QMutexLocker locker(&m_mutex);
278     m_stopped = true;
279 }
280
281 void VncClientThread::run()
282 {
283     QMutexLocker locker(&m_mutex);
284
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...
289
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);
299
300         m_cl->serverHost = strdup(m_host.toUtf8().constData());
301
302         if (m_port < 0 || !m_port) // port is invalid or empty...
303             m_port = 5900; // fallback: try an often used VNC port
304
305         if (m_port >= 0 && m_port < 100) // the user most likely used the short form (e.g. :1)
306             m_port += 5900;
307         m_cl->serverPort = m_port;
308
309         m_cl->listenSpecified = rfbBool(m_listen_port > 0);
310         m_cl->listenPort = m_listen_port;
311
312         kDebug(5011) << "--------------------- trying init ---------------------";
313
314         if (rfbInitClient(m_cl, 0, 0))
315             break;
316
317         //init failed...
318         if (m_passwordError) {
319             passwd_failures++;
320             if(passwd_failures < 3)
321                 continue; //that's ok, try again
322         }
323
324         //stop connecting
325         m_stopped = true;
326         return; //no cleanup necessary, m_cl was free()d by rfbInitClient()
327     }
328
329     locker.unlock();
330
331     // Main VNC event loop
332     while (!m_stopped) {
333         const int i = WaitForMessage(m_cl, 500);
334         if(m_stopped or i < 0)
335             break;
336
337         if (i)
338             if (!HandleRFBServerMessage(m_cl))
339                 break;
340
341         locker.relock();
342
343         while (!m_eventQueue.isEmpty()) {
344             ClientEvent* clientEvent = m_eventQueue.dequeue();
345             clientEvent->fire(m_cl);
346             delete clientEvent;
347         }
348
349         locker.unlock();
350     }
351
352     // Cleanup allocated resources
353     locker.relock();
354     rfbClientCleanup(m_cl);
355     m_stopped = true;
356 }
357
358 ClientEvent::~ClientEvent()
359 {
360 }
361
362 void PointerClientEvent::fire(rfbClient* cl)
363 {
364     SendPointerEvent(cl, m_x, m_y, m_buttonMask);
365 }
366
367 void KeyClientEvent::fire(rfbClient* cl)
368 {
369     SendKeyEvent(cl, m_key, m_pressed);
370 }
371
372 void ClientCutEvent::fire(rfbClient* cl)
373 {
374     SendClientCutText(cl, text.toUtf8().data(), text.size());
375 }
376
377 void VncClientThread::mouseEvent(int x, int y, int buttonMask)
378 {
379     QMutexLocker lock(&m_mutex);
380     if (m_stopped)
381         return;
382
383     m_eventQueue.enqueue(new PointerClientEvent(x, y, buttonMask));
384 }
385
386 void VncClientThread::keyEvent(int key, bool pressed)
387 {
388     QMutexLocker lock(&m_mutex);
389     if (m_stopped)
390         return;
391
392     m_eventQueue.enqueue(new KeyClientEvent(key, pressed));
393 }
394
395 void VncClientThread::clientCut(const QString &text)
396 {
397     QMutexLocker lock(&m_mutex);
398     if (m_stopped)
399         return;
400
401     m_eventQueue.enqueue(new ClientCutEvent(text));
402 }